implemented fx handler, rewrite handlers using fx and cfx, optimized subscriptions, reorganized structure and renamed files, implemented re-frame-test and wrote some tests
This commit is contained in:
parent
68fe57b016
commit
3d05f99bd4
|
@ -50,7 +50,8 @@
|
|||
:optimizations :none}}}}
|
||||
:repl-options {:nrepl-middleware [cemerick.piggieback/wrap-cljs-repl]
|
||||
:timeout 240000}}
|
||||
:test {:plugins [[lein-doo "0.1.7"]]
|
||||
:test {:dependencies [[day8.re-frame/test "0.1.5"]]
|
||||
:plugins [[lein-doo "0.1.7"]]
|
||||
:cljsbuild {:builds
|
||||
[{:id "test"
|
||||
:source-paths ["src" "test/cljs"]
|
||||
|
|
|
@ -15,9 +15,9 @@
|
|||
http-bridge]]
|
||||
[status-im.components.main-tabs :refer [main-tabs]]
|
||||
[status-im.components.context-menu :refer [menu-context]]
|
||||
[status-im.contacts.views.contact-list :refer [contact-list]]
|
||||
[status-im.contacts.views.contact-list-modal :refer [contact-list-modal]]
|
||||
[status-im.contacts.views.new-contact :refer [new-contact]]
|
||||
[status-im.contacts.contact-list.views :refer [contact-list]]
|
||||
[status-im.contacts.contact-list-modal.views :refer [contact-list-modal]]
|
||||
[status-im.contacts.new-contact.views :refer [new-contact]]
|
||||
[status-im.qr-scanner.screen :refer [qr-scanner]]
|
||||
[status-im.discover.search-results :refer [discover-search-results]]
|
||||
[status-im.chat.screen :refer [chat]]
|
||||
|
|
|
@ -266,8 +266,8 @@
|
|||
db' (-> db
|
||||
(assoc :current-chat-id chat-id)
|
||||
(assoc-in [:chats chat-id :was-opened?] true))
|
||||
commands-loaded? (get-in db [:contacts chat-id :commands-loaded?])
|
||||
bot-url (get-in db [:contacts chat-id :bot-url])
|
||||
commands-loaded? (get-in db [:contacts/contacts chat-id :commands-loaded?])
|
||||
bot-url (get-in db [:contacts/contacts chat-id :bot-url])
|
||||
was-opened? (get-in db [:chats chat-id :was-opened?])
|
||||
call-init-command #(when (and (not was-opened?) bot-url)
|
||||
(status/call-function!
|
||||
|
@ -311,7 +311,7 @@
|
|||
(callback))
|
||||
(dispatch [::clear-chat-loaded-callbacks chat-id])))))
|
||||
|
||||
(defn prepare-chat [{:keys [contacts]} chat-id chat]
|
||||
(defn prepare-chat [{:contacts/keys [contacts]} chat-id chat]
|
||||
(let [name (get-in contacts [chat-id :name])]
|
||||
(merge {:chat-id chat-id
|
||||
:name (or name (generate-gfy))
|
||||
|
@ -432,7 +432,8 @@
|
|||
delete-chat!))
|
||||
|
||||
(defn send-seen!
|
||||
[{:keys [web3 current-public-key chats contacts]}
|
||||
[{: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)]
|
||||
|
@ -452,7 +453,8 @@
|
|||
(u/side-effect! send-seen!))
|
||||
|
||||
(defn send-clock-value-request!
|
||||
[{:keys [web3 current-public-key contacts]} [_ {:keys [message-id from]}]]
|
||||
[{:keys [web3 current-public-key]
|
||||
:contacts/keys [contacts]} [_ {:keys [message-id from]}]]
|
||||
(when-not (get-in contacts [from :dapp?])
|
||||
(protocol/send-clock-value-request!
|
||||
{:web3 web3
|
||||
|
@ -487,8 +489,9 @@
|
|||
|
||||
(register-handler :check-and-open-dapp!
|
||||
(u/side-effect!
|
||||
(fn [{:keys [current-chat-id global-commands contacts] :as db}]
|
||||
(let [dapp-url (get-in db [:contacts current-chat-id :dapp-url])]
|
||||
(fn [{:keys [current-chat-id global-commands]
|
||||
:contacts/keys [contacts]}]
|
||||
(let [dapp-url (get-in contacts [current-chat-id :dapp-url])]
|
||||
(when dapp-url
|
||||
(am/go
|
||||
(dispatch [:select-chat-input-command
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
[status-im.utils.platform :as platform]
|
||||
[taoensso.timbre :as log]))
|
||||
|
||||
(defn generate-context [{:keys [contacts current-account-id chats] :as db} chat-id to]
|
||||
(defn generate-context [{:keys [current-account-id chats] :as db} chat-id to]
|
||||
(merge {:platform platform/platform
|
||||
:from current-account-id
|
||||
:to to
|
||||
|
@ -21,7 +21,8 @@
|
|||
|
||||
(handlers/register-handler :request-command-data
|
||||
(handlers/side-effect!
|
||||
(fn [{:keys [contacts current-account-id chats] :as db}
|
||||
(fn [{:keys [current-account-id chats]
|
||||
:contacts/keys [contacts] :as db}
|
||||
[_ {{:keys [command params content-command type]} :content
|
||||
:keys [message-id chat-id jail-id on-requested from] :as message} data-type]]
|
||||
(let [jail-id (or jail-id chat-id)
|
||||
|
|
|
@ -117,7 +117,7 @@
|
|||
all-commands (->> (into global-commands commands)
|
||||
(remove (fn [[k {:keys [hidden?]}]] hidden?))
|
||||
(into {}))
|
||||
{:keys [dapp?]} (get-in db [:contacts chat-id])]
|
||||
{:keys [dapp?]} (get-in db [:contacts/contacts chat-id])]
|
||||
(when dapp?
|
||||
(if (str/blank? chat-text)
|
||||
(dispatch [:set-in [:chats chat-id :parameter-boxes :message] nil])
|
||||
|
@ -144,7 +144,7 @@
|
|||
(input-model/split-command-args)
|
||||
(rest))
|
||||
seq-arg (get-in db [:chats current-chat-id :seq-argument-input-text])
|
||||
to (get-in db [:contacts current-chat-id :address])
|
||||
to (get-in db [:contacts/contacts current-chat-id :address])
|
||||
params {:parameters {:args args
|
||||
:bot-db bot-db
|
||||
:seq-arg seq-arg}
|
||||
|
@ -237,7 +237,8 @@
|
|||
|
||||
(handlers/register-handler ::request-command-data
|
||||
(handlers/side-effect!
|
||||
(fn [{:keys [contacts bot-db] :as db}
|
||||
(fn [{:keys [bot-db]
|
||||
:contacts/keys [contacts] :as db}
|
||||
[_ {{:keys [command
|
||||
metadata
|
||||
args]
|
||||
|
|
|
@ -97,7 +97,7 @@
|
|||
(assoc-in db [:chats chat-id :last-message] message)))
|
||||
|
||||
(defn commands-loaded? [db chat-id]
|
||||
(get-in db [:contacts chat-id :commands-loaded?]))
|
||||
(get-in db [:contacts/contacts chat-id :commands-loaded?]))
|
||||
|
||||
(def timeout 400)
|
||||
|
||||
|
|
|
@ -137,7 +137,8 @@
|
|||
|
||||
(register-handler ::invoke-command-handlers!
|
||||
(u/side-effect!
|
||||
(fn [{:keys [bot-db accounts current-account-id] :as db}
|
||||
(fn [{:keys [bot-db accounts current-account-id]
|
||||
:contacts/keys [contacts] :as db}
|
||||
[_ {{:keys [command
|
||||
params
|
||||
id]} :command
|
||||
|
@ -145,7 +146,7 @@
|
|||
:as orig-params}]]
|
||||
(let [{:keys [type name bot owner-id]} command
|
||||
handler-type (if (= :command type) :commands :responses)
|
||||
to (get-in db [:contacts chat-id :address])
|
||||
to (get-in contacts [chat-id :address])
|
||||
identity (or owner-id bot chat-id)
|
||||
bot-db (get bot-db (or bot chat-id))
|
||||
params {:parameters params
|
||||
|
@ -217,7 +218,7 @@
|
|||
|
||||
(register-handler :received-bot-response
|
||||
(u/side-effect!
|
||||
(fn [{:keys [contacts]} [_ {:keys [chat-id] :as params} {:keys [result] :as data}]]
|
||||
(fn [{:contacts/keys [contacts]} [_ {:keys [chat-id] :as params} {:keys [result] :as data}]]
|
||||
(let [{:keys [returned context]} result
|
||||
{:keys [markup text-message err]} returned
|
||||
{:keys [log-messages update-db default-db]} context
|
||||
|
@ -287,10 +288,11 @@
|
|||
(register-handler ::send-message!
|
||||
(u/side-effect!
|
||||
(fn [{:keys [web3 chats network-status current-account-id accounts]
|
||||
:contacts/keys [contacts]
|
||||
:as db} [_ {{:keys [message-type]
|
||||
:as message} :message
|
||||
chat-id :chat-id}]]
|
||||
(let [{:keys [dapp?]} (get-in db [:contacts chat-id])]
|
||||
(let [{:keys [dapp?]} (get contacts chat-id)]
|
||||
(if dapp?
|
||||
(dispatch [::send-dapp-message chat-id message])
|
||||
(when message
|
||||
|
@ -323,7 +325,8 @@
|
|||
(register-handler ::send-command-protocol!
|
||||
(u/side-effect!
|
||||
(fn [{:keys [web3 current-public-key chats network-status
|
||||
current-account-id accounts contacts] :as db}
|
||||
current-account-id accounts]
|
||||
:contacts/keys [contacts] :as db}
|
||||
[_ {:keys [chat-id command]}]]
|
||||
(log/debug "sending command: " command)
|
||||
(if (get-in contacts [chat-id :dapp?])
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
[taoensso.timbre :as log]
|
||||
[status-im.models.commands :as commands]
|
||||
[status-im.commands.utils :as cu]
|
||||
[status-im.contacts.validations :as v]
|
||||
[status-im.contacts.db :as v]
|
||||
[status-im.components.status :as s]
|
||||
[status-im.components.nfc :as nfc]
|
||||
[status-im.constants :as c]
|
||||
|
@ -24,7 +24,7 @@
|
|||
(str "\"" s "\"")))
|
||||
|
||||
(defn scan-qr-handler
|
||||
[{:keys [contacts]} [_ _ data]]
|
||||
[{:contacts/keys [contacts]} [_ _ data]]
|
||||
(let [data' (try (read-string (wrap-hex data))
|
||||
(catch :default e data))
|
||||
data'' (cond
|
||||
|
@ -60,9 +60,10 @@
|
|||
|
||||
(register-handler ::send-command
|
||||
(u/side-effect!
|
||||
(fn [{:keys [current-chat-id] :as db}
|
||||
(fn [{:keys [current-chat-id]
|
||||
:contacts/keys [contacts]}
|
||||
[_ command-key {:keys [contact amount]}]]
|
||||
(let [command (get-in db [:contacts current-chat-id :commands command-key])]
|
||||
(let [command (get-in contacts [current-chat-id :commands command-key])]
|
||||
(dispatch [:set-in [:bot-db current-chat-id :public :recipient] contact])
|
||||
(dispatch [:proceed-command
|
||||
{:command command,
|
||||
|
@ -107,9 +108,9 @@
|
|||
|
||||
(defmethod nav/preload-data! :contact-list-modal
|
||||
[db [_ _ {:keys [handler action params]}]]
|
||||
(assoc db :contacts-click-handler handler
|
||||
:contacts-click-action action
|
||||
:contacts-click-params params))
|
||||
(assoc db :contacts/click-handler handler
|
||||
:contacts/click-action action
|
||||
:contacts/click-params params))
|
||||
|
||||
(def qr-context {:toolbar-title (label :t/address)})
|
||||
|
||||
|
|
|
@ -50,7 +50,7 @@
|
|||
{:keys [contacts requests]} (get-in db [:chats chat-id])]
|
||||
(->> contacts
|
||||
(map (fn [{:keys [identity]}]
|
||||
(let [{:keys [commands responses]} (get-in db [: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))
|
||||
responses' (mapv (fn [{:keys [message-id type]}]
|
||||
[type [(get responses type) message-id]])
|
||||
|
|
|
@ -23,14 +23,14 @@
|
|||
(let [requests (get-in db [:chats current-chat-id :requests])]
|
||||
(->> requests
|
||||
(map (fn [{:keys [type] :as v}]
|
||||
(assoc v :name (get-in db [:contacts current-chat-id :responses type :name]))))
|
||||
(assoc v :name (get-in db [:contacts/contacts current-chat-id :responses type :name]))))
|
||||
(filter (fn [v] ((can-be-suggested? text) v))))))
|
||||
|
||||
(defn get-command-suggestions
|
||||
[{:keys [current-chat-id] :as db} text]
|
||||
(->> (get-in db [:chats current-chat-id :contacts])
|
||||
(map (fn [{:keys [identity]}]
|
||||
(let [commands (get-in db [:contacts identity :commands])]
|
||||
(let [commands (get-in db [:contacts/contacts identity :commands])]
|
||||
(->> commands
|
||||
(filter (fn [[_ v]] ((can-be-suggested? text) v)))))))
|
||||
(reduce (fn [m cur] (into (or m {}) cur)))
|
||||
|
|
|
@ -27,7 +27,7 @@
|
|||
|
||||
(reg-sub
|
||||
:chats
|
||||
(fn [db ]
|
||||
(fn [db]
|
||||
(:chats db)))
|
||||
|
||||
(reg-sub
|
||||
|
@ -39,7 +39,7 @@
|
|||
|
||||
(reg-sub
|
||||
:get-current-chat-id
|
||||
(fn [db ]
|
||||
(fn [db]
|
||||
(:current-chat-id db)))
|
||||
|
||||
(reg-sub
|
||||
|
@ -55,23 +55,22 @@
|
|||
(reg-sub :get-commands
|
||||
(fn [db [_ chat-id]]
|
||||
(let [current-chat (or chat-id (db :current-chat-id))]
|
||||
(or (get-in db [:contacts current-chat :commands]) {}))))
|
||||
(or (get-in db [:contacts/contacts current-chat :commands]) {}))))
|
||||
|
||||
(reg-sub
|
||||
:get-responses
|
||||
(fn [db [_ chat-id]]
|
||||
(let [current-chat (or chat-id (db :current-chat-id))]
|
||||
(or (get-in db [:contacts current-chat :responses]) {}))))
|
||||
(or (get-in db [:contacts/contacts current-chat :responses]) {}))))
|
||||
|
||||
(reg-sub :get-commands-and-responses
|
||||
(fn [db [_ chat-id]]
|
||||
(let [{:keys [chats contacts]} db]
|
||||
(->> (get-in chats [chat-id :contacts])
|
||||
(filter :is-in-chat)
|
||||
(mapv (fn [{:keys [identity]}]
|
||||
(let [{:keys [commands responses]} (get contacts identity)]
|
||||
(merge responses commands))))
|
||||
(apply merge)))))
|
||||
(fn [{:keys [chats] :contacts/keys [contacts]} [_ chat-id]]
|
||||
(->> (get-in chats [chat-id :contacts])
|
||||
(filter :is-in-chat)
|
||||
(mapv (fn [{:keys [identity]}]
|
||||
(let [{:keys [commands responses]} (get contacts identity)]
|
||||
(merge responses commands))))
|
||||
(apply merge))))
|
||||
|
||||
(reg-sub
|
||||
:selected-chat-command
|
||||
|
@ -148,7 +147,7 @@
|
|||
(reg-sub :get-response
|
||||
(fn [db [_ n]]
|
||||
(let [chat-id (subscribe [:get-current-chat-id])]
|
||||
(get-in db [:contacts @chat-id :responses n]))))
|
||||
(get-in db [:contacts/contacts @chat-id :responses n]))))
|
||||
|
||||
(reg-sub :is-request-answered?
|
||||
:<- [:chat :requests]
|
||||
|
@ -170,7 +169,7 @@
|
|||
(get-in db [:chats @chat-id :all-loaded?]))))
|
||||
|
||||
(reg-sub :photo-path
|
||||
:<- [:get :contacts]
|
||||
:<- [:get-contacts]
|
||||
(fn [contacts [_ id]]
|
||||
(:photo-path (contacts id))))
|
||||
|
||||
|
@ -178,9 +177,9 @@
|
|||
(fn [db [_ chat-id]]
|
||||
(let [{:keys [last-message messages]} (get-in db [:chats chat-id])]
|
||||
(->> (conj messages last-message)
|
||||
(sort-by :clock-value > )
|
||||
(filter :show?)
|
||||
(first)))))
|
||||
(sort-by :clock-value >)
|
||||
(filter :show?)
|
||||
(first)))))
|
||||
|
||||
(reg-sub :get-last-message-short-preview
|
||||
(fn [db [_ chat-id]]
|
||||
|
|
|
@ -69,7 +69,7 @@
|
|||
public? [:chat :public?]
|
||||
show-actions? [:chat-ui-props :show-actions?]
|
||||
accounts [:get :accounts]
|
||||
contact [:get-in [:contacts @chat-id]]
|
||||
contact [:get-in [:contacts/contacts @chat-id]]
|
||||
sync-state [:get :sync-state]
|
||||
creating? [:get :creating-account?]]
|
||||
[view (st/chat-name-view (or (empty? accounts)
|
||||
|
|
|
@ -37,7 +37,8 @@
|
|||
:else nil)))
|
||||
|
||||
(defn suggestions-handler!
|
||||
[{:keys [contacts chats] :as db} [{:keys [chat-id default-db command parameter-index result]}]]
|
||||
[{:keys [chats] :as db}
|
||||
[{:keys [chat-id default-db command parameter-index result]}]]
|
||||
(let [{:keys [markup height] :as returned} (get-in result [:result :returned])
|
||||
contains-markup? (contains? returned :markup)
|
||||
path (if command
|
||||
|
@ -53,7 +54,7 @@
|
|||
(defn suggestions-events-handler!
|
||||
[{:keys [current-chat-id bot-db] :as db} [[n & data :as ev] val]]
|
||||
(log/debug "Suggestion event: " n (first data) val)
|
||||
(let [{:keys [dapp?]} (get-in db [:contacts current-chat-id])]
|
||||
(let [{:keys [dapp?]} (get-in db [:contacts/contacts current-chat-id])]
|
||||
(case (keyword n)
|
||||
:set-command-argument
|
||||
(let [[index value move-to-next?] (first data)]
|
||||
|
|
|
@ -22,7 +22,8 @@
|
|||
|
||||
|
||||
(defn load-commands!
|
||||
[{:keys [current-chat-id contacts chats]} [jail-id callback]]
|
||||
[{:keys [current-chat-id chats]
|
||||
:contacts/keys [contacts]} [jail-id callback]]
|
||||
(let [identity (or jail-id current-chat-id)
|
||||
contact-ids (if (get contacts identity)
|
||||
[identity]
|
||||
|
@ -67,7 +68,7 @@
|
|||
|
||||
(defn get-hash-by-identity
|
||||
[db identity]
|
||||
(get-in db [:contacts identity :dapp-hash]))
|
||||
(get-in db [:contacts/contacts identity :dapp-hash]))
|
||||
|
||||
(defn get-hash-by-file
|
||||
[file]
|
||||
|
@ -121,7 +122,7 @@
|
|||
(into {})))
|
||||
|
||||
(defn get-mailmans-commands [db]
|
||||
(->> (get-in db [:contacts bots-constants/mailman-bot :commands])
|
||||
(->> (get-in db [:contacts/contacts bots-constants/mailman-bot :commands])
|
||||
(map
|
||||
(fn [[k v :as com]]
|
||||
[k (-> v
|
||||
|
@ -147,7 +148,7 @@
|
|||
(cond-> db
|
||||
|
||||
true
|
||||
(update-in [:contacts id] assoc
|
||||
(update-in [:contacts/contacts id] assoc
|
||||
:commands-loaded? true
|
||||
:commands (merge mailman-commands commands'')
|
||||
:responses (each-merge responses' {:type :response
|
||||
|
@ -160,20 +161,20 @@
|
|||
:type :command))
|
||||
|
||||
(= id bots-constants/mailman-bot)
|
||||
(update :contacts (fn [contacts]
|
||||
(reduce (fn [contacts [k _]]
|
||||
(update-in contacts [k :commands]
|
||||
(fn [c]
|
||||
(merge mailman-commands c))))
|
||||
contacts
|
||||
contacts))))))
|
||||
(update :contacts/contacts (fn [contacts]
|
||||
(reduce (fn [contacts [k _]]
|
||||
(update-in contacts [k :commands]
|
||||
(fn [c]
|
||||
(merge mailman-commands c))))
|
||||
contacts
|
||||
contacts))))))
|
||||
|
||||
(defn save-commands-js!
|
||||
[_ [id file]]
|
||||
#_(commands/save {:chat-id id :file file}))
|
||||
|
||||
(defn save-commands!
|
||||
[{:keys [global-commands contacts]} [id]]
|
||||
[{:keys [global-commands] :contacts/keys [contacts]} [id]]
|
||||
(let [command (get global-commands (keyword id))
|
||||
commands (get-in contacts [id :commands])
|
||||
responses (get-in contacts [id :responses])]
|
||||
|
@ -195,7 +196,7 @@
|
|||
|
||||
(reg-handler :check-and-load-commands!
|
||||
(u/side-effect!
|
||||
(fn [{:keys [contacts]} [identity callback]]
|
||||
(fn [{:contacts/keys [contacts]} [identity callback]]
|
||||
(if (get-in contacts [identity :commands-loaded?])
|
||||
(callback)
|
||||
(dispatch [:load-commands! identity callback])))))
|
||||
|
@ -216,7 +217,7 @@
|
|||
(after (fn [_ [id]]
|
||||
(dispatch [:invoke-commands-loading-callbacks id])
|
||||
(dispatch [:invoke-chat-loaded-callbacks id])))
|
||||
(after (fn [{:keys [contacts]} [id]]
|
||||
(after (fn [{:contacts/keys [contacts]} [id]]
|
||||
(let [subscriptions (get-in contacts [id :subscriptions])]
|
||||
(doseq [[name opts] subscriptions]
|
||||
(dispatch [:register-bot-subscription
|
||||
|
|
|
@ -34,7 +34,7 @@
|
|||
|
||||
(defview pending-contact-badge
|
||||
[chat-id {:keys [pending-wrapper pending-outer-circle pending-inner-circle]}]
|
||||
[pending-contact? [:get-in [:contacts chat-id :pending?]]]
|
||||
[pending-contact? [:get-in [:contacts/contacts chat-id :pending?]]]
|
||||
(when pending-contact?
|
||||
[view pending-wrapper
|
||||
[view pending-outer-circle
|
||||
|
@ -42,7 +42,7 @@
|
|||
|
||||
(defview chat-icon-view [chat-id group-chat name online styles & [hide-dapp?]]
|
||||
[photo-path [:chat-photo chat-id]
|
||||
dapp? [:get-in [:contacts chat-id :dapp?]]]
|
||||
dapp? [:get-in [:contacts/contacts chat-id :dapp?]]]
|
||||
[view (:container styles)
|
||||
(if-not (s/blank? photo-path)
|
||||
[chat-icon photo-path styles]
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
[status-im.components.tabs.bottom-shadow :refer [bottom-shadow-view]]
|
||||
[status-im.chats-list.screen :refer [chats-list]]
|
||||
[status-im.discover.screen :refer [discover]]
|
||||
[status-im.contacts.screen :refer [contact-list]]
|
||||
[status-im.contacts.views :refer [contact-list]]
|
||||
[status-im.components.tabs.tabs :refer [tabs]]
|
||||
[status-im.components.tabs.styles :as st]
|
||||
[status-im.components.styles :as common-st]
|
||||
|
|
|
@ -0,0 +1,69 @@
|
|||
(ns status-im.contacts.contact-list.views
|
||||
(:require-macros [status-im.utils.views :refer [defview letsubs]])
|
||||
(:require [re-frame.core :refer [dispatch]]
|
||||
[status-im.components.renderers.renderers :as renderers]
|
||||
[status-im.components.contact.contact :refer [contact-view]]
|
||||
[status-im.contacts.views :refer [contact-options]]
|
||||
[status-im.components.react :refer [view list-view list-item]]
|
||||
[status-im.components.status-bar :refer [status-bar]]
|
||||
[status-im.components.toolbar-new.view :refer [toolbar-with-search toolbar]]
|
||||
[status-im.components.toolbar-new.actions :as act]
|
||||
[status-im.components.drawer.view :refer [drawer-view]]
|
||||
[status-im.contacts.styles :as st]
|
||||
[status-im.utils.listview :as lw]
|
||||
[status-im.i18n :refer [label]]))
|
||||
|
||||
(defn render-row [group edit?]
|
||||
(fn [row _ _]
|
||||
(list-item
|
||||
^{:key row}
|
||||
[contact-view {:contact row
|
||||
:on-press #(dispatch [:open-chat-with-contact %])
|
||||
:extended? edit?
|
||||
:extend-options (contact-options row group)}])))
|
||||
|
||||
(defview contact-list-toolbar-edit [group]
|
||||
[toolbar {:nav-action (act/back #(dispatch [:set-in [:contacts/list-ui-props :edit?] false]))
|
||||
:actions [{:image :blank}]
|
||||
:title (if-not group
|
||||
(label :t/contacts)
|
||||
(or (:name group) (label :t/contacts-group-new-chat)))}])
|
||||
|
||||
(defview contact-list-toolbar [group]
|
||||
(letsubs [show-search [:get-in [:toolbar-search :show]]
|
||||
search-text [:get-in [:toolbar-search :text]]]
|
||||
(toolbar-with-search
|
||||
{:show-search? (= show-search :contact-list)
|
||||
:search-text search-text
|
||||
:search-key :contact-list
|
||||
:title (if-not group
|
||||
(label :t/contacts)
|
||||
(or (:name group) (label :t/contacts-group-new-chat)))
|
||||
:search-placeholder (label :t/search-contacts)
|
||||
:actions [(act/opts [{:text (label :t/edit)
|
||||
:value #(dispatch [:set-in [:contacts/list-ui-props :edit?] true])}])]})))
|
||||
|
||||
(defview contacts-list-view [group edit?]
|
||||
(letsubs [contacts [:all-added-group-contacts-filtered (:group-id group)]]
|
||||
[list-view {:dataSource (lw/to-datasource contacts)
|
||||
:enableEmptySections true
|
||||
:renderRow (render-row group edit?)
|
||||
:keyboardShouldPersistTaps :always
|
||||
:renderHeader renderers/list-header-renderer
|
||||
:renderFooter renderers/list-footer-renderer
|
||||
:renderSeparator renderers/list-separator-renderer
|
||||
:style st/contacts-list}]))
|
||||
|
||||
(defview contact-list []
|
||||
(letsubs [edit? [:get-in [:contacts/list-ui-props :edit?]]
|
||||
group [:get :contacts-group]
|
||||
type [:get :group-type]]
|
||||
[drawer-view
|
||||
[view {:flex 1}
|
||||
[view
|
||||
[status-bar]
|
||||
(if edit?
|
||||
[contact-list-toolbar-edit group]
|
||||
[contact-list-toolbar group])]
|
||||
[contacts-list-view group edit?]]]))
|
||||
|
|
@ -0,0 +1,78 @@
|
|||
(ns status-im.contacts.contact-list-modal.views
|
||||
(:require-macros [status-im.utils.views :refer [defview letsubs]])
|
||||
(:require [re-frame.core :refer [dispatch]]
|
||||
[status-im.components.common.common :as common]
|
||||
[status-im.components.renderers.renderers :as renderers]
|
||||
[status-im.components.react :refer [view list-view list-item]]
|
||||
[status-im.components.contact.contact :refer [contact-view]]
|
||||
[status-im.components.action-button.action-button :refer [action-button
|
||||
action-separator]]
|
||||
[status-im.components.action-button.styles :refer [actions-list]]
|
||||
[status-im.components.status-bar :refer [status-bar]]
|
||||
[status-im.components.toolbar-new.view :refer [toolbar-with-search]]
|
||||
[status-im.components.drawer.view :refer [drawer-view]]
|
||||
[status-im.contacts.styles :as st]
|
||||
[status-im.utils.listview :as lw]
|
||||
[status-im.i18n :refer [label]]))
|
||||
|
||||
(defview contact-list-modal-toolbar []
|
||||
(letsubs [show-search [:get-in [:toolbar-search :show]]
|
||||
search-text [:get-in [:toolbar-search :text]]]
|
||||
(toolbar-with-search
|
||||
{:show-search? (= show-search :contact-list)
|
||||
:search-text search-text
|
||||
:search-key :contact-list
|
||||
:title (label :t/contacts)
|
||||
:search-placeholder (label :t/search-contacts)})))
|
||||
|
||||
(defn actions-view [action click-handler]
|
||||
[view actions-list
|
||||
[action-button (label :t/enter-address)
|
||||
:address_blue
|
||||
#(do
|
||||
(dispatch [:send-to-webview-bridge
|
||||
{:event (name :webview-send-transaction)}])
|
||||
(dispatch [:navigate-back]))]
|
||||
[action-separator]
|
||||
(if (= :request action)
|
||||
[action-button (label :t/show-qr)
|
||||
:q_r_blue
|
||||
#(click-handler :qr-scan action)]
|
||||
[action-button (label :t/scan-qr)
|
||||
:fullscreen_blue
|
||||
#(click-handler :qr-scan action)])])
|
||||
|
||||
(defn render-row [click-handler action params]
|
||||
(fn [row _ _]
|
||||
(list-item
|
||||
^{:key row}
|
||||
[contact-view {:contact row
|
||||
:on-press #(when click-handler
|
||||
(click-handler row action params))}])))
|
||||
|
||||
(defview contact-list-modal []
|
||||
(letsubs [contacts [:contacts-filtered :all-added-people-contacts]
|
||||
click-handler [:get :contacts/click-handler]
|
||||
action [:get :contacts/click-action]
|
||||
params [:get :contacts/click-params]]
|
||||
[drawer-view
|
||||
[view {:flex 1}
|
||||
[status-bar {:type :modal}]
|
||||
[contact-list-modal-toolbar]
|
||||
[list-view {:dataSource (lw/to-datasource contacts)
|
||||
:enableEmptySections true
|
||||
:renderRow (render-row click-handler action params)
|
||||
:bounces false
|
||||
:keyboardShouldPersistTaps :always
|
||||
:renderHeader #(list-item
|
||||
[view
|
||||
[actions-view action click-handler]
|
||||
[common/bottom-shadow]
|
||||
[common/form-title (label :t/choose-from-contacts)
|
||||
{:count-value (count contacts)}]
|
||||
[common/list-header]])
|
||||
:renderFooter #(list-item [view
|
||||
[common/list-footer]
|
||||
[common/bottom-shadow]])
|
||||
:renderSeparator renderers/list-separator-renderer
|
||||
:style st/contacts-list-modal}]]]))
|
|
@ -0,0 +1,96 @@
|
|||
(ns status-im.contacts.db
|
||||
(:require-macros [status-im.utils.db :refer [allowed-keys]])
|
||||
(:require [cljs.spec.alpha :as s]
|
||||
[clojure.string :as str]
|
||||
[status-im.data-store.contacts :as contacts]
|
||||
[status-im.js-dependencies :as dependencies]))
|
||||
|
||||
(defn contact-can-be-added? [identity]
|
||||
(if (contacts/exists? identity)
|
||||
(:pending? (contacts/get-by-id identity))
|
||||
true))
|
||||
|
||||
(defn is-address? [s]
|
||||
(.isAddress dependencies/Web3.prototype s))
|
||||
|
||||
(defn hex-string? [s]
|
||||
(let [s' (if (str/starts-with? s "0x")
|
||||
(subs s 2)
|
||||
s)]
|
||||
(boolean (re-matches #"(?i)[0-9a-f]+" s'))))
|
||||
|
||||
(defn valid-length? [identity]
|
||||
(let [length (count identity)]
|
||||
(and
|
||||
(hex-string? identity)
|
||||
(or
|
||||
(and (= 128 length) (not (str/includes? identity "0x")))
|
||||
(and (= 130 length) (str/starts-with? identity "0x"))
|
||||
(and (= 132 length) (str/starts-with? identity "0x04"))
|
||||
(is-address? identity)))))
|
||||
|
||||
(s/def ::not-empty-string (s/and string? not-empty))
|
||||
(s/def ::public-key (s/and ::not-empty-string valid-length?))
|
||||
|
||||
;;;; DB
|
||||
|
||||
;;Contact
|
||||
|
||||
;we can't validate public key, because for dapps whisper-identity is just string
|
||||
(s/def :contact/whisper-identity ::not-empty-string)
|
||||
(s/def :contact/name ::not-empty-string)
|
||||
(s/def :contact/address (s/nilable is-address?))
|
||||
(s/def :contact/private-key (s/nilable string?))
|
||||
(s/def :contact/public-key (s/nilable string?))
|
||||
(s/def :contact/photo-path (s/nilable string?))
|
||||
(s/def :contact/status (s/nilable string?))
|
||||
|
||||
(s/def :contact/last-updated (s/nilable int?))
|
||||
(s/def :contact/last-online (s/nilable int?))
|
||||
(s/def :contact/pending? boolean?)
|
||||
(s/def :contact/unremovable? boolean?)
|
||||
|
||||
(s/def :contact/dapp? boolean?)
|
||||
(s/def :contact/dapp-url (s/nilable string?))
|
||||
(s/def :contact/dapp-hash (s/nilable int?))
|
||||
(s/def :contact/bot-url (s/nilable string?))
|
||||
(s/def :contact/global-command (s/nilable map?))
|
||||
(s/def :contact/commands (s/nilable (s/map-of keyword? map?)))
|
||||
(s/def :contact/responses (s/nilable (s/map-of keyword? map?)))
|
||||
(s/def :contact/commands-loaded? (s/nilable boolean?))
|
||||
(s/def :contact/subscriptions (s/nilable map?))
|
||||
;true when contact added using status-dev-cli
|
||||
(s/def :contact/debug? boolean?)
|
||||
|
||||
(s/def :contact/contact (allowed-keys
|
||||
:req-un [:contact/name :contact/whisper-identity]
|
||||
:opt-un [:contact/address :contact/private-key :contact/public-key :contact/photo-path
|
||||
:contact/status :contact/last-updated :contact/last-online :contact/pending?
|
||||
:contact/unremovable? :contact/dapp? :contact/dapp-url :contact/dapp-hash
|
||||
:contact/bot-url :contact/global-command :contact/commands-loaded?
|
||||
:contact/commands :contact/responses :contact/debug? :contact/subscriptions]))
|
||||
|
||||
;;Contact list ui props
|
||||
(s/def :contact-list-ui/edit? boolean?)
|
||||
|
||||
;;Contacts ui props
|
||||
(s/def :contacts-ui/edit? boolean?)
|
||||
|
||||
|
||||
(s/def :contacts/contacts (s/nilable (s/map-of ::not-empty-string :contact/contact)))
|
||||
;public key of new contact during adding this new contact
|
||||
(s/def :contacts/new-identity (s/nilable string?))
|
||||
(s/def :contacts/new-public-key-error (s/nilable string?))
|
||||
;on showing this contact's profile (andrey: better to move into profile ns)
|
||||
(s/def :contacts/identity (s/nilable ::not-empty-string))
|
||||
(s/def :contacts/list-ui-props (s/nilable (allowed-keys :opt-un [:contact-list-ui/edit?])))
|
||||
(s/def :contacts/ui-props (s/nilable (allowed-keys :opt-un [:contacts-ui/edit?])))
|
||||
;used in modal list (for example for wallet)
|
||||
(s/def :contacts/click-handler (s/nilable fn?))
|
||||
;used in modal list (for example for wallet)
|
||||
(s/def :contacts/click-action (s/nilable #{:send :request}))
|
||||
;used in modal list (for example for wallet)
|
||||
(s/def :contacts/click-params (s/nilable map?))
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,456 @@
|
|||
(ns status-im.contacts.events
|
||||
(:require [re-frame.core :refer [dispatch reg-fx reg-cofx inject-cofx]]
|
||||
[status-im.utils.handlers :refer [register-handler-db register-handler-fx]]
|
||||
[status-im.data-store.contacts :as contacts]
|
||||
[status-im.utils.crypt :refer [encrypt]]
|
||||
[clojure.string :as s]
|
||||
[status-im.protocol.core :as protocol]
|
||||
[status-im.utils.utils :refer [http-post]]
|
||||
[status-im.utils.phone-number :refer [format-phone-number]]
|
||||
[status-im.utils.random :as random]
|
||||
[taoensso.timbre :as log]
|
||||
[cljs.reader :refer [read-string]]
|
||||
[status-im.utils.js-resources :as js-res]
|
||||
[status-im.react-native.js-dependencies :as rn-dependencies]
|
||||
[status-im.contacts.navigation]
|
||||
[status-im.utils.identicon :refer [identicon]]
|
||||
[status-im.utils.gfycat.core :refer [generate-gfy]]
|
||||
[status-im.i18n :refer [label]]
|
||||
[status-im.contacts.db :as v]))
|
||||
|
||||
;;;; COFX
|
||||
|
||||
(reg-cofx
|
||||
::get-all-contacts
|
||||
(fn [coeffects _]
|
||||
(assoc coeffects :all-contacts (contacts/get-all))))
|
||||
|
||||
(reg-cofx
|
||||
::get-default-contacts-and-groups
|
||||
(fn [coeffects _]
|
||||
(assoc coeffects :default-contacts js-res/default-contacts
|
||||
:default-groups js-res/default-contact-groups)))
|
||||
|
||||
;;;; FX
|
||||
|
||||
(reg-fx
|
||||
::watch-contact
|
||||
(fn [{:keys [web3 whisper-identity public-key private-key]}]
|
||||
(protocol/watch-user! {:web3 web3
|
||||
:identity whisper-identity
|
||||
:keypair {:public public-key
|
||||
:private private-key}
|
||||
:callback #(dispatch [:incoming-message %1 %2])})))
|
||||
|
||||
(reg-fx
|
||||
::stop-watching-contact
|
||||
(fn [{:keys [web3 whisper-identity]}]
|
||||
(protocol/stop-watching-user! {:web3 web3
|
||||
:identity whisper-identity})))
|
||||
|
||||
(reg-fx
|
||||
::send-contact-request-fx
|
||||
(fn [{:keys [web3 current-public-key name whisper-identity photo-path current-account-id status
|
||||
updates-public-key updates-private-key] :as params}]
|
||||
(protocol/contact-request!
|
||||
{:web3 web3
|
||||
:message {:from current-public-key
|
||||
:to whisper-identity
|
||||
:message-id (random/id)
|
||||
:payload {:contact {:name name
|
||||
:profile-image photo-path
|
||||
:address current-account-id
|
||||
:status status}
|
||||
:keypair {:public updates-public-key
|
||||
:private updates-private-key}}}})))
|
||||
|
||||
(reg-fx
|
||||
::reset-pending-messages
|
||||
(fn [from]
|
||||
(protocol/reset-pending-messages! from)))
|
||||
|
||||
(reg-fx
|
||||
::save-contact
|
||||
(fn [contact]
|
||||
(contacts/save contact)))
|
||||
|
||||
(reg-fx
|
||||
::save-contacts!
|
||||
(fn [new-contacts]
|
||||
(contacts/save-all new-contacts)))
|
||||
|
||||
(reg-fx
|
||||
::delete-contact
|
||||
(fn [contact]
|
||||
(contacts/delete contact)))
|
||||
|
||||
(defn- contact-name [contact]
|
||||
(->> contact
|
||||
((juxt :givenName :middleName :familyName))
|
||||
(remove s/blank?)
|
||||
(s/join " ")))
|
||||
|
||||
(defn- normalize-phone-contacts [contacts]
|
||||
(let [contacts' (js->clj contacts :keywordize-keys true)]
|
||||
(map (fn [{:keys [thumbnailPath phoneNumbers] :as contact}]
|
||||
{:name (contact-name contact)
|
||||
:photo-path thumbnailPath
|
||||
:phone-numbers phoneNumbers}) contacts')))
|
||||
|
||||
(reg-fx
|
||||
::fetch-contacts-from-phone!
|
||||
(fn [_]
|
||||
(.getAll rn-dependencies/contacts
|
||||
(fn [error contacts]
|
||||
(if error
|
||||
(log/debug :error-on-fetching-loading error)
|
||||
(let [contacts' (normalize-phone-contacts contacts)]
|
||||
(dispatch [::get-contacts-identities contacts'])))))))
|
||||
|
||||
(defn- get-contacts-by-hash [contacts]
|
||||
(->> contacts
|
||||
(mapcat (fn [{:keys [phone-numbers] :as contact}]
|
||||
(map (fn [{:keys [number]}]
|
||||
(let [number' (format-phone-number number)]
|
||||
[(encrypt number')
|
||||
(-> contact
|
||||
(assoc :phone-number number')
|
||||
(dissoc :phone-numbers))]))
|
||||
phone-numbers)))
|
||||
(into {})))
|
||||
|
||||
(defn- add-identity [contacts-by-hash contacts]
|
||||
(map (fn [{:keys [phone-number-hash whisper-identity address]}]
|
||||
(let [contact (contacts-by-hash phone-number-hash)]
|
||||
(assoc contact :whisper-identity whisper-identity
|
||||
:address address)))
|
||||
(js->clj contacts)))
|
||||
|
||||
(reg-fx
|
||||
::request-stored-contacts
|
||||
(fn [contacts]
|
||||
(let [contacts-by-hash (get-contacts-by-hash contacts)
|
||||
data (or (keys contacts-by-hash) '())]
|
||||
(http-post "get-contacts" {:phone-number-hashes data}
|
||||
(fn [{:keys [contacts]}]
|
||||
(let [contacts' (add-identity contacts-by-hash contacts)]
|
||||
(dispatch [:add-contacts contacts'])))))))
|
||||
|
||||
(reg-fx
|
||||
::request-contacts-by-address
|
||||
(fn [id]
|
||||
(http-post "get-contacts-by-address" {:addresses [id]}
|
||||
(fn [{:keys [contacts]}]
|
||||
(if (> (count contacts) 0)
|
||||
(let [{:keys [whisper-identity]} (first contacts)
|
||||
contact {:name (generate-gfy)
|
||||
:address id
|
||||
:photo-path (identicon whisper-identity)
|
||||
:whisper-identity whisper-identity}]
|
||||
(if (contacts/exists? whisper-identity)
|
||||
(dispatch [:add-pending-contact whisper-identity])
|
||||
(dispatch [:add-new-contact-and-open-chat contact])))
|
||||
(dispatch [:set :contacts/new-public-key-error (label :t/unknown-address)]))))))
|
||||
|
||||
;;;; Handlers
|
||||
|
||||
(register-handler-fx
|
||||
::get-contacts-identities
|
||||
(fn [_ [_ contacts]]
|
||||
{::request-stored-contacts contacts}))
|
||||
|
||||
(register-handler-fx
|
||||
:watch-contact
|
||||
(fn [{:keys [db]} [_ {:keys [public-key private-key] :as contact}]]
|
||||
(when (and public-key private-key)
|
||||
{::watch-contact (merge
|
||||
(select-keys db [:web3])
|
||||
(select-keys contact [:whisper-identity :public-key :private-key]))})))
|
||||
|
||||
(register-handler-fx
|
||||
:update-contact!
|
||||
(fn [{:keys [db]} [_ {:keys [whisper-identity] :as contact}]]
|
||||
(when (get-in db [:contacts/contacts whisper-identity])
|
||||
{:db (update-in db [:contacts/contacts whisper-identity] merge contact)
|
||||
::save-contact contact})))
|
||||
|
||||
(register-handler-fx
|
||||
:load-contacts
|
||||
[(inject-cofx ::get-all-contacts)]
|
||||
(fn [{:keys [db all-contacts]} _]
|
||||
(let [contacts-list (map #(vector (:whisper-identity %) %) all-contacts)
|
||||
global-commands (->> contacts-list
|
||||
(filter (fn [[_ c]] (:global-command c)))
|
||||
(map (fn [[id {:keys [global-command]}]]
|
||||
[(keyword id) (-> global-command
|
||||
(update :params (comp vec vals))
|
||||
(assoc :bot id
|
||||
:type :command))]))
|
||||
(into {}))
|
||||
contacts (into {} contacts-list)]
|
||||
{:db (assoc db :contacts/contacts contacts
|
||||
:global-commands global-commands)
|
||||
:dispatch-n (mapv (fn [_ contact] [:watch-contact contact]) contacts)})))
|
||||
|
||||
(register-handler-fx
|
||||
:sync-contacts
|
||||
(fn [_ _]
|
||||
{::fetch-contacts-from-phone! nil}))
|
||||
|
||||
(defn- public-key->address [public-key]
|
||||
(let [length (count public-key)
|
||||
normalized-key (case length
|
||||
132 (subs public-key 4)
|
||||
130 (subs public-key 2)
|
||||
128 public-key
|
||||
nil)]
|
||||
(when normalized-key
|
||||
(subs (.sha3 js/Web3.prototype normalized-key #js {:encoding "hex"}) 26))))
|
||||
|
||||
(defn- prepare-default-groups-events [groups default-groups]
|
||||
[[:add-groups
|
||||
(for [[id {:keys [name contacts]}] default-groups
|
||||
:let [id' (clojure.core/name id)]
|
||||
:when (not (get groups id'))]
|
||||
{:group-id id'
|
||||
:name (:en name)
|
||||
:order 0
|
||||
:timestamp (random/timestamp)
|
||||
:contacts (mapv #(hash-map :identity %) contacts)})]])
|
||||
|
||||
(defn- prepare-default-contacts-events [contacts default-contacts]
|
||||
[[:add-contacts
|
||||
(for [[id {:keys [name photo-path public-key add-chat? global-command
|
||||
dapp? dapp-url dapp-hash bot-url unremovable?]}] default-contacts
|
||||
:let [id' (clojure.core/name id)]
|
||||
:when (not (get contacts id'))]
|
||||
{:whisper-identity id'
|
||||
:address (public-key->address id')
|
||||
:name (:en name)
|
||||
:photo-path photo-path
|
||||
:public-key public-key
|
||||
:unremovable? (boolean unremovable?)
|
||||
:dapp? dapp?
|
||||
:dapp-url (:en dapp-url)
|
||||
:bot-url bot-url
|
||||
:global-command global-command
|
||||
:dapp-hash dapp-hash})]])
|
||||
|
||||
(defn- prepare-add-chat-events [contacts default-contacts]
|
||||
(for [[id {:keys [name add-chat?]}] default-contacts
|
||||
:let [id' (clojure.core/name id)]
|
||||
:when (and (not (get contacts id')) add-chat?)]
|
||||
[:add-chat id' {:name (:en name)}]))
|
||||
|
||||
(defn- prepare-bot-commands-events [contacts default-contacts]
|
||||
(for [[id {:keys [bot-url]}] default-contacts
|
||||
:let [id' (clojure.core/name id)]
|
||||
:when bot-url]
|
||||
[:load-commands! id']))
|
||||
|
||||
(defn- prepare-add-contacts-to-groups-events [contacts default-contacts]
|
||||
(let [groups (for [[id {:keys [groups]}] default-contacts
|
||||
:let [id' (clojure.core/name id)]
|
||||
:when (and (not (get contacts id')) groups)]
|
||||
(for [group groups]
|
||||
{:group-id group :whisper-identity id'}))
|
||||
groups' (vals (group-by :group-id (flatten groups)))]
|
||||
(for [contacts groups']
|
||||
[:add-contacts-to-group
|
||||
(:group-id (first contacts))
|
||||
(mapv :whisper-identity contacts)])))
|
||||
|
||||
(register-handler-fx
|
||||
:load-default-contacts!
|
||||
[(inject-cofx ::get-default-contacts-and-groups)]
|
||||
(fn [{:keys [db default-contacts default-groups]} _]
|
||||
(let [{:keys [groups] :contacts/keys [contacts]} db]
|
||||
{:dispatch-n (concat
|
||||
(prepare-default-groups-events groups default-groups)
|
||||
(prepare-default-contacts-events contacts default-contacts)
|
||||
(prepare-add-chat-events contacts default-contacts)
|
||||
(prepare-bot-commands-events contacts default-contacts)
|
||||
(prepare-add-contacts-to-groups-events contacts default-contacts))})))
|
||||
|
||||
(register-handler-db
|
||||
:remove-contacts-click-handler
|
||||
(fn [db _]
|
||||
(dissoc db
|
||||
:contacts/click-handler
|
||||
:contacts/click-action)))
|
||||
|
||||
(defn- update-pending-status [old-contacts {:keys [whisper-identity pending?] :as contact}]
|
||||
(let [{old-pending :pending?
|
||||
:as old-contact} (get old-contacts whisper-identity)
|
||||
pending?' (if old-contact (and old-pending pending?) pending?)]
|
||||
(assoc contact :pending? (boolean pending?'))))
|
||||
|
||||
(register-handler-fx
|
||||
:add-contacts
|
||||
(fn [{:keys [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 {}))
|
||||
new-contacts-vals (vals new-contacts')]
|
||||
{:db (-> db
|
||||
(update :global-commands merge global-commands)
|
||||
(update :contacts/contacts merge new-contacts'))
|
||||
::save-contacts! new-contacts-vals})))
|
||||
|
||||
(register-handler-fx
|
||||
::send-contact-request
|
||||
(fn [{{:keys [accounts current-account-id] :as db} :db} [_ contact]]
|
||||
(let [current-account (get accounts current-account-id)]
|
||||
{::send-contact-request-fx (merge
|
||||
(select-keys db [:current-public-key :web3 :current-account-id :accounts])
|
||||
(select-keys contact [:whisper-identity])
|
||||
(select-keys current-account [:name :photo-path :status
|
||||
:updates-public-key :updates-private-key]))})))
|
||||
|
||||
(register-handler-fx
|
||||
::add-new-contact
|
||||
(fn [{:keys [db]} [_ {:keys [whisper-identity] :as contact}]]
|
||||
{:db (-> db
|
||||
(update-in [:contacts/contacts whisper-identity] merge contact)
|
||||
(assoc :contacts/new-identity ""))
|
||||
:dispatch [::send-contact-request contact]
|
||||
::save-contact contact}))
|
||||
|
||||
(register-handler-fx
|
||||
:add-new-contact-and-open-chat
|
||||
(fn [{:keys [db]} [_ {:keys [whisper-identity] :as contact}]]
|
||||
(when-not (get-in db [:contacts/contacts whisper-identity])
|
||||
(let [contact (assoc contact :address (public-key->address whisper-identity))]
|
||||
{:dispatch-n [[::add-new-contact contact]
|
||||
[:start-chat whisper-identity {} :navigation-replace]]}))))
|
||||
|
||||
(register-handler-fx
|
||||
:add-pending-contact
|
||||
(fn [{:keys [db]} [_ chat-id]]
|
||||
(let [{:keys [chats] :contacts/keys [contacts]} db
|
||||
contact (if-let [contact-info (get-in chats [chat-id :contact-info])]
|
||||
(read-string contact-info)
|
||||
(get contacts chat-id))
|
||||
contact' (assoc contact :address (public-key->address chat-id)
|
||||
:pending? false)]
|
||||
{:dispatch-n [[::add-new-contact contact']
|
||||
[:watch-contact contact']
|
||||
[:discoveries-send-portions chat-id]]})))
|
||||
|
||||
(register-handler-db
|
||||
:set-contact-identity-from-qr
|
||||
(fn [db [_ _ contact-identity]]
|
||||
(assoc db :contacts/new-identity contact-identity)))
|
||||
|
||||
(register-handler-fx
|
||||
:contact-update-received
|
||||
(fn [{:keys [db]} [_ {:keys [from payload]}]]
|
||||
(let [{:keys [chats current-public-key]} db]
|
||||
(when (not= current-public-key from)
|
||||
(let [{:keys [content timestamp]} payload
|
||||
{:keys [status name profile-image]} (:profile content)
|
||||
prev-last-updated (get-in db [:contacts/contacts from :last-updated])]
|
||||
(when (<= prev-last-updated timestamp)
|
||||
(let [contact {:whisper-identity from
|
||||
:name name
|
||||
:photo-path profile-image
|
||||
:status status
|
||||
:last-updated timestamp}]
|
||||
{:dispatch-n (concat [[:update-contact! contact]]
|
||||
(when (chats from)
|
||||
[[:update-chat! {:chat-id from
|
||||
:name name}]]))})))))))
|
||||
|
||||
(register-handler-fx
|
||||
:update-keys-received
|
||||
(fn [{:keys [db]} [_ {:keys [from payload]}]]
|
||||
(let [{{:keys [public private]} :keypair
|
||||
timestamp :timestamp} payload
|
||||
prev-last-updated (get-in db [:contacts/contacts from :keys-last-updated])]
|
||||
(when (<= prev-last-updated timestamp)
|
||||
(let [contact {:whisper-identity from
|
||||
:public-key public
|
||||
:private-key private
|
||||
:keys-last-updated timestamp}]
|
||||
{:dispatch [:update-contact! contact]})))))
|
||||
|
||||
(register-handler-fx
|
||||
:contact-online-received
|
||||
(fn [{:keys [db]} [_ {:keys [from]
|
||||
{{:keys [timestamp]} :content} :payload}]]
|
||||
(let [prev-last-online (get-in db [:contacts/contacts from :last-online])]
|
||||
(when (and timestamp (< prev-last-online timestamp))
|
||||
{::reset-pending-messages from
|
||||
:dispatch [:update-contact! {:whisper-identity from
|
||||
:last-online timestamp}]}))))
|
||||
|
||||
(register-handler-fx
|
||||
:hide-contact
|
||||
(fn [{:keys [db]} [_ {:keys [whisper-identity] :as contact}]]
|
||||
{::stop-watching-contact (merge
|
||||
(select-keys db [:web3])
|
||||
(select-keys contact [:whisper-identity]))
|
||||
:dispatch-n [[:update-contact! {:whisper-identity whisper-identity
|
||||
:pending? true}]
|
||||
[:account-update-keys]]}))
|
||||
|
||||
(defn remove-contact-from-group [whisper-identity]
|
||||
(fn [contacts]
|
||||
(remove #(= whisper-identity (:identity %)) contacts)))
|
||||
|
||||
(register-handler-fx
|
||||
:remove-contact-from-group
|
||||
(fn [{:keys [db]} [_ whisper-identity group-id]]
|
||||
(let [{:keys [contact-groups]} db
|
||||
group' (update (contact-groups group-id) :contacts (remove-contact-from-group whisper-identity))]
|
||||
{:dispatch [:update-group group']})))
|
||||
|
||||
(register-handler-fx
|
||||
:remove-contact
|
||||
(fn [{:keys [db]} [_ whisper-identity pred]]
|
||||
(let [contact (get-in db [:contacts/contacts whisper-identity])]
|
||||
(when (and contact (pred contact))
|
||||
{:db (update db :contacts/contacts dissoc whisper-identity)
|
||||
::delete-contact contact}))))
|
||||
|
||||
(register-handler-fx
|
||||
:open-contact-toggle-list
|
||||
(fn [{:keys [db]} [_ group-type]]
|
||||
{:db (-> db
|
||||
(assoc :group-type group-type
|
||||
:selected-contacts #{}
|
||||
:new-chat-name "")
|
||||
(assoc-in [:toolbar-search :show] nil)
|
||||
(assoc-in [:toolbar-search :text] ""))
|
||||
:dispatch [:navigate-to :contact-toggle-list]}))
|
||||
|
||||
(register-handler-fx
|
||||
:open-chat-with-contact
|
||||
(fn [_ [_ {:keys [whisper-identity dapp?] :as contact}]]
|
||||
{:dispatch-n (concat
|
||||
[[:navigate-to-clean :chat-list]
|
||||
[:start-chat whisper-identity {}]]
|
||||
(when-not dapp?
|
||||
[[::send-contact-request contact]]))}))
|
||||
|
||||
(register-handler-fx
|
||||
:add-contact-handler
|
||||
(fn [{:keys [db]} [_ id]]
|
||||
(if (v/is-address? id)
|
||||
{::request-contacts-by-address id}
|
||||
{:dispatch (if (get-in db [:contacts/contacts id])
|
||||
[:add-pending-contact id]
|
||||
[:add-new-contact-and-open-chat {:name (generate-gfy)
|
||||
:photo-path (identicon id)
|
||||
:whisper-identity id}])})))
|
|
@ -1,409 +0,0 @@
|
|||
(ns status-im.contacts.handlers
|
||||
(:require [re-frame.core :refer [after dispatch]]
|
||||
[status-im.utils.handlers :refer [register-handler]]
|
||||
[status-im.data-store.contacts :as contacts]
|
||||
[status-im.utils.crypt :refer [encrypt]]
|
||||
[clojure.string :as s]
|
||||
[status-im.protocol.core :as protocol]
|
||||
[status-im.utils.utils :refer [http-post]]
|
||||
[status-im.utils.phone-number :refer [format-phone-number]]
|
||||
[status-im.utils.handlers :as u]
|
||||
[status-im.navigation.handlers :as nav]
|
||||
[status-im.utils.random :as random]
|
||||
[status-im.i18n :refer [label]]
|
||||
[taoensso.timbre :as log]
|
||||
[cljs.reader :refer [read-string]]
|
||||
[status-im.utils.js-resources :as js-res]
|
||||
[status-im.react-native.js-dependencies :as rn-dependencies]))
|
||||
|
||||
(defmethod nav/preload-data! :group-contacts
|
||||
[db [_ _ group show-search?]]
|
||||
(-> db
|
||||
(assoc :contacts-group group)
|
||||
(update :toolbar-search assoc
|
||||
:show (when show-search? :contact-list)
|
||||
:text "")))
|
||||
|
||||
(defmethod nav/preload-data! :edit-group
|
||||
[db [_ _ group group-type]]
|
||||
(if group
|
||||
(assoc db :contact-group-id (:group-id group)
|
||||
:group-type group-type
|
||||
:new-chat-name (:name group))
|
||||
db))
|
||||
|
||||
(defmethod nav/preload-data! :contact-list
|
||||
[db [_ _ click-handler]]
|
||||
(-> db
|
||||
(assoc-in [:toolbar-search :show] nil)
|
||||
(assoc-in [:contact-list-ui-props :edit?] false)
|
||||
(assoc-in [:contacts-ui-props :edit?] false)
|
||||
(assoc :contacts-click-handler click-handler)))
|
||||
|
||||
(defmethod nav/preload-data! :reorder-groups
|
||||
[db [_ _]]
|
||||
(assoc db :groups-order (->> (vals (:contact-groups db))
|
||||
(remove :pending?)
|
||||
(sort-by :order >)
|
||||
(map :group-id))))
|
||||
|
||||
(register-handler :remove-contacts-click-handler
|
||||
(fn [db]
|
||||
(dissoc db
|
||||
:contacts-click-handler
|
||||
:contacts-click-action)))
|
||||
|
||||
(defn save-contact
|
||||
[_ [_ contact]]
|
||||
(contacts/save contact))
|
||||
|
||||
(defn watch-contact
|
||||
[{:keys [web3]} [_ {:keys [whisper-identity public-key private-key]}]]
|
||||
(when (and public-key private-key)
|
||||
(protocol/watch-user! {:web3 web3
|
||||
:identity whisper-identity
|
||||
:keypair {:public public-key
|
||||
:private private-key}
|
||||
:callback #(dispatch [:incoming-message %1 %2])})))
|
||||
|
||||
(register-handler :watch-contact (u/side-effect! watch-contact))
|
||||
|
||||
(defn stop-watching-contact
|
||||
[{:keys [web3]} [_ {:keys [whisper-identity]}]]
|
||||
(protocol/stop-watching-user! {:web3 web3
|
||||
:identity whisper-identity}))
|
||||
|
||||
(register-handler :stop-watching-contact (u/side-effect! stop-watching-contact))
|
||||
|
||||
(defn send-contact-request
|
||||
[{:keys [current-public-key web3 current-account-id accounts]} [_ contact]]
|
||||
(let [{:keys [whisper-identity]} contact
|
||||
{:keys [name photo-path updates-public-key updates-private-key status]}
|
||||
(get accounts current-account-id)]
|
||||
(protocol/contact-request!
|
||||
{:web3 web3
|
||||
:message {:from current-public-key
|
||||
:to whisper-identity
|
||||
:message-id (random/id)
|
||||
:payload {:contact {:name name
|
||||
:profile-image photo-path
|
||||
:address current-account-id
|
||||
:status status}
|
||||
:keypair {:public updates-public-key
|
||||
:private updates-private-key}}}})))
|
||||
|
||||
(register-handler :send-contact-request! (u/side-effect! send-contact-request))
|
||||
|
||||
(register-handler :update-contact!
|
||||
(fn [db [_ {:keys [whisper-identity] :as contact}]]
|
||||
(if (contacts/exists? whisper-identity)
|
||||
(do
|
||||
(contacts/save contact)
|
||||
(update-in db [:contacts whisper-identity] merge contact))
|
||||
db)))
|
||||
|
||||
(defn load-contacts! [db _]
|
||||
(let [contacts-list (->> (contacts/get-all)
|
||||
(map (fn [{:keys [whisper-identity] :as contact}]
|
||||
[whisper-identity contact])))
|
||||
global-commands (->> contacts-list
|
||||
(filter (fn [[_ c]] (:global-command c)))
|
||||
(map (fn [[id {:keys [global-command]}]]
|
||||
[(keyword id) (-> global-command
|
||||
(update :params (comp vec vals))
|
||||
(assoc :bot id
|
||||
:type :command))]))
|
||||
(into {}))
|
||||
contacts (into {} contacts-list)]
|
||||
(doseq [[_ contact] contacts]
|
||||
(dispatch [:watch-contact contact]))
|
||||
(assoc db :contacts contacts
|
||||
:global-commands global-commands)))
|
||||
|
||||
(register-handler :load-contacts load-contacts!)
|
||||
|
||||
(defn contact-name [contact]
|
||||
(->> contact
|
||||
((juxt :givenName :middleName :familyName))
|
||||
(remove s/blank?)
|
||||
(s/join " ")))
|
||||
|
||||
(defn normalize-phone-contacts [contacts]
|
||||
(let [contacts' (js->clj contacts :keywordize-keys true)]
|
||||
(map (fn [{:keys [thumbnailPath phoneNumbers] :as contact}]
|
||||
{:name (contact-name contact)
|
||||
:photo-path thumbnailPath
|
||||
:phone-numbers phoneNumbers}) contacts')))
|
||||
|
||||
(defn fetch-contacts-from-phone!
|
||||
[_ _]
|
||||
(.getAll rn-dependencies/contacts
|
||||
(fn [error contacts]
|
||||
(if error
|
||||
(log/debug :error-on-fetching-loading error)
|
||||
(let [contacts' (normalize-phone-contacts contacts)]
|
||||
(dispatch [:get-contacts-identities contacts']))))))
|
||||
|
||||
(register-handler :sync-contacts
|
||||
(u/side-effect! fetch-contacts-from-phone!))
|
||||
|
||||
(defn get-contacts-by-hash [contacts]
|
||||
(->> contacts
|
||||
(mapcat (fn [{:keys [phone-numbers] :as contact}]
|
||||
(map (fn [{:keys [number]}]
|
||||
(let [number' (format-phone-number number)]
|
||||
[(encrypt number')
|
||||
(-> contact
|
||||
(assoc :phone-number number')
|
||||
(dissoc :phone-numbers))]))
|
||||
phone-numbers)))
|
||||
(into {})))
|
||||
|
||||
(defn add-identity [contacts-by-hash contacts]
|
||||
(map (fn [{:keys [phone-number-hash whisper-identity address]}]
|
||||
(let [contact (contacts-by-hash phone-number-hash)]
|
||||
(assoc contact :whisper-identity whisper-identity
|
||||
:address address)))
|
||||
(js->clj contacts)))
|
||||
|
||||
(defn request-stored-contacts [contacts]
|
||||
(let [contacts-by-hash (get-contacts-by-hash contacts)
|
||||
data (or (keys contacts-by-hash) ())]
|
||||
(http-post "get-contacts" {:phone-number-hashes data}
|
||||
(fn [{:keys [contacts]}]
|
||||
(let [contacts' (add-identity contacts-by-hash contacts)]
|
||||
(dispatch [:add-contacts contacts']))))))
|
||||
|
||||
(defn get-identities-by-contacts! [_ [_ contacts]]
|
||||
(request-stored-contacts contacts))
|
||||
|
||||
(register-handler :get-contacts-identities
|
||||
(u/side-effect! get-identities-by-contacts!))
|
||||
|
||||
(defn add-contacts-to-groups [{:keys [new-contacts]} _]
|
||||
(let [default-contacts js-res/default-contacts]
|
||||
(doseq [{:keys [whisper-identity]} new-contacts]
|
||||
(let [groups (:groups ((keyword whisper-identity) default-contacts))]
|
||||
(doseq [group groups]
|
||||
(dispatch [:add-contacts-to-group group [whisper-identity]]))))))
|
||||
|
||||
(defn save-contacts! [{:keys [new-contacts]} _]
|
||||
(contacts/save-all new-contacts))
|
||||
|
||||
(defn update-pending-status [old-contacts {:keys [whisper-identity pending?] :as contact}]
|
||||
(let [{old-pending :pending?
|
||||
:as old-contact} (get old-contacts whisper-identity)
|
||||
pending?' (if old-contact (and old-pending pending?) pending?)]
|
||||
(assoc contact :pending? (boolean pending?'))))
|
||||
|
||||
(defn add-new-contacts
|
||||
[{:keys [contacts] :as db} [_ new-contacts]]
|
||||
(let [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
|
||||
(update :global-commands merge global-commands)
|
||||
(update :contacts merge new-contacts')
|
||||
(assoc :new-contacts (vals new-contacts')))))
|
||||
|
||||
(defn public-key->address [public-key]
|
||||
(let [length (count public-key)
|
||||
normalized-key (case length
|
||||
132 (subs public-key 4)
|
||||
130 (subs public-key 2)
|
||||
128 public-key
|
||||
nil)]
|
||||
(when normalized-key
|
||||
(subs (.sha3 js/Web3.prototype normalized-key #js {:encoding "hex"}) 26))))
|
||||
|
||||
(register-handler :load-default-contacts!
|
||||
(u/side-effect!
|
||||
(fn [{:keys [contacts groups]}]
|
||||
(let [default-contacts js-res/default-contacts
|
||||
default-groups js-res/default-contact-groups]
|
||||
(dispatch [:add-groups (mapv
|
||||
(fn [[id {:keys [name contacts]}]]
|
||||
{:group-id (clojure.core/name id)
|
||||
:name (:en name)
|
||||
:order 0
|
||||
:timestamp (random/timestamp)
|
||||
:contacts (mapv #(hash-map :identity %) contacts)})
|
||||
default-groups)])
|
||||
(doseq [[id {:keys [name photo-path public-key add-chat? global-command
|
||||
dapp? dapp-url dapp-hash bot-url unremovable?]}] default-contacts]
|
||||
(let [id' (clojure.core/name id)]
|
||||
(when-not (get contacts id')
|
||||
(when add-chat?
|
||||
(dispatch [:add-chat id' {:name (:en name)}]))
|
||||
(let [contact
|
||||
{:whisper-identity id'
|
||||
:address (public-key->address id')
|
||||
:name (:en name)
|
||||
:photo-path photo-path
|
||||
:public-key public-key
|
||||
:unremovable? (boolean unremovable?)
|
||||
:dapp? dapp?
|
||||
:dapp-url (:en dapp-url)
|
||||
:bot-url bot-url
|
||||
:global-command global-command
|
||||
:dapp-hash dapp-hash}]
|
||||
(dispatch [:add-contacts [contact]])))
|
||||
(when bot-url
|
||||
(dispatch [:load-commands! id']))))))))
|
||||
|
||||
|
||||
(register-handler :add-contacts
|
||||
[(after save-contacts!)
|
||||
(after add-contacts-to-groups)]
|
||||
add-new-contacts)
|
||||
|
||||
(defn add-new-contact [db [_ {:keys [whisper-identity] :as contact}]]
|
||||
(-> db
|
||||
(update-in [:contacts whisper-identity] merge contact)
|
||||
(assoc :new-contact-identity "")))
|
||||
|
||||
(register-handler :add-new-contact
|
||||
(u/side-effect!
|
||||
(fn [_ [_ {:keys [whisper-identity] :as contact}]]
|
||||
(when-not (contacts/get-by-id whisper-identity)
|
||||
(let [contact (assoc contact :address (public-key->address whisper-identity))]
|
||||
(dispatch [::prepare-contact contact]))
|
||||
(dispatch [:start-chat whisper-identity {} :navigation-replace])))))
|
||||
|
||||
(register-handler ::prepare-contact
|
||||
(u/handlers->
|
||||
add-new-contact
|
||||
save-contact
|
||||
send-contact-request))
|
||||
|
||||
(register-handler ::update-pending-contact
|
||||
(after save-contact)
|
||||
add-new-contact)
|
||||
|
||||
(register-handler :add-pending-contact
|
||||
(u/side-effect!
|
||||
(fn [{:keys [chats contacts]} [_ chat-id]]
|
||||
(let [contact (if-let [contact-info (get-in chats [chat-id :contact-info])]
|
||||
(read-string contact-info)
|
||||
(assoc (get contacts chat-id) :pending? false))
|
||||
contact' (assoc contact :address (public-key->address chat-id)
|
||||
:pending? false)]
|
||||
(dispatch [::prepare-contact contact'])
|
||||
(dispatch [:watch-contact contact'])
|
||||
(dispatch [:discoveries-send-portions chat-id])))))
|
||||
|
||||
(defn set-contact-identity-from-qr
|
||||
[db [_ _ contact-identity]]
|
||||
(assoc db :new-contact-identity contact-identity))
|
||||
|
||||
(register-handler :set-contact-identity-from-qr set-contact-identity-from-qr)
|
||||
|
||||
(register-handler :contact-update-received
|
||||
(u/side-effect!
|
||||
(fn [{:keys [chats current-public-key] :as db} [_ {:keys [from payload]}]]
|
||||
(when (not= current-public-key from)
|
||||
(let [{:keys [content timestamp]} payload
|
||||
{:keys [status name profile-image]} (:profile content)
|
||||
prev-last-updated (get-in db [:contacts from :last-updated])]
|
||||
(when (<= prev-last-updated timestamp)
|
||||
(let [contact {:whisper-identity from
|
||||
:name name
|
||||
:photo-path profile-image
|
||||
:status status
|
||||
:last-updated timestamp}]
|
||||
(dispatch [:update-contact! contact])
|
||||
(when (chats from)
|
||||
(dispatch [:update-chat! {:chat-id from
|
||||
:name name}])))))))))
|
||||
|
||||
(register-handler :update-keys-received
|
||||
(u/side-effect!
|
||||
(fn [db [_ {:keys [from payload]}]]
|
||||
(let [{{:keys [public private]} :keypair
|
||||
timestamp :timestamp} payload
|
||||
prev-last-updated (get-in db [:contacts from :keys-last-updated])]
|
||||
|
||||
|
||||
(when (<= prev-last-updated timestamp)
|
||||
(let [contact {:whisper-identity from
|
||||
:public-key public
|
||||
:private-key private
|
||||
:keys-last-updated timestamp}]
|
||||
(dispatch [:update-contact! contact])))))))
|
||||
|
||||
(register-handler :contact-online-received
|
||||
(u/side-effect!
|
||||
(fn [db [_ {:keys [from]
|
||||
{{:keys [timestamp]} :content} :payload}]]
|
||||
(let [prev-last-online (get-in db [:contacts from :last-online])]
|
||||
(when (and timestamp (< prev-last-online timestamp))
|
||||
(protocol/reset-pending-messages! from)
|
||||
(dispatch [:update-contact! {:whisper-identity from
|
||||
:last-online timestamp}]))))))
|
||||
|
||||
(register-handler :hide-contact
|
||||
(after stop-watching-contact)
|
||||
(u/side-effect!
|
||||
(fn [_ [_ {:keys [whisper-identity] :as contact}]]
|
||||
(dispatch [:update-contact! {:whisper-identity whisper-identity
|
||||
:pending? true}])
|
||||
(dispatch [:account-update-keys]))))
|
||||
|
||||
(defn remove-contact-from-group [whisper-identity]
|
||||
(fn [contacts]
|
||||
(remove #(= whisper-identity (:identity %)) contacts)))
|
||||
|
||||
(register-handler :remove-contact-from-group
|
||||
(u/side-effect!
|
||||
(fn [{:keys [contact-groups]} [_ whisper-identity group-id]]
|
||||
(let [group' (update (contact-groups group-id) :contacts (remove-contact-from-group whisper-identity))]
|
||||
(dispatch [:update-group group'])))))
|
||||
|
||||
(register-handler :remove-contact
|
||||
(fn [db [_ whisper-identity pred]]
|
||||
(if-let [contact (contacts/get-by-id whisper-identity)]
|
||||
(if (pred contact)
|
||||
(do
|
||||
(contacts/delete contact)
|
||||
(update db :contacts dissoc whisper-identity))
|
||||
db)
|
||||
db)))
|
||||
|
||||
(register-handler :open-contact-menu
|
||||
(u/side-effect!
|
||||
(fn [_ [_ list-selection-fn {:keys [name] :as contact}]]
|
||||
(list-selection-fn {:title name
|
||||
:options [(label :t/remove-contact)]
|
||||
:callback (fn [index]
|
||||
(case index
|
||||
0 (dispatch [:hide-contact contact])
|
||||
:default))
|
||||
:cancel-text (label :t/cancel)}))))
|
||||
|
||||
(register-handler :open-contact-toggle-list
|
||||
(after #(dispatch [:navigate-to :contact-toggle-list]))
|
||||
(fn [db [_ group-type]]
|
||||
(->
|
||||
(assoc db :group-type group-type
|
||||
:selected-contacts #{}
|
||||
:new-chat-name "")
|
||||
(assoc-in [:toolbar-search :show] nil)
|
||||
(assoc-in [:toolbar-search :text] ""))))
|
||||
|
||||
(register-handler :open-chat-with-contact
|
||||
(u/side-effect!
|
||||
(fn [_ [_ {:keys [whisper-identity dapp?] :as contact}]]
|
||||
(when-not dapp?
|
||||
(dispatch [:send-contact-request! contact]))
|
||||
(dispatch [:navigate-to-clean :chat-list])
|
||||
(dispatch [:start-chat whisper-identity {}]))))
|
|
@ -0,0 +1,33 @@
|
|||
(ns status-im.contacts.navigation
|
||||
(:require [status-im.navigation.handlers :as nav]))
|
||||
|
||||
(defmethod nav/preload-data! :group-contacts
|
||||
[db [_ _ group show-search?]]
|
||||
(-> db
|
||||
(assoc :contacts-group group)
|
||||
(update :toolbar-search assoc
|
||||
:show (when show-search? :contact-list)
|
||||
:text "")))
|
||||
|
||||
(defmethod nav/preload-data! :edit-group
|
||||
[db [_ _ group group-type]]
|
||||
(if group
|
||||
(assoc db :contact-group-id (:group-id group)
|
||||
:group-type group-type
|
||||
:new-chat-name (:name group))
|
||||
db))
|
||||
|
||||
(defmethod nav/preload-data! :contact-list
|
||||
[db [_ _ click-handler]]
|
||||
(-> db
|
||||
(assoc-in [:toolbar-search :show] nil)
|
||||
(assoc-in [:contacts/list-ui-props :edit?] false)
|
||||
(assoc-in [:contacts/ui-props :edit?] false)
|
||||
(assoc :contacts/click-handler click-handler)))
|
||||
|
||||
(defmethod nav/preload-data! :reorder-groups
|
||||
[db [_ _]]
|
||||
(assoc db :groups-order (->> (vals (:contact-groups db))
|
||||
(remove :pending?)
|
||||
(sort-by :order >)
|
||||
(map :group-id))))
|
|
@ -0,0 +1,88 @@
|
|||
(ns status-im.contacts.new-contact.views
|
||||
(:require-macros [status-im.utils.views :refer [defview letsubs]])
|
||||
(:require [re-frame.core :refer [dispatch]]
|
||||
[clojure.string :as str]
|
||||
[status-im.components.react :refer [view text]]
|
||||
[status-im.components.text-field.view :refer [text-field]]
|
||||
[status-im.components.status-bar :refer [status-bar]]
|
||||
[status-im.components.toolbar.view :refer [toolbar]]
|
||||
[status-im.components.toolbar.actions :as act]
|
||||
[status-im.components.toolbar.styles :refer [toolbar-title-container
|
||||
toolbar-title-text
|
||||
toolbar-background1]]
|
||||
[status-im.components.styles :refer [icon-ok button-input-container button-input color-blue]]
|
||||
[status-im.components.image-button.view :refer [scan-button]]
|
||||
[status-im.i18n :refer [label]]
|
||||
[cljs.spec.alpha :as s]
|
||||
[status-im.contacts.styles :as st]
|
||||
[status-im.utils.hex :refer [normalize-hex]]
|
||||
[status-im.utils.platform :refer [platform-specific]]
|
||||
[status-im.contacts.db :as v]))
|
||||
|
||||
(def toolbar-title
|
||||
[view toolbar-title-container
|
||||
[text {:style toolbar-title-text}
|
||||
(label :t/add-new-contact)]])
|
||||
|
||||
(defn- validation-error-message
|
||||
[whisper-identity {:keys [address public-key]} error]
|
||||
(cond
|
||||
(#{(normalize-hex address) (normalize-hex public-key)}
|
||||
(normalize-hex whisper-identity))
|
||||
(label :t/can-not-add-yourself)
|
||||
|
||||
(not (s/valid? ::v/public-key whisper-identity))
|
||||
(label :t/enter-valid-public-key)
|
||||
|
||||
(not (v/contact-can-be-added? whisper-identity))
|
||||
(label :t/contact-already-added)
|
||||
|
||||
:else error))
|
||||
|
||||
(defn toolbar-actions [new-contact-identity account error]
|
||||
(let [error-message (validation-error-message new-contact-identity account error)]
|
||||
[{:image {:source {:uri (if (str/blank? error-message)
|
||||
:icon_ok_blue
|
||||
:icon_ok_disabled)}
|
||||
:style icon-ok}
|
||||
:handler #(when (str/blank? error-message)
|
||||
(dispatch [:add-contact-handler new-contact-identity]))}]))
|
||||
|
||||
(defview contact-whisper-id-input [whisper-identity error]
|
||||
(letsubs [current-account [:get-current-account]]
|
||||
(let [error (when-not (str/blank? whisper-identity)
|
||||
(validation-error-message whisper-identity current-account error))]
|
||||
[view button-input-container
|
||||
[text-field
|
||||
{:error error
|
||||
:error-color color-blue
|
||||
:input-style st/qr-input
|
||||
:value whisper-identity
|
||||
:wrapper-style button-input
|
||||
:label (label :t/public-key)
|
||||
:on-change-text #(do
|
||||
(dispatch [:set :contacts/new-identity %])
|
||||
(dispatch [:set :contacts/new-public-key-error nil]))}]
|
||||
[scan-button {:show-label? (zero? (count whisper-identity))
|
||||
:handler #(dispatch [:scan-qr-code
|
||||
{:toolbar-title (label :t/new-contact)}
|
||||
:set-contact-identity-from-qr])}]])))
|
||||
|
||||
(defview new-contact []
|
||||
(letsubs [new-contact-identity [:get :contacts/new-identity]
|
||||
error [:get :contacts/new-public-key-error]
|
||||
account [:get-current-account]]
|
||||
[view st/contact-form-container
|
||||
[status-bar]
|
||||
[toolbar {:background-color toolbar-background1
|
||||
:style (get-in platform-specific [:component-styles :toolbar])
|
||||
:nav-action (act/back #(dispatch [:navigate-back]))
|
||||
:title (label :t/add-new-contact)
|
||||
:actions (toolbar-actions new-contact-identity account error)}]
|
||||
[view st/form-container
|
||||
[contact-whisper-id-input new-contact-identity error]]
|
||||
[view st/address-explication-container
|
||||
[text {:style st/address-explication
|
||||
:font :default}
|
||||
(label :t/address-explication)]]]))
|
||||
|
|
@ -1,15 +0,0 @@
|
|||
(ns status-im.contacts.specs
|
||||
(:require [cljs.spec.alpha :as s]))
|
||||
|
||||
(s/def :contacts/contacts (s/nilable map?)) ;; {id (string) contact (map)}
|
||||
(s/def :contacts/new-contacts (s/nilable seq?))
|
||||
(s/def :contacts/new-contact-identity (s/nilable string?)) ;;public key of new contact during adding this new contact
|
||||
(s/def :contacts/new-contact-public-key-error (s/nilable string?))
|
||||
(s/def :contacts/contact-identity (s/nilable string?)) ;;on showing this contact profile
|
||||
(s/def :contacts/contacts-ui-props (s/nilable map?))
|
||||
(s/def :contacts/contact-list-ui-props (s/nilable map?))
|
||||
(s/def :contacts/contacts-click-handler (s/nilable fn?)) ;;used in modal list (for example for wallet)
|
||||
(s/def :contacts/contacts-click-action (s/nilable keyword?)) ;;used in modal list (for example for wallet)
|
||||
(s/def :contacts/contacts-click-params (s/nilable map?)) ;;used in modal list (for example for wallet)
|
||||
|
||||
|
|
@ -3,8 +3,6 @@
|
|||
(:require [status-im.components.styles :as common]
|
||||
[status-im.components.tabs.styles :as tabs-st]))
|
||||
|
||||
;; Contacts list
|
||||
|
||||
(def toolbar-actions
|
||||
{:flex-direction :row})
|
||||
|
||||
|
|
|
@ -4,13 +4,15 @@
|
|||
[clojure.string :as str]
|
||||
[status-im.bots.constants :as bots-constants]))
|
||||
|
||||
(reg-sub :current-contact
|
||||
(reg-sub
|
||||
:current-contact
|
||||
(fn [db [_ k]]
|
||||
(get-in db [:contacts (:current-chat-id db) k])))
|
||||
(get-in db [:contacts/contacts (:current-chat-id db) k])))
|
||||
|
||||
(reg-sub :get-contacts
|
||||
(reg-sub
|
||||
:get-contacts
|
||||
(fn [db _]
|
||||
(:contacts db)))
|
||||
(:contacts/contacts db)))
|
||||
|
||||
(defn sort-contacts [contacts]
|
||||
(sort (fn [c1 c2]
|
||||
|
@ -20,74 +22,93 @@
|
|||
(clojure.string/lower-case name2))))
|
||||
(vals contacts)))
|
||||
|
||||
(reg-sub
|
||||
:all-added-contacts
|
||||
:<- [:get-contacts]
|
||||
(fn [contacts]
|
||||
(->> (remove (fn [[_ {:keys [pending? whisper-identity]}]]
|
||||
(or (true? pending?)
|
||||
(bots-constants/hidden-bots whisper-identity))) contacts)
|
||||
(sort-contacts))))
|
||||
|
||||
(reg-sub :all-added-contacts
|
||||
(fn [db]
|
||||
(let [contacts (:contacts db)]
|
||||
(->> (remove (fn [[_ {:keys [pending? whisper-identity]}]]
|
||||
(or (true? pending?)
|
||||
(bots-constants/hidden-bots whisper-identity))) contacts)
|
||||
(sort-contacts)))))
|
||||
|
||||
(reg-sub :all-added-people-contacts
|
||||
(reg-sub
|
||||
:all-added-people-contacts
|
||||
:<- [:all-added-contacts]
|
||||
(fn [contacts]
|
||||
(remove #(true? (:dapp? %)) contacts)))
|
||||
|
||||
(reg-sub :people-in-current-chat
|
||||
(fn [{:keys [current-chat-id]} _]
|
||||
(let [contacts (subscribe [:current-chat-contacts])]
|
||||
(remove #(true? (:dapp? %)) @contacts))))
|
||||
(reg-sub
|
||||
:people-in-current-chat
|
||||
:<- [:current-chat-contacts]
|
||||
(fn [contacts]
|
||||
(remove #(true? (:dapp? %)) contacts)))
|
||||
|
||||
(defn filter-group-contacts [group-contacts contacts]
|
||||
(filter #(group-contacts (:whisper-identity %)) contacts))
|
||||
(let [group-contacts' (into #{} (map #(:identity %) group-contacts))]
|
||||
(filter #(group-contacts' (:whisper-identity %)) contacts)))
|
||||
|
||||
(reg-sub :all-added-group-contacts
|
||||
(reg-sub
|
||||
:group-contacts
|
||||
(fn [db [_ group-id]]
|
||||
(let [contacts (subscribe [:all-added-contacts])
|
||||
group-contacts (into #{} (map #(:identity %)
|
||||
(get-in db [:contact-groups group-id :contacts])))]
|
||||
(filter-group-contacts group-contacts @contacts))))
|
||||
(get-in db [:contact-groups group-id :contacts])))
|
||||
|
||||
(reg-sub
|
||||
:all-added-group-contacts
|
||||
(fn [[_ group-id] _]
|
||||
[(subscribe [:all-added-contacts])
|
||||
(subscribe [:group-contacts group-id])])
|
||||
(fn [[contacts group-contacts] _]
|
||||
(filter-group-contacts group-contacts contacts)))
|
||||
|
||||
(defn filter-not-group-contacts [group-contacts contacts]
|
||||
(remove #(group-contacts (:whisper-identity %)) contacts))
|
||||
(let [group-contacts' (into #{} (map #(:identity %) group-contacts))]
|
||||
(remove #(group-contacts' (:whisper-identity %)) contacts)))
|
||||
|
||||
(reg-sub :all-not-added-group-contacts
|
||||
(fn [db [_ group-id]]
|
||||
(let [contacts (subscribe [:all-added-contacts])
|
||||
group-contacts (into #{} (map #(:identity %)
|
||||
(get-in db [:contact-groups group-id :contacts])))]
|
||||
(filter-not-group-contacts group-contacts @contacts))))
|
||||
(reg-sub
|
||||
:all-not-added-group-contacts
|
||||
(fn [[_ group-id] _]
|
||||
[(subscribe [:all-added-contacts])
|
||||
(subscribe [:group-contacts group-id])])
|
||||
(fn [[contacts group-contacts]]
|
||||
(filter-not-group-contacts group-contacts contacts)))
|
||||
|
||||
(reg-sub :all-added-group-contacts-with-limit
|
||||
(fn [db [_ group-id limit]]
|
||||
(let [contacts (subscribe [:all-added-group-contacts group-id])]
|
||||
(take limit @contacts))))
|
||||
(reg-sub
|
||||
:all-added-group-contacts-with-limit
|
||||
(fn [[_ group-id limit] _]
|
||||
(subscribe [:all-added-group-contacts group-id]))
|
||||
(fn [contacts [_ group-id limit]]
|
||||
(take limit contacts)))
|
||||
|
||||
(reg-sub :all-added-group-contacts-count
|
||||
(fn [_ [_ group-id]]
|
||||
(let [contacts (subscribe [:all-added-group-contacts group-id])]
|
||||
(count @contacts))))
|
||||
(reg-sub
|
||||
:all-added-group-contacts-count
|
||||
(fn [[_ group-id] _]
|
||||
(subscribe [:all-added-group-contacts group-id]))
|
||||
(fn [contacts _]
|
||||
(count contacts)))
|
||||
|
||||
(reg-sub :get-added-contacts-with-limit
|
||||
(reg-sub
|
||||
:get-added-contacts-with-limit
|
||||
:<- [:all-added-contacts]
|
||||
(fn [contacts [_ limit]]
|
||||
(take limit contacts)))
|
||||
|
||||
(reg-sub :added-contacts-count
|
||||
(reg-sub
|
||||
:added-contacts-count
|
||||
:<- [:all-added-contacts]
|
||||
(fn [contacts]
|
||||
(count contacts)))
|
||||
|
||||
(reg-sub :all-added-groups
|
||||
(reg-sub
|
||||
:contact-groups
|
||||
(fn [db]
|
||||
(let [groups (vals (:contact-groups db))]
|
||||
(->> (remove :pending? groups)
|
||||
(sort-by :order >)))))
|
||||
(vals (:contact-groups db))))
|
||||
|
||||
(defn get-contact-letter [contact]
|
||||
(when-let [letter (first (:name contact))]
|
||||
(clojure.string/upper-case letter)))
|
||||
(reg-sub
|
||||
:all-added-groups
|
||||
:<- [:contact-groups]
|
||||
(fn [groups]
|
||||
(->> (remove :pending? groups)
|
||||
(sort-by :order >))))
|
||||
|
||||
(defn search-filter [text item]
|
||||
(let [name (-> (or (:name item) "")
|
||||
|
@ -95,92 +116,126 @@
|
|||
text (str/lower-case text)]
|
||||
(not= (str/index-of name text) nil)))
|
||||
|
||||
(defn search-filter-reaction [contacts]
|
||||
(let [text (subscribe [:get-in [:toolbar-search :text]])]
|
||||
(if @text
|
||||
(filter #(search-filter @text %) @contacts)
|
||||
@contacts)))
|
||||
(defn search-filter-reaction [contacts text]
|
||||
(if text
|
||||
(filter #(search-filter text %) contacts)
|
||||
contacts))
|
||||
|
||||
(reg-sub :all-added-group-contacts-filtered
|
||||
(fn [_ [_ group-id]]
|
||||
(let [contacts (if group-id
|
||||
(subscribe [:all-added-group-contacts group-id])
|
||||
(subscribe [:all-added-contacts]))]
|
||||
(search-filter-reaction contacts))))
|
||||
(reg-sub
|
||||
:all-added-group-contacts-filtered
|
||||
(fn [[_ group-id] _]
|
||||
[(if group-id
|
||||
(subscribe [:all-added-group-contacts group-id])
|
||||
(subscribe [:all-added-contacts]))
|
||||
(subscribe [:get-in [:toolbar-search :text]])])
|
||||
(fn [[contacts text] _]
|
||||
(search-filter-reaction contacts text)))
|
||||
|
||||
(reg-sub :all-group-not-added-contacts-filtered
|
||||
(reg-sub
|
||||
:contact-group-contacts
|
||||
(fn [db]
|
||||
(let [contact-group-id (:contact-group-id db)
|
||||
contacts (subscribe [:all-not-added-group-contacts contact-group-id])]
|
||||
(search-filter-reaction contacts))))
|
||||
(get-in db [:contact-groups (:contact-group-id db) :contacts])))
|
||||
|
||||
(reg-sub :contacts-filtered
|
||||
(fn [_ [_ subscription-id]]
|
||||
(let [contacts (subscribe [subscription-id])]
|
||||
(search-filter-reaction contacts))))
|
||||
(reg-sub
|
||||
:all-not-added-contact-group-contacts
|
||||
(fn [_ _]
|
||||
[(subscribe [:all-added-contacts])
|
||||
(subscribe [:contact-group-contacts])])
|
||||
(fn [[contacts group-contacts]]
|
||||
(filter-not-group-contacts group-contacts contacts)))
|
||||
|
||||
(reg-sub :contacts-with-letters
|
||||
(reg-sub
|
||||
:all-group-not-added-contacts-filtered
|
||||
(fn [_ _]
|
||||
[(subscribe [:all-not-added-contact-group-contacts])
|
||||
(subscribe [:get-in [:toolbar-search :text]])])
|
||||
(fn [[contacts text] _]
|
||||
(search-filter-reaction contacts text)))
|
||||
|
||||
(reg-sub
|
||||
:contacts-filtered
|
||||
(fn [[_ subscription-id] _]
|
||||
[(subscribe [subscription-id])
|
||||
(subscribe [:get-in [:toolbar-search :text]])])
|
||||
(fn [[contacts text]]
|
||||
(search-filter-reaction contacts text)))
|
||||
|
||||
(reg-sub
|
||||
:contact
|
||||
(fn [db]
|
||||
(let [contacts (:contacts db)]
|
||||
(let [ordered (sort-contacts contacts)]
|
||||
(reduce (fn [prev cur]
|
||||
(let [prev-letter (get-contact-letter (last prev))
|
||||
cur-letter (get-contact-letter cur)]
|
||||
(conj prev
|
||||
(if (not= prev-letter cur-letter)
|
||||
(assoc cur :letter cur-letter)
|
||||
cur))))
|
||||
[] ordered)))))
|
||||
(let [identity (:contacts/identity db)]
|
||||
(get-in db [:contacts/contacts identity]))))
|
||||
|
||||
(defn contacts-by-chat [fn db chat-id]
|
||||
(let [chat (get-in db [:chats chat-id])
|
||||
contacts (:contacts db)]
|
||||
(when chat
|
||||
(let [current-participants (->> chat
|
||||
:contacts
|
||||
(map :identity)
|
||||
set)]
|
||||
(fn #(current-participants (:whisper-identity %))
|
||||
(vals contacts))))))
|
||||
|
||||
(defn contacts-by-current-chat [fn db]
|
||||
(let [current-chat-id (:current-chat-id db)]
|
||||
(contacts-by-chat fn db current-chat-id)))
|
||||
|
||||
(reg-sub :contact
|
||||
(fn [db]
|
||||
(let [identity (:contact-identity db)]
|
||||
(get-in db [:contacts identity]))))
|
||||
|
||||
(reg-sub :contact-by-identity
|
||||
(reg-sub
|
||||
:contact-by-identity
|
||||
(fn [db [_ identity]]
|
||||
(get-in db [:contacts identity])))
|
||||
(get-in db [:contacts/contacts identity])))
|
||||
|
||||
(reg-sub :contact-name-by-identity
|
||||
(reg-sub
|
||||
:contact-name-by-identity
|
||||
:<- [:get-contacts]
|
||||
(fn [contacts [_ identity]]
|
||||
(:name (contacts identity))))
|
||||
|
||||
(reg-sub :all-new-contacts
|
||||
(fn [db]
|
||||
(contacts-by-current-chat remove db)))
|
||||
|
||||
(reg-sub :current-chat-contacts
|
||||
(fn [db]
|
||||
(contacts-by-current-chat filter db)))
|
||||
|
||||
(reg-sub :chat-photo
|
||||
(reg-sub
|
||||
:chat-by-id
|
||||
(fn [db [_ chat-id]]
|
||||
(let [chat-id (or chat-id (:current-chat-id db))
|
||||
chat (get-in db [:chats chat-id])
|
||||
contacts (contacts-by-chat filter db chat-id)]
|
||||
(when (and chat (not (:group-chat chat)))
|
||||
(cond
|
||||
(:photo-path chat)
|
||||
(:photo-path chat)
|
||||
(get-in db [:chats chat-id])))
|
||||
|
||||
(pos? (count contacts))
|
||||
(:photo-path (first contacts))
|
||||
(reg-sub
|
||||
:current-chat
|
||||
(fn [db _]
|
||||
(get-in db [:chats (:current-chat-id db)])))
|
||||
|
||||
:else
|
||||
(identicon chat-id))))))
|
||||
(defn chat-contacts [[chat contacts] [_ fn]]
|
||||
(when chat
|
||||
(let [current-participants (->> chat
|
||||
:contacts
|
||||
(map :identity)
|
||||
set)]
|
||||
(fn #(current-participants (:whisper-identity %))
|
||||
(vals contacts)))))
|
||||
|
||||
(reg-sub
|
||||
:contacts-current-chat
|
||||
:<- [:current-chat]
|
||||
:<- [:get-contacts]
|
||||
chat-contacts)
|
||||
|
||||
(reg-sub
|
||||
:all-new-contacts
|
||||
:<- [:contacts-current-chat remove]
|
||||
(fn [contacts]
|
||||
contacts))
|
||||
|
||||
(reg-sub
|
||||
:current-chat-contacts
|
||||
:<- [:contacts-current-chat filter]
|
||||
(fn [contacts]
|
||||
contacts))
|
||||
|
||||
(reg-sub
|
||||
:contacts-by-chat
|
||||
(fn [[_ fn chat-id] _]
|
||||
[(subscribe [:chat-by-id chat-id])
|
||||
(subscribe [:get-contacts])])
|
||||
chat-contacts)
|
||||
|
||||
(reg-sub
|
||||
:chat-photo
|
||||
(fn [[_ chat-id] _]
|
||||
[(if chat-id
|
||||
(subscribe [:chat-by-id chat-id])
|
||||
(subscribe [:current-chat]))
|
||||
(subscribe [:contacts-by-chat filter chat-id])])
|
||||
(fn [[chat contacts] [_ chat-id]]
|
||||
(when (and chat (not (:group-chat chat)))
|
||||
(cond
|
||||
(:photo-path chat)
|
||||
(:photo-path chat)
|
||||
|
||||
(pos? (count contacts))
|
||||
(:photo-path (first contacts))
|
||||
|
||||
:else
|
||||
(identicon chat-id)))))
|
|
@ -1,39 +0,0 @@
|
|||
(ns status-im.contacts.validations
|
||||
(:require [cljs.spec.alpha :as s]
|
||||
[clojure.string :as str]
|
||||
[status-im.data-store.contacts :as contacts]
|
||||
[status-im.js-dependencies :as dependencies]))
|
||||
|
||||
(defn is-address? [s]
|
||||
(.isAddress dependencies/Web3.prototype s))
|
||||
|
||||
(defn contact-can-be-added? [identity]
|
||||
(if (contacts/exists? identity)
|
||||
(:pending? (contacts/get-by-id identity))
|
||||
true))
|
||||
|
||||
(defn hex-string? [s]
|
||||
(let [s' (if (str/starts-with? s "0x")
|
||||
(subs s 2)
|
||||
s)]
|
||||
(boolean (re-matches #"(?i)[0-9a-f]+" s'))))
|
||||
|
||||
(defn valid-length? [identity]
|
||||
(let [length (count identity)]
|
||||
(and
|
||||
(hex-string? identity)
|
||||
(or
|
||||
(and (= 128 length) (not (str/includes? identity "0x")))
|
||||
(and (= 130 length) (str/starts-with? identity "0x"))
|
||||
(and (= 132 length) (str/starts-with? identity "0x04"))
|
||||
(is-address? identity)))))
|
||||
|
||||
(s/def ::identity-length valid-length?)
|
||||
(s/def ::contact-can-be-added contact-can-be-added?)
|
||||
(s/def ::not-empty-string (s/and string? not-empty))
|
||||
(s/def ::name ::not-empty-string)
|
||||
(s/def ::whisper-identity (s/and ::not-empty-string
|
||||
::identity-length))
|
||||
|
||||
(s/def ::contact (s/keys :req-un [::name ::whisper-identity]
|
||||
:opt-un [::phone ::photo-path ::address]))
|
|
@ -1,23 +1,14 @@
|
|||
(ns status-im.contacts.screen
|
||||
(:require-macros [status-im.utils.views :refer [defview]])
|
||||
(ns status-im.contacts.views
|
||||
(:require-macros [status-im.utils.views :refer [defview letsubs]])
|
||||
(:require [re-frame.core :refer [dispatch]]
|
||||
[status-im.components.common.common :as common]
|
||||
[status-im.components.react :refer [view
|
||||
text
|
||||
image
|
||||
icon
|
||||
touchable-highlight
|
||||
scroll-view
|
||||
list-view
|
||||
list-item]]
|
||||
[status-im.components.react :refer [view text icon touchable-highlight scroll-view]]
|
||||
[status-im.components.native-action-button :refer [native-action-button
|
||||
native-action-button-item]]
|
||||
[status-im.components.status-bar :refer [status-bar]]
|
||||
[status-im.components.toolbar-new.view :refer [toolbar]]
|
||||
[status-im.components.toolbar-new.actions :as act]
|
||||
[status-im.components.drawer.view :refer [open-drawer]]
|
||||
[status-im.components.icons.custom-icons :refer [ion-icon]]
|
||||
[status-im.components.context-menu :refer [context-menu]]
|
||||
[status-im.components.contact.contact :refer [contact-view]]
|
||||
[status-im.utils.platform :refer [platform-specific ios? android?]]
|
||||
[status-im.utils.utils :as u]
|
||||
|
@ -26,11 +17,11 @@
|
|||
[status-im.components.styles :refer [color-blue
|
||||
create-icon]]))
|
||||
|
||||
(def contacts-limit 5)
|
||||
(def ^:const contacts-limit 5)
|
||||
|
||||
(def toolbar-options
|
||||
[{:text (label :t/new-contact) :value #(dispatch [:navigate-to :new-contact])}
|
||||
{:text (label :t/edit) :value #(dispatch [:set-in [:contacts-ui-props :edit?] true])}
|
||||
{:text (label :t/edit) :value #(dispatch [:set-in [:contacts/ui-props :edit?] true])}
|
||||
{:text (label :t/new-group) :value #(dispatch [:open-contact-toggle-list :contact-group])}
|
||||
{:text (label :t/reorder-groups) :value #(dispatch [:navigate-to :reorder-groups])}])
|
||||
|
||||
|
@ -39,23 +30,23 @@
|
|||
(act/opts (if ios? toolbar-options (rest toolbar-options)))])
|
||||
|
||||
(defn toolbar-view []
|
||||
[toolbar {:title (label :t/contacts)
|
||||
:nav-action (act/hamburger open-drawer)
|
||||
:actions (toolbar-actions)}])
|
||||
[toolbar {:title (label :t/contacts)
|
||||
:nav-action (act/hamburger open-drawer)
|
||||
:actions (toolbar-actions)}])
|
||||
|
||||
(defn toolbar-edit []
|
||||
[toolbar {:nav-action (act/back #(dispatch [:set-in [:contacts-ui-props :edit?] false]))
|
||||
:actions [{:image :blank}]
|
||||
:title (label :t/edit-contacts)}])
|
||||
[toolbar {:nav-action (act/back #(dispatch [:set-in [:contacts/ui-props :edit?] false]))
|
||||
:actions [{:image :blank}]
|
||||
:title (label :t/edit-contacts)}])
|
||||
|
||||
(defn contact-options [{:keys [unremovable?] :as contact} group]
|
||||
(let [delete-contact-opt {:value #(u/show-confirmation
|
||||
(str (label :t/delete-contact) "?") (label :t/delete-contact-confirmation)
|
||||
(label :t/delete)
|
||||
(fn[] (dispatch [:hide-contact contact])))
|
||||
(fn [] (dispatch [:hide-contact contact])))
|
||||
:text (label :t/delete-contact)
|
||||
:destructive? true}
|
||||
options (if unremovable? [] [delete-contact-opt])]
|
||||
options (if unremovable? [] [delete-contact-opt])]
|
||||
(if group
|
||||
(conj options
|
||||
{:value #(dispatch [:remove-contact-from-group
|
||||
|
@ -69,10 +60,10 @@
|
|||
[view
|
||||
(when subtitle
|
||||
[common/form-title subtitle
|
||||
{:count-value contacts-count
|
||||
:extended? edit?
|
||||
:options [{:value #(dispatch [:navigate-to :edit-group group :contact-group])
|
||||
:text (label :t/edit-group)}]}])
|
||||
{:count-value contacts-count
|
||||
:extended? edit?
|
||||
:options [{:value #(dispatch [:navigate-to :edit-group group :contact-group])
|
||||
:text (label :t/edit-group)}]}])
|
||||
[view st/contacts-list
|
||||
[common/list-footer]
|
||||
(doall
|
||||
|
@ -93,20 +84,20 @@
|
|||
[view st/show-all
|
||||
[touchable-highlight {:on-press #(do
|
||||
(when edit?
|
||||
(dispatch [:set-in [:contact-list-ui-props :edit?] true]))
|
||||
(dispatch [:set-in [:contacts/list-ui-props :edit?] true]))
|
||||
(dispatch [:navigate-to :group-contacts group]))}
|
||||
[view
|
||||
[text {:style st/show-all-text
|
||||
[text {:style st/show-all-text
|
||||
:uppercase? (get-in platform-specific [:uppercase?])
|
||||
:font (get-in platform-specific [:component-styles :contacts :show-all-text-font])}
|
||||
:font (get-in platform-specific [:component-styles :contacts :show-all-text-font])}
|
||||
(str (- contacts-count contacts-limit) " " (label :t/more))]]]]])
|
||||
[common/bottom-shadow]]))
|
||||
|
||||
(defview contact-group-view [{:keys [group] :as params}]
|
||||
[contacts [:all-added-group-contacts-with-limit (:group-id group) contacts-limit]
|
||||
contacts-count [:all-added-group-contacts-count (:group-id group)]]
|
||||
[contact-group-form (merge params {:contacts contacts
|
||||
:contacts-count contacts-count})])
|
||||
(letsubs [contacts [:all-added-group-contacts-with-limit (:group-id group) contacts-limit]
|
||||
contacts-count [:all-added-group-contacts-count (:group-id group)]]
|
||||
[contact-group-form (merge params {:contacts contacts
|
||||
:contacts-count contacts-count})]))
|
||||
|
||||
(defn contacts-action-button []
|
||||
[native-action-button {:button-color color-blue
|
||||
|
@ -122,28 +113,28 @@
|
|||
:style create-icon}]]])
|
||||
|
||||
(defview contact-list [_]
|
||||
[contacts [:get-added-contacts-with-limit contacts-limit]
|
||||
contacts-count [:added-contacts-count]
|
||||
edit? [:get-in [:contacts-ui-props :edit?]]
|
||||
groups [:all-added-groups]
|
||||
tabs-hidden? [:tabs-hidden?]]
|
||||
[view {:flex 1}
|
||||
[view (st/contacts-list-container tabs-hidden?)
|
||||
(if edit?
|
||||
[toolbar-edit]
|
||||
[toolbar-view])
|
||||
(if (pos? (+ (count groups) contacts-count))
|
||||
[scroll-view {:style st/contact-groups}
|
||||
(when (pos? contacts-count)
|
||||
[contact-group-form {:contacts contacts
|
||||
:contacts-count contacts-count
|
||||
:edit? edit?}])
|
||||
(for [group groups]
|
||||
^{:key group}
|
||||
[contact-group-view {:group group
|
||||
:edit? edit?}])]
|
||||
[view st/empty-contact-groups
|
||||
[icon :group_big st/empty-contacts-icon]
|
||||
[text {:style st/empty-contacts-text} (label :t/no-contacts)]])]
|
||||
(when (and android? (not edit?))
|
||||
[contacts-action-button])])
|
||||
(letsubs [contacts [:get-added-contacts-with-limit contacts-limit]
|
||||
contacts-count [:added-contacts-count]
|
||||
edit? [:get-in [:contacts/ui-props :edit?]]
|
||||
groups [:all-added-groups]
|
||||
tabs-hidden? [:tabs-hidden?]]
|
||||
[view {:flex 1}
|
||||
[view (st/contacts-list-container tabs-hidden?)
|
||||
(if edit?
|
||||
[toolbar-edit]
|
||||
[toolbar-view])
|
||||
(if (pos? (+ (count groups) contacts-count))
|
||||
[scroll-view {:style st/contact-groups}
|
||||
(when (pos? contacts-count)
|
||||
[contact-group-form {:contacts contacts
|
||||
:contacts-count contacts-count
|
||||
:edit? edit?}])
|
||||
(for [group groups]
|
||||
^{:key group}
|
||||
[contact-group-view {:group group
|
||||
:edit? edit?}])]
|
||||
[view st/empty-contact-groups
|
||||
[icon :group_big st/empty-contacts-icon]
|
||||
[text {:style st/empty-contacts-text} (label :t/no-contacts)]])]
|
||||
(when (and android? (not edit?))
|
||||
[contacts-action-button])]))
|
|
@ -1,77 +0,0 @@
|
|||
(ns status-im.contacts.views.contact-list
|
||||
(:require-macros [status-im.utils.views :refer [defview]])
|
||||
(:require [re-frame.core :refer [subscribe dispatch dispatch-sync]]
|
||||
[status-im.components.common.common :as common]
|
||||
[status-im.components.renderers.renderers :as renderers]
|
||||
[status-im.components.contact.contact :refer [contact-view]]
|
||||
[status-im.contacts.screen :refer [contact-options]]
|
||||
[status-im.components.react :refer [view text
|
||||
image
|
||||
icon
|
||||
touchable-highlight
|
||||
list-view
|
||||
list-item]]
|
||||
[status-im.components.status-bar :refer [status-bar]]
|
||||
[status-im.components.toolbar-new.view :refer [toolbar-with-search toolbar]]
|
||||
[status-im.components.toolbar-new.actions :as act]
|
||||
[status-im.components.toolbar-new.styles :refer [toolbar-background1]]
|
||||
[status-im.components.drawer.view :refer [drawer-view open-drawer]]
|
||||
[status-im.components.image-button.view :refer [scan-button]]
|
||||
[status-im.contacts.styles :as st]
|
||||
[status-im.utils.listview :as lw]
|
||||
[status-im.i18n :refer [label]]
|
||||
[status-im.utils.platform :refer [platform-specific]]))
|
||||
|
||||
(defn render-row [group edit?]
|
||||
(fn [row _ _]
|
||||
(list-item
|
||||
^{:key row}
|
||||
[contact-view {:contact row
|
||||
:on-press #(dispatch [:open-chat-with-contact %])
|
||||
:extended? edit?
|
||||
:extend-options (contact-options row group)}])))
|
||||
|
||||
(defview contact-list-toolbar-edit [group]
|
||||
[toolbar {:nav-action (act/back #(dispatch [:set-in [:contact-list-ui-props :edit?] false]))
|
||||
:actions [{:image :blank}]
|
||||
:title (if-not group
|
||||
(label :t/contacts)
|
||||
(or (:name group) (label :t/contacts-group-new-chat)))}])
|
||||
|
||||
(defview contact-list-toolbar [group]
|
||||
[show-search [:get-in [:toolbar-search :show]]
|
||||
search-text [:get-in [:toolbar-search :text]]]
|
||||
(toolbar-with-search
|
||||
{:show-search? (= show-search :contact-list)
|
||||
:search-text search-text
|
||||
:search-key :contact-list
|
||||
:title (if-not group
|
||||
(label :t/contacts)
|
||||
(or (:name group) (label :t/contacts-group-new-chat)))
|
||||
:search-placeholder (label :t/search-contacts)
|
||||
:actions [(act/opts [{:text (label :t/edit)
|
||||
:value #(dispatch [:set-in [:contact-list-ui-props :edit?] true])}])]}))
|
||||
|
||||
(defview contacts-list-view [group edit?]
|
||||
[contacts [:all-added-group-contacts-filtered (:group-id group)]]
|
||||
[list-view {:dataSource (lw/to-datasource contacts)
|
||||
:enableEmptySections true
|
||||
:renderRow (render-row group edit?)
|
||||
:keyboardShouldPersistTaps :always
|
||||
:renderHeader renderers/list-header-renderer
|
||||
:renderFooter renderers/list-footer-renderer
|
||||
:renderSeparator renderers/list-separator-renderer
|
||||
:style st/contacts-list}])
|
||||
|
||||
(defview contact-list []
|
||||
[edit? [:get-in [:contact-list-ui-props :edit?]]
|
||||
group [:get :contacts-group]
|
||||
type [:get :group-type]]
|
||||
[drawer-view
|
||||
[view {:flex 1}
|
||||
[view
|
||||
[status-bar]
|
||||
(if edit?
|
||||
[contact-list-toolbar-edit group]
|
||||
[contact-list-toolbar group])]
|
||||
[contacts-list-view group edit?]]])
|
|
@ -1,84 +0,0 @@
|
|||
(ns status-im.contacts.views.contact-list-modal
|
||||
(:require-macros [status-im.utils.views :refer [defview]])
|
||||
(:require [re-frame.core :refer [dispatch]]
|
||||
[status-im.components.common.common :as common]
|
||||
[status-im.components.renderers.renderers :as renderers]
|
||||
[status-im.components.react :refer [view text
|
||||
image
|
||||
icon
|
||||
touchable-highlight
|
||||
list-view
|
||||
list-item]]
|
||||
[status-im.components.contact.contact :refer [contact-view]]
|
||||
[status-im.components.action-button.action-button :refer [action-button
|
||||
action-separator]]
|
||||
[status-im.components.action-button.styles :refer [actions-list]]
|
||||
[status-im.components.status-bar :refer [status-bar]]
|
||||
[status-im.components.toolbar-new.view :refer [toolbar-with-search toolbar]]
|
||||
[status-im.components.toolbar-new.actions :as act]
|
||||
[status-im.components.drawer.view :refer [drawer-view]]
|
||||
[status-im.contacts.styles :as st]
|
||||
[status-im.utils.listview :as lw]
|
||||
[status-im.i18n :refer [label]]))
|
||||
|
||||
(defview contact-list-modal-toolbar []
|
||||
[show-search [:get-in [:toolbar-search :show]]
|
||||
search-text [:get-in [:toolbar-search :text]]]
|
||||
(toolbar-with-search
|
||||
{:show-search? (= show-search :contact-list)
|
||||
:search-text search-text
|
||||
:search-key :contact-list
|
||||
:title (label :t/contacts)
|
||||
:search-placeholder (label :t/search-contacts)}))
|
||||
|
||||
(defn actions-view [action click-handler]
|
||||
[view actions-list
|
||||
[action-button (label :t/enter-address)
|
||||
:address_blue
|
||||
#(do
|
||||
(dispatch [:send-to-webview-bridge
|
||||
{:event (name :webview-send-transaction)}])
|
||||
(dispatch [:navigate-back]))]
|
||||
[action-separator]
|
||||
(if (= :request action)
|
||||
[action-button (label :t/show-qr)
|
||||
:q_r_blue
|
||||
#(click-handler :qr-scan action)]
|
||||
[action-button (label :t/scan-qr)
|
||||
:fullscreen_blue
|
||||
#(click-handler :qr-scan action)])])
|
||||
|
||||
(defn render-row [click-handler action params]
|
||||
(fn [row _ _]
|
||||
(list-item
|
||||
^{:key row}
|
||||
[contact-view {:contact row
|
||||
:on-press #(when click-handler
|
||||
(click-handler row action params))}])))
|
||||
|
||||
(defview contact-list-modal []
|
||||
[contacts [:contacts-filtered :all-added-people-contacts]
|
||||
click-handler [:get :contacts-click-handler]
|
||||
action [:get :contacts-click-action]
|
||||
params [:get :contacts-click-params]]
|
||||
[drawer-view
|
||||
[view {:flex 1}
|
||||
[status-bar {:type :modal}]
|
||||
[contact-list-modal-toolbar]
|
||||
[list-view {:dataSource (lw/to-datasource contacts)
|
||||
:enableEmptySections true
|
||||
:renderRow (render-row click-handler action params)
|
||||
:bounces false
|
||||
:keyboardShouldPersistTaps :always
|
||||
:renderHeader #(list-item
|
||||
[view
|
||||
[actions-view action click-handler]
|
||||
[common/bottom-shadow]
|
||||
[common/form-title (label :t/choose-from-contacts)
|
||||
{:count-value (count contacts)}]
|
||||
[common/list-header]])
|
||||
:renderFooter #(list-item [view
|
||||
[common/list-footer]
|
||||
[common/bottom-shadow]])
|
||||
:renderSeparator renderers/list-separator-renderer
|
||||
:style st/contacts-list-modal}]]])
|
|
@ -1,119 +0,0 @@
|
|||
(ns status-im.contacts.views.new-contact
|
||||
(:require-macros [status-im.utils.views :refer [defview]])
|
||||
(:require [re-frame.core :refer [subscribe dispatch dispatch-sync]]
|
||||
[clojure.string :as str]
|
||||
[status-im.components.react :refer [view
|
||||
text
|
||||
image
|
||||
touchable-highlight]]
|
||||
[status-im.components.text-field.view :refer [text-field]]
|
||||
[status-im.utils.identicon :refer [identicon]]
|
||||
[status-im.components.status-bar :refer [status-bar]]
|
||||
[status-im.components.toolbar.view :refer [toolbar]]
|
||||
[status-im.components.toolbar.actions :as act]
|
||||
[status-im.components.toolbar.styles :refer [toolbar-title-container
|
||||
toolbar-title-text
|
||||
toolbar-background1]]
|
||||
[status-im.utils.utils :refer [http-post]]
|
||||
[status-im.components.styles :refer [icon-ok
|
||||
button-input-container
|
||||
button-input
|
||||
color-blue]]
|
||||
[status-im.components.image-button.view :refer [scan-button]]
|
||||
[status-im.i18n :refer [label]]
|
||||
[cljs.spec.alpha :as s]
|
||||
[status-im.contacts.validations :as v]
|
||||
[status-im.contacts.styles :as st]
|
||||
[status-im.data-store.contacts :as contacts]
|
||||
[status-im.utils.gfycat.core :refer [generate-gfy]]
|
||||
[status-im.utils.hex :refer [normalize-hex]]
|
||||
[status-im.utils.platform :refer [platform-specific]]))
|
||||
|
||||
|
||||
(def toolbar-title
|
||||
[view toolbar-title-container
|
||||
[text {:style toolbar-title-text}
|
||||
(label :t/add-new-contact)]])
|
||||
|
||||
(defn on-add-contact [id]
|
||||
(if (v/is-address? id)
|
||||
(http-post "get-contacts-by-address" {:addresses [id]}
|
||||
(fn [{:keys [contacts]}]
|
||||
(if (> (count contacts) 0)
|
||||
(let [{:keys [whisper-identity]} (first contacts)
|
||||
contact {:name (generate-gfy)
|
||||
:address id
|
||||
:photo-path (identicon whisper-identity)
|
||||
:whisper-identity whisper-identity}]
|
||||
(if (contacts/exists? whisper-identity)
|
||||
(dispatch [:add-pending-contact whisper-identity])
|
||||
(dispatch [:add-new-contact contact])))
|
||||
(dispatch [:set :new-contact-public-key-error (label :t/unknown-address)]))))
|
||||
(if (contacts/exists? id)
|
||||
(dispatch [:add-pending-contact id])
|
||||
(dispatch [:add-new-contact {:name (generate-gfy)
|
||||
:photo-path (identicon id)
|
||||
:whisper-identity id}]))))
|
||||
|
||||
(defn- validation-error-message
|
||||
[whisper-identity {:keys [address public-key]} error]
|
||||
(cond
|
||||
(#{(normalize-hex address) (normalize-hex public-key)}
|
||||
(normalize-hex whisper-identity))
|
||||
(label :t/can-not-add-yourself)
|
||||
|
||||
(not (s/valid? ::v/whisper-identity whisper-identity))
|
||||
(label :t/enter-valid-public-key)
|
||||
|
||||
(not (s/valid? ::v/contact-can-be-added whisper-identity))
|
||||
(label :t/contact-already-added)
|
||||
|
||||
:else error))
|
||||
|
||||
(defn toolbar-actions [new-contact-identity account error]
|
||||
(let [error-message (validation-error-message new-contact-identity account error)]
|
||||
[{:image {:source {:uri (if (str/blank? error-message)
|
||||
:icon_ok_blue
|
||||
:icon_ok_disabled)}
|
||||
:style icon-ok}
|
||||
:handler #(when (str/blank? error-message)
|
||||
(on-add-contact new-contact-identity))}]))
|
||||
|
||||
(defview contact-whisper-id-input [whisper-identity error]
|
||||
[current-account [:get-current-account]]
|
||||
(let [error (when-not (str/blank? whisper-identity)
|
||||
(validation-error-message whisper-identity current-account error))]
|
||||
[view button-input-container
|
||||
[text-field
|
||||
{:error error
|
||||
:error-color color-blue
|
||||
:input-style st/qr-input
|
||||
:value whisper-identity
|
||||
:wrapper-style button-input
|
||||
:label (label :t/public-key)
|
||||
:on-change-text #(do
|
||||
(dispatch [:set-in [:new-contact-identity] %])
|
||||
(dispatch [:set :new-contact-public-key-error nil]))}]
|
||||
[scan-button {:show-label? (zero? (count whisper-identity))
|
||||
:handler #(dispatch [:scan-qr-code
|
||||
{:toolbar-title (label :t/new-contact)}
|
||||
:set-contact-identity-from-qr])}]]))
|
||||
|
||||
|
||||
(defview new-contact []
|
||||
[new-contact-identity [:get :new-contact-identity]
|
||||
error [:get :new-contact-public-key-error]
|
||||
account [:get-current-account]]
|
||||
[view st/contact-form-container
|
||||
[status-bar]
|
||||
[toolbar {:background-color toolbar-background1
|
||||
:style (get-in platform-specific [:component-styles :toolbar])
|
||||
:nav-action (act/back #(dispatch [:navigate-back]))
|
||||
:title (label :t/add-new-contact)
|
||||
:actions (toolbar-actions new-contact-identity account error)}]
|
||||
[view st/form-container
|
||||
[contact-whisper-id-input new-contact-identity error]]
|
||||
[view st/address-explication-container
|
||||
[text {:style st/address-explication
|
||||
:font :default}
|
||||
(label :t/address-explication)]]])
|
|
@ -8,7 +8,7 @@
|
|||
:keyboard-height 0
|
||||
:accounts {}
|
||||
:navigation-stack '()
|
||||
:contacts {}
|
||||
:contacts/contacts {}
|
||||
:qr-codes {}
|
||||
:contact-groups {}
|
||||
:selected-contacts #{}
|
||||
|
|
|
@ -72,7 +72,7 @@
|
|||
|
||||
(register-handler :debug-add-contact
|
||||
(u/side-effect!
|
||||
(fn [{:keys [contacts]} [_ {:keys [name whisper-identity dapp-url bot-url] :as dapp-data}]]
|
||||
(fn [{:contacts/keys [contacts]} [_ {:keys [name whisper-identity dapp-url bot-url] :as dapp-data}]]
|
||||
(if (and name
|
||||
whisper-identity
|
||||
(or dapp-url bot-url))
|
||||
|
@ -113,7 +113,8 @@
|
|||
|
||||
(register-handler :debug-contact-changed
|
||||
(u/side-effect!
|
||||
(fn [{:keys [webview-bridge current-chat-id contacts]} [_ {:keys [whisper-identity] :as dapp-data}]]
|
||||
(fn [{:keys [webview-bridge current-chat-id]
|
||||
:contacts/keys [contacts]} [_ {:keys [whisper-identity] :as dapp-data}]]
|
||||
(when (get-in contacts [whisper-identity :debug?])
|
||||
(when (and (= current-chat-id whisper-identity)
|
||||
webview-bridge)
|
||||
|
@ -132,7 +133,7 @@
|
|||
|
||||
(register-handler :debug-dapps-list
|
||||
(u/side-effect!
|
||||
(fn [{:keys [contacts]}]
|
||||
(fn [{:contacts/keys [contacts]}]
|
||||
(let [contacts (->> (vals contacts)
|
||||
(filter :debug?)
|
||||
(map #(select-keys % [:name :whisper-identity :dapp-url :bot-url])))]
|
||||
|
|
|
@ -29,7 +29,8 @@
|
|||
|
||||
(register-handler :broadcast-status
|
||||
(u/side-effect!
|
||||
(fn [{:keys [current-public-key web3 current-account-id accounts contacts]}
|
||||
(fn [{:keys [current-public-key web3 current-account-id accounts]
|
||||
:contacts/keys [contacts]}
|
||||
[_ status hashtags]]
|
||||
(let [{:keys [name photo-path]} (get accounts current-account-id)
|
||||
message-id (random/id)
|
||||
|
@ -73,7 +74,8 @@
|
|||
|
||||
(register-handler :request-discoveries
|
||||
(u/side-effect!
|
||||
(fn [{:keys [current-public-key web3 contacts]}]
|
||||
(fn [{:keys [current-public-key web3]
|
||||
:contacts/keys [contacts]}]
|
||||
(doseq [id (u/identities contacts)]
|
||||
(when-not (protocol/message-pending? web3 :discoveries-request id)
|
||||
(protocol/send-discoveries-request!
|
||||
|
@ -84,7 +86,8 @@
|
|||
|
||||
(register-handler :discoveries-send-portions
|
||||
(u/side-effect!
|
||||
(fn [{:keys [current-public-key contacts web3]} [_ to]]
|
||||
(fn [{:keys [current-public-key web3]
|
||||
:contacts/keys [contacts]} [_ to]]
|
||||
(when (get contacts to)
|
||||
(protocol/send-discoveries-response!
|
||||
{:web3 web3
|
||||
|
@ -99,7 +102,8 @@
|
|||
|
||||
(register-handler :discoveries-response-received
|
||||
(u/side-effect!
|
||||
(fn [{:keys [discoveries contacts]} [_ {:keys [payload from]}]]
|
||||
(fn [{:keys [discoveries]
|
||||
:contacts/keys [contacts]} [_ {:keys [payload from]}]]
|
||||
(when (get contacts from)
|
||||
(when-let [data (:data payload)]
|
||||
(doseq [{:keys [message-id] :as discover} data]
|
||||
|
|
|
@ -76,7 +76,7 @@
|
|||
(defview discover [current-view?]
|
||||
[show-search [:get-in [:toolbar-search :show]]
|
||||
search-text [:get-in [:toolbar-search :text]]
|
||||
contacts [:get :contacts]
|
||||
contacts [:get-contacts]
|
||||
current-account [:get-current-account]
|
||||
discoveries [:get-recent-discoveries]
|
||||
tabs-hidden? [:tabs-hidden?]]
|
||||
|
|
|
@ -2,7 +2,8 @@
|
|||
(:require [re-frame.core :refer [reg-sub]]
|
||||
[status-im.utils.datetime :as time]))
|
||||
|
||||
(defn- calculate-priority [{:keys [chats contacts current-public-key]}
|
||||
(defn- calculate-priority [{:keys [chats current-public-key]
|
||||
:contacts/keys [contacts]}
|
||||
{:keys [whisper-id created-at]}]
|
||||
(let [contact (get contacts whisper-id)
|
||||
chat (get chats whisper-id)
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
account-name :name
|
||||
:as current-account} :current-account}]
|
||||
[{contact-name :name
|
||||
contact-photo-path :photo-path} [:get-in [:contacts whisper-id]]]
|
||||
contact-photo-path :photo-path} [:get-in [:contacts/contacts whisper-id]]]
|
||||
(let [item-style (get-in platform-specific [:component-styles :discover :item])]
|
||||
[view
|
||||
[view st/popular-list-item
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
(reg-sub :selected-participant
|
||||
(fn [db]
|
||||
(let [identity (first (:selected-participants db))]
|
||||
(get-in db [:contacts identity]))))
|
||||
(get-in db [:contacts/contacts identity]))))
|
||||
|
||||
(defn get-chat-name-validation-messages [chat-name]
|
||||
(filter some?
|
||||
|
|
|
@ -1,17 +1,17 @@
|
|||
(ns status-im.handlers
|
||||
(:require
|
||||
[re-frame.core :refer [after dispatch dispatch-sync debug]]
|
||||
[re-frame.core :refer [after dispatch dispatch-sync debug reg-fx]]
|
||||
[status-im.db :refer [app-db]]
|
||||
[status-im.data-store.core :as data-store]
|
||||
[taoensso.timbre :as log]
|
||||
[status-im.utils.crypt :refer [gen-random-bytes]]
|
||||
[status-im.components.status :as status]
|
||||
[status-im.components.permissions :as permissions]
|
||||
[status-im.utils.handlers :refer [register-handler] :as u]
|
||||
[status-im.utils.handlers :refer [register-handler register-handler-fx] :as u]
|
||||
status-im.chat.handlers
|
||||
status-im.group-settings.handlers
|
||||
status-im.navigation.handlers
|
||||
status-im.contacts.handlers
|
||||
status-im.contacts.events
|
||||
status-im.discover.handlers
|
||||
status-im.new-group.handlers
|
||||
status-im.profile.handlers
|
||||
|
@ -44,17 +44,23 @@
|
|||
|
||||
(register-handler :set-in set-in)
|
||||
|
||||
(register-handler :initialize-db
|
||||
(fn [{:keys [status-module-initialized? status-node-started?
|
||||
network-status network first-run]} _]
|
||||
(data-store/init)
|
||||
(assoc app-db :current-account-id nil
|
||||
:contacts {}
|
||||
:network-status network-status
|
||||
:status-module-initialized? (or p/ios? js/goog.DEBUG status-module-initialized?)
|
||||
:status-node-started? status-node-started?
|
||||
:network (or network :testnet)
|
||||
:first-run (or (nil? first-run) first-run))))
|
||||
(reg-fx
|
||||
::init-store
|
||||
(fn []
|
||||
(data-store/init)))
|
||||
|
||||
(register-handler-fx :initialize-db
|
||||
(fn [{{:keys [status-module-initialized? status-node-started?
|
||||
network-status network first-run _]} :db} _]
|
||||
{::init-store nil
|
||||
:db (assoc app-db
|
||||
:current-account-id nil
|
||||
:contacts/contacts {}
|
||||
:network-status network-status
|
||||
:status-module-initialized? (or p/ios? js/goog.DEBUG status-module-initialized?)
|
||||
:status-node-started? status-node-started?
|
||||
:network (or network :testnet)
|
||||
:first-run (or (nil? first-run) first-run))}))
|
||||
|
||||
(register-handler :initialize-account-db
|
||||
(fn [db _]
|
||||
|
@ -62,7 +68,7 @@
|
|||
(assoc :current-chat-id console-chat-id)
|
||||
(dissoc :transactions
|
||||
:transactions-queue
|
||||
:new-contact-identity))))
|
||||
:contacts/new-identity))))
|
||||
|
||||
(register-handler :initialize-account
|
||||
(u/side-effect!
|
||||
|
|
|
@ -12,9 +12,9 @@
|
|||
splash-screen
|
||||
http-bridge]]
|
||||
[status-im.components.main-tabs :refer [main-tabs]]
|
||||
[status-im.contacts.views.contact-list :refer [contact-list]]
|
||||
[status-im.contacts.views.contact-list-modal :refer [contact-list-modal]]
|
||||
[status-im.contacts.views.new-contact :refer [new-contact]]
|
||||
[status-im.contacts.contact-list.views :refer [contact-list]]
|
||||
[status-im.contacts.contact-list-modal.views :refer [contact-list-modal]]
|
||||
[status-im.contacts.new-contact.views :refer [new-contact]]
|
||||
[status-im.qr-scanner.screen :refer [qr-scanner]]
|
||||
[status-im.discover.search-results :refer [discover-search-results]]
|
||||
[status-im.chat.screen :refer [chat]]
|
||||
|
|
|
@ -101,7 +101,7 @@
|
|||
(defn show-profile
|
||||
[db [_ identity]]
|
||||
(dispatch [:navigate-forget :profile])
|
||||
(assoc db :contact-identity identity))
|
||||
(assoc db :contacts/identity identity))
|
||||
|
||||
(register-handler :show-profile show-profile)
|
||||
|
||||
|
|
|
@ -53,7 +53,7 @@
|
|||
|
||||
(defview new-chat []
|
||||
[contacts [:all-added-group-contacts-filtered]
|
||||
params [:get :contacts-click-params]]
|
||||
params [:get :contacts/click-params]]
|
||||
[drawer-view
|
||||
[view st/contacts-list-container
|
||||
[new-chat-toolbar]
|
||||
|
|
|
@ -44,7 +44,8 @@
|
|||
(register-handler :select-contact select-contact)
|
||||
|
||||
(defn group-name-from-contacts
|
||||
[{:keys [contacts selected-contacts username]}]
|
||||
[{:keys [selected-contacts username]
|
||||
:contacts/keys [contacts]}]
|
||||
(->> (select-keys contacts selected-contacts)
|
||||
vals
|
||||
(map :name)
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
|
||||
(reg-sub :selected-contacts-count
|
||||
:<- [:get :selected-contacts]
|
||||
:<- [:get :contacts]
|
||||
:<- [:get-contacts]
|
||||
(fn [[selected-contacts contacts]]
|
||||
;TODO temporary, contact should be deleted from group after contact deletion from contacts
|
||||
(count (filter-selected-contacts selected-contacts contacts))))
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
(defview qr-code-view []
|
||||
[{:keys [photo-path address name] :as contact} [:get-in [:qr-modal :contact]]
|
||||
{:keys [qr-source amount? dimensions]} [:get :qr-modal]
|
||||
{:keys [amount]} [:get :contacts-click-params]]
|
||||
{:keys [amount]} [:get :contacts/click-params]]
|
||||
[view st/wallet-qr-code
|
||||
[status-bar {:type :modal}]
|
||||
[view st/account-toolbar
|
||||
|
|
|
@ -396,7 +396,7 @@
|
|||
|
||||
(register-handler :contact-request-received
|
||||
(u/side-effect!
|
||||
(fn [{:keys [contacts]} [_ {:keys [from payload]}]]
|
||||
(fn [{:contacts/keys [contacts]} [_ {:keys [from payload]}]]
|
||||
(when from
|
||||
(let [{{:keys [name profile-image address status]} :contact
|
||||
{:keys [public private]} :keypair} payload
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
(:require [cljs.spec.alpha :as s]
|
||||
[status-im.accounts.specs]
|
||||
[status-im.navigation.specs]
|
||||
[status-im.contacts.specs]
|
||||
[status-im.contacts.db]
|
||||
[status-im.qr-scanner.specs]
|
||||
[status-im.new-group.specs]
|
||||
[status-im.chat.specs]
|
||||
|
@ -11,119 +11,134 @@
|
|||
[status-im.transactions.specs]
|
||||
[status-im.discover.specs]))
|
||||
|
||||
;GLOBAL
|
||||
(s/def ::current-public-key (s/nilable string?)) ;;public key of current logged in account
|
||||
(s/def ::first-run (s/nilable boolean?)) ;;true when application running at first time
|
||||
;;;;GLOBAL
|
||||
|
||||
;;public key of current logged in account
|
||||
(s/def ::current-public-key (s/nilable string?))
|
||||
;;true when application running at first time
|
||||
(s/def ::first-run (s/nilable boolean?))
|
||||
(s/def ::was-modal? (s/nilable boolean?))
|
||||
(s/def ::rpc-url (s/nilable string?)) ;;"http://localhost:8545"
|
||||
(s/def ::web3 (s/nilable any?)) ;;object? doesn't work
|
||||
(s/def ::webview-bridge (s/nilable any?)) ;;object?
|
||||
;;"http://localhost:8545"
|
||||
(s/def ::rpc-url (s/nilable string?))
|
||||
;;object? doesn't work
|
||||
(s/def ::web3 (s/nilable any?))
|
||||
;;object?
|
||||
(s/def ::webview-bridge (s/nilable any?))
|
||||
(s/def ::status-module-initialized? (s/nilable boolean?))
|
||||
(s/def ::status-node-started? (s/nilable boolean?))
|
||||
(s/def ::toolbar-search (s/nilable map?))
|
||||
(s/def ::keyboard-height (s/nilable number?)) ;;height of native keyboard if shown
|
||||
;;height of native keyboard if shown
|
||||
(s/def ::keyboard-height (s/nilable number?))
|
||||
(s/def ::keyboard-max-height (s/nilable number?))
|
||||
(s/def ::orientation (s/nilable keyword?)) ;;:unknown - not used
|
||||
(s/def ::network-status (s/nilable keyword?)) ;;:online - presence of internet connection in the phone
|
||||
;NODE
|
||||
;;:unknown - not used
|
||||
(s/def ::orientation (s/nilable keyword?))
|
||||
;;:online - presence of internet connection in the phone
|
||||
(s/def ::network-status (s/nilable keyword?))
|
||||
|
||||
;;;;NODE
|
||||
|
||||
(s/def ::sync-listening-started (s/nilable boolean?))
|
||||
(s/def ::sync-state (s/nilable keyword?))
|
||||
(s/def ::sync-data (s/nilable map?))
|
||||
;NETWORK
|
||||
(s/def ::network (s/nilable keyword?)) ;;network name :testnet
|
||||
|
||||
(s/def ::db (allowed-keys :opt-un
|
||||
[::current-public-key
|
||||
::first-run
|
||||
::modal
|
||||
::was-modal?
|
||||
::rpc-url
|
||||
::web3
|
||||
::webview-bridge
|
||||
::status-module-initialized?
|
||||
::status-node-started?
|
||||
::toolbar-search
|
||||
::keyboard-height
|
||||
::keyboard-max-height
|
||||
::orientation
|
||||
::network-status
|
||||
::sync-listening-started
|
||||
::sync-state
|
||||
::sync-data
|
||||
::network
|
||||
:accounts/accounts
|
||||
:accounts/account-creation?
|
||||
:accounts/creating-account?
|
||||
:accounts/current-account-id
|
||||
:accounts/recover
|
||||
:accounts/login
|
||||
:navigation/view-id
|
||||
:navigation/navigation-stack
|
||||
:navigation/prev-tab-view-id
|
||||
:navigation/prev-view-id
|
||||
:contacts/contacts
|
||||
:contacts/new-contacts
|
||||
:contacts/new-contact-identity
|
||||
:contacts/new-contact-public-key-error
|
||||
:contacts/contact-identity
|
||||
:contacts/contacts-ui-props
|
||||
:contacts/contact-list-ui-props
|
||||
:contacts/contacts-click-handler
|
||||
:contacts/contacts-click-action
|
||||
:contacts/contacts-click-params
|
||||
:qr/qr-codes
|
||||
:qr/qr-modal
|
||||
:qr/current-qr-context
|
||||
:group/contact-groups
|
||||
:group/contact-group-id
|
||||
:group/group-type
|
||||
:group/new-group
|
||||
:group/new-groups
|
||||
:group/contacts-group
|
||||
:group/selected-contacts
|
||||
:group/groups-order
|
||||
:chat/chats
|
||||
:chat/current-chat-id
|
||||
:chat/chat-id
|
||||
:chat/new-chat
|
||||
:chat/new-chat-name
|
||||
:chat/chat-animations
|
||||
:chat/chat-ui-props
|
||||
:chat/chat-list-ui-props
|
||||
:chat/layout-height
|
||||
:chat/expandable-view-height-to-value
|
||||
:chat/global-commands
|
||||
:chat/loading-allowed
|
||||
:chat/message-data
|
||||
:chat/message-id->transaction-id
|
||||
:chat/message-status
|
||||
:chat/unviewed-messages
|
||||
:chat/selected-participants
|
||||
:chat/chat-loaded-callbacks
|
||||
:chat/commands-callbacks
|
||||
:chat/command-hash-valid?
|
||||
:chat/public-group-topic
|
||||
:chat/confirmation-code-sms-listener
|
||||
:chat/messages
|
||||
:chat/loaded-chats
|
||||
:chat/bot-subscriptions
|
||||
:chat/new-request
|
||||
:chat/raw-unviewed-messages
|
||||
:chat/bot-db
|
||||
:chat/geolocation
|
||||
:profile/profile-edit
|
||||
:transactions/transactions
|
||||
:transactions/transactions-queue
|
||||
:transactions/selected-transaction
|
||||
:transactions/confirm-transactions
|
||||
:transactions/confirmed-transactions-count
|
||||
:transactions/transactions-list-ui-props
|
||||
:transactions/transaction-details-ui-props
|
||||
:transactions/wrong-password-counter
|
||||
:transactions/wrong-password?
|
||||
:discoveries/discoveries
|
||||
:discoveries/discover-search-tags
|
||||
:discoveries/tags
|
||||
:discoveries/current-tag
|
||||
:discoveries/request-discoveries-timer
|
||||
:discoveries/new-discover]))
|
||||
;;;;NETWORK
|
||||
|
||||
;;network name :testnet
|
||||
(s/def ::network (s/nilable keyword?))
|
||||
|
||||
(s/def ::db (allowed-keys
|
||||
:opt
|
||||
[:contacts/contacts
|
||||
:contacts/new-identity
|
||||
:contacts/new-public-key-error
|
||||
:contacts/identity
|
||||
:contacts/ui-props
|
||||
:contacts/list-ui-props
|
||||
:contacts/click-handler
|
||||
:contacts/click-action
|
||||
:contacts/click-params]
|
||||
:opt-un
|
||||
[::current-public-key
|
||||
::first-run
|
||||
::modal
|
||||
::was-modal?
|
||||
::rpc-url
|
||||
::web3
|
||||
::webview-bridge
|
||||
::status-module-initialized?
|
||||
::status-node-started?
|
||||
::toolbar-search
|
||||
::keyboard-height
|
||||
::keyboard-max-height
|
||||
::orientation
|
||||
::network-status
|
||||
::sync-listening-started
|
||||
::sync-state
|
||||
::sync-data
|
||||
::network
|
||||
:accounts/accounts
|
||||
:accounts/account-creation?
|
||||
:accounts/creating-account?
|
||||
:accounts/current-account-id
|
||||
:accounts/recover
|
||||
:accounts/login
|
||||
:navigation/view-id
|
||||
:navigation/navigation-stack
|
||||
:navigation/prev-tab-view-id
|
||||
:navigation/prev-view-id
|
||||
:qr/qr-codes
|
||||
:qr/qr-modal
|
||||
:qr/current-qr-context
|
||||
:group/contact-groups
|
||||
:group/contact-group-id
|
||||
:group/group-type
|
||||
:group/new-group
|
||||
:group/new-groups
|
||||
:group/contacts-group
|
||||
:group/selected-contacts
|
||||
:group/groups-order
|
||||
:chat/chats
|
||||
:chat/current-chat-id
|
||||
:chat/chat-id
|
||||
:chat/new-chat
|
||||
:chat/new-chat-name
|
||||
:chat/chat-animations
|
||||
:chat/chat-ui-props
|
||||
:chat/chat-list-ui-props
|
||||
:chat/layout-height
|
||||
:chat/expandable-view-height-to-value
|
||||
:chat/global-commands
|
||||
:chat/loading-allowed
|
||||
:chat/message-data
|
||||
:chat/message-id->transaction-id
|
||||
:chat/message-status
|
||||
:chat/unviewed-messages
|
||||
:chat/selected-participants
|
||||
:chat/chat-loaded-callbacks
|
||||
:chat/commands-callbacks
|
||||
:chat/command-hash-valid?
|
||||
:chat/public-group-topic
|
||||
:chat/confirmation-code-sms-listener
|
||||
:chat/messages
|
||||
:chat/loaded-chats
|
||||
:chat/bot-subscriptions
|
||||
:chat/new-request
|
||||
:chat/raw-unviewed-messages
|
||||
:chat/bot-db
|
||||
:chat/geolocation
|
||||
:profile/profile-edit
|
||||
:transactions/transactions
|
||||
:transactions/transactions-queue
|
||||
:transactions/selected-transaction
|
||||
:transactions/confirm-transactions
|
||||
:transactions/confirmed-transactions-count
|
||||
:transactions/transactions-list-ui-props
|
||||
:transactions/transaction-details-ui-props
|
||||
:transactions/wrong-password-counter
|
||||
:transactions/wrong-password?
|
||||
:discoveries/discoveries
|
||||
:discoveries/discover-search-tags
|
||||
:discoveries/tags
|
||||
:discoveries/current-tag
|
||||
:discoveries/request-discoveries-timer
|
||||
:discoveries/new-discover]))
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
|
||||
(reg-sub :get
|
||||
(fn [db [_ k]]
|
||||
(k db)))
|
||||
(get db k)))
|
||||
|
||||
(reg-sub :get-current-account
|
||||
(fn [db]
|
||||
|
@ -32,7 +32,7 @@
|
|||
(reg-sub :tabs-hidden?
|
||||
:<- [:get-in [:toolbar-search :show]]
|
||||
:<- [:get-in [:chat-list-ui-props :edit?]]
|
||||
:<- [:get-in [:contacts-ui-props :edit?]]
|
||||
:<- [:get-in [:contacts/ui-props :edit?]]
|
||||
:<- [:get :view-id]
|
||||
(fn [[search-mode? chats-edit-mode? contacts-edit-mode? view-id]]
|
||||
(or search-mode?
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
(into {} (map (fn [[_ {:keys [address] :as contact}]]
|
||||
(when address
|
||||
[address contact]))
|
||||
(:contacts db)))))
|
||||
(:contacts/contacts db)))))
|
||||
|
||||
(reg-sub :contact-by-address
|
||||
:<- [:contacts-by-address]
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
(ns status-im.utils.handlers
|
||||
(:require [re-frame.core :refer [reg-event-db]]
|
||||
(:require [re-frame.core :refer [reg-event-db reg-event-fx]]
|
||||
[re-frame.interceptor :refer [->interceptor get-coeffect]]
|
||||
[clojure.string :as str]
|
||||
[taoensso.timbre :as log]
|
||||
|
@ -26,7 +26,7 @@
|
|||
"throw an exception if db doesn't match the spec"
|
||||
(->interceptor
|
||||
:id check-spec
|
||||
:before
|
||||
:after
|
||||
(fn check-handler
|
||||
[context]
|
||||
(let [new-db (get-coeffect context :db)
|
||||
|
@ -40,6 +40,16 @@
|
|||
([name middleware handler]
|
||||
(reg-event-db name [debug-handlers-names (when js/goog.DEBUG check-spec) middleware] handler)))
|
||||
|
||||
(defn register-handler-db
|
||||
([name handler] (register-handler-db name nil handler))
|
||||
([name interceptors handler]
|
||||
(reg-event-db name [debug-handlers-names (when js/goog.DEBUG check-spec) interceptors] handler)))
|
||||
|
||||
(defn register-handler-fx
|
||||
([name handler] (register-handler-fx name nil handler))
|
||||
([name interceptors handler]
|
||||
(reg-event-fx name [debug-handlers-names (when js/goog.DEBUG check-spec) interceptors] handler)))
|
||||
|
||||
(defn get-hashtags [status]
|
||||
(if status
|
||||
(let [hashtags (map #(str/lower-case (subs % 1))
|
||||
|
|
|
@ -1,14 +0,0 @@
|
|||
(ns reagent.core)
|
||||
|
||||
(defn create-element [& args])
|
||||
|
||||
(defn adapt-react-class [& args])
|
||||
(defn atom [& args])
|
||||
(defn as-element [& args])
|
||||
(defn create-class [& args])
|
||||
(defn set-state [& args])
|
||||
(defn state [& args])
|
||||
(defn after-render [& args])
|
||||
(defn props [& args])
|
||||
(defn current-component [& args])
|
||||
(defn reactify-component [& args])
|
|
@ -2,14 +2,14 @@
|
|||
|
||||
(def action-button #js {})
|
||||
(def android-sms-listener #js {})
|
||||
(def autolink #js {})
|
||||
(def autolink #js {:default #js {}})
|
||||
(def camera #js {:constants #js {}})
|
||||
(def circle-checkbox #js {})
|
||||
(def contacts #js {})
|
||||
(def dialogs #js {})
|
||||
(def dismiss-keyboard #js {})
|
||||
(def drawer #js {})
|
||||
(def emoji-picker #js {})
|
||||
(def emoji-picker #js {:default #js {}})
|
||||
(def fs #js {})
|
||||
(def http-bridge #js {})
|
||||
(def i18n #js {})
|
||||
|
@ -26,13 +26,14 @@
|
|||
(def random-bytes #js {})
|
||||
(def react-native
|
||||
#js {:NativeModules #js {}
|
||||
:Animated #js {}
|
||||
:Animated #js {:View #js {}
|
||||
:Text #js {}}
|
||||
:DeviceEventEmitter #js {:addListener (fn [])}})
|
||||
(def realm #js {:schemaVersion (fn [])
|
||||
:close (fn [])})
|
||||
(def sortable-listview #js {})
|
||||
(def swiper #js {})
|
||||
(def vector-icons #js {})
|
||||
(def webview-bridge #js {})
|
||||
(def webview-bridge #js {:default #js {}})
|
||||
|
||||
|
||||
|
|
|
@ -0,0 +1,193 @@
|
|||
(ns status-im.test.contacts.handlers
|
||||
(:require [cljs.test :refer-macros [deftest is]]
|
||||
reagent.core
|
||||
[re-frame.core :as rf]
|
||||
[day8.re-frame.test :refer-macros [run-test-sync]]
|
||||
status-im.specs
|
||||
status-im.db
|
||||
[status-im.contacts.events :as e]
|
||||
[status-im.handlers :as h]
|
||||
status-im.subs))
|
||||
|
||||
(def browse-contact-from-realm-db
|
||||
{:last-updated 0
|
||||
:address nil
|
||||
:name "Browse"
|
||||
|
||||
:global-command
|
||||
{
|
||||
:description "Launch the browser"
|
||||
:sequential-params false
|
||||
:color "#ffa500"
|
||||
:name "global"
|
||||
:params
|
||||
{
|
||||
:0
|
||||
{
|
||||
:name "url"
|
||||
:type "text"
|
||||
:placeholder "URL"}}
|
||||
:icon nil
|
||||
:title "Browser"
|
||||
:has-handler false
|
||||
:fullscreen true
|
||||
:suggestions-trigger "on-change"}
|
||||
:dapp-url nil
|
||||
:dapp-hash nil
|
||||
|
||||
:commands
|
||||
{
|
||||
:location
|
||||
{
|
||||
:description "Share your location"
|
||||
:sequential-params true
|
||||
:color nil
|
||||
:name "location"
|
||||
|
||||
:params
|
||||
{
|
||||
:0
|
||||
{
|
||||
:name "address"
|
||||
:type "text"
|
||||
:placeholder "address"}}
|
||||
:icon nil
|
||||
:title "Location"
|
||||
:has-handler false
|
||||
:fullscreen true
|
||||
:owner-id "browse"
|
||||
:suggestions-trigger "on-change"}}
|
||||
:photo-path nil
|
||||
:debug? false
|
||||
:status nil
|
||||
:bot-url "local://browse-bot"
|
||||
:responses {}
|
||||
:pending? false
|
||||
:whisper-identity "browse"
|
||||
:last-online 0
|
||||
:dapp? true
|
||||
:unremovable? true
|
||||
:private-key nil
|
||||
:public-key nil})
|
||||
|
||||
(def browse-global-commands
|
||||
{:browse
|
||||
{
|
||||
:description "Launch the browser"
|
||||
:bot "browse"
|
||||
:color "#ffa500"
|
||||
:name "global"
|
||||
:params
|
||||
[
|
||||
{
|
||||
:name "url"
|
||||
:placeholder "URL"
|
||||
:type "text"}]
|
||||
:type :command
|
||||
:title "Browser"
|
||||
:sequential-params false
|
||||
:icon nil
|
||||
:has-handler false
|
||||
:fullscreen true
|
||||
:suggestions-trigger "on-change"}})
|
||||
|
||||
#_{:registered-only true
|
||||
:has-handler false
|
||||
:fullscreen true
|
||||
:hidden? nil}
|
||||
|
||||
(def browse-contatcs
|
||||
{
|
||||
"browse"
|
||||
{
|
||||
:last-updated 0
|
||||
:address nil
|
||||
:name "Browse"
|
||||
|
||||
:global-command
|
||||
{
|
||||
:description "Launch the browser"
|
||||
:sequential-params false
|
||||
:color "#ffa500"
|
||||
:name "global"
|
||||
|
||||
:params
|
||||
{
|
||||
|
||||
:0
|
||||
{
|
||||
:name "url"
|
||||
:type "text"
|
||||
:placeholder "URL"}}
|
||||
:icon nil
|
||||
:title "Browser"
|
||||
:has-handler false
|
||||
:fullscreen true
|
||||
:suggestions-trigger "on-change"}
|
||||
:commands-loaded? true
|
||||
:dapp-url nil
|
||||
:dapp-hash nil
|
||||
:subscriptions {}
|
||||
|
||||
:commands
|
||||
{
|
||||
|
||||
:location
|
||||
{
|
||||
:description "Share your location"
|
||||
:bot "mailman"
|
||||
:sequential-params true
|
||||
:hide-send-button true
|
||||
:name "location"
|
||||
|
||||
:params
|
||||
[
|
||||
|
||||
{
|
||||
:name "address"
|
||||
:placeholder "address"
|
||||
:type "text"}]
|
||||
:type :command
|
||||
:title "Location"
|
||||
:has-handler false
|
||||
:hidden? nil
|
||||
:owner-id "mailman"}}}
|
||||
:photo-path nil
|
||||
:debug? false
|
||||
:status nil
|
||||
:bot-url "local://browse-bot"
|
||||
:responses {}
|
||||
:pending? false
|
||||
:whisper-identity "browse"
|
||||
:last-online 0
|
||||
:dapp? true
|
||||
:unremovable? true
|
||||
:private-key nil
|
||||
:public-key nil})
|
||||
|
||||
(defn test-fixtures
|
||||
[]
|
||||
(rf/reg-cofx
|
||||
::e/get-all-contacts
|
||||
(fn [coeffects _]
|
||||
(assoc coeffects :all-contacts [browse-contact-from-realm-db])))
|
||||
(rf/reg-fx
|
||||
::h/init-store
|
||||
(fn []
|
||||
nil)))
|
||||
|
||||
(deftest basic-sync
|
||||
(run-test-sync
|
||||
(test-fixtures)
|
||||
(rf/dispatch [:initialize-db])
|
||||
(let [contacts (rf/subscribe [:get-contacts])
|
||||
global-commands (rf/subscribe [:get :global-commands])]
|
||||
|
||||
;;Assert the initial state
|
||||
(is (and (map? @contacts) (empty? @contacts)))
|
||||
(is (nil? @global-commands))
|
||||
|
||||
(rf/dispatch [:load-contacts])
|
||||
|
||||
(is (= {"browse" browse-contact-from-realm-db} @contacts))
|
||||
(is (= browse-global-commands @global-commands)))))
|
|
@ -1,25 +0,0 @@
|
|||
(ns status-im.test.handlers-stubs
|
||||
(:require [re-frame.core :refer [subscribe dispatch after]]
|
||||
[status-im.utils.handlers :refer [register-handler]]
|
||||
[status-im.utils.handlers :as u]
|
||||
[status-im.chat.sign-up :as sign-up-service]
|
||||
status-im.handlers))
|
||||
|
||||
(defn init-stubs []
|
||||
(register-handler :sign-up
|
||||
(u/side-effect!
|
||||
(fn []
|
||||
;; todo save phone number to db
|
||||
(sign-up-service/on-sign-up-response))))
|
||||
|
||||
(register-handler :sign-up-confirm
|
||||
(u/side-effect!
|
||||
(fn [_ [_ confirmation-code]]
|
||||
(sign-up-service/on-send-code-response
|
||||
(if (= "1234" confirmation-code)
|
||||
{:message "Done!"
|
||||
:status :confirmed
|
||||
:confirmed true}
|
||||
{:message "Wrong code!"
|
||||
:status :failed
|
||||
:confirmed false}))))))
|
|
@ -1,5 +1,6 @@
|
|||
(ns status-im.test.runner
|
||||
(:require [doo.runner :refer-macros [doo-tests]]
|
||||
[status-im.test.contacts.handlers]
|
||||
[status-im.test.chat.models.input]
|
||||
[status-im.test.handlers]
|
||||
[status-im.test.utils.utils]))
|
||||
|
@ -12,6 +13,7 @@
|
|||
|
||||
(set! goog.DEBUG false)
|
||||
|
||||
(doo-tests 'status-im.test.chat.models.input
|
||||
(doo-tests 'status-im.test.contacts.handlers
|
||||
'status-im.test.chat.models.input
|
||||
'status-im.test.handlers
|
||||
'status-im.test.utils.utils)
|
||||
|
|
Loading…
Reference in New Issue