diff --git a/src/status_im/contacts/events.cljs b/src/status_im/contacts/events.cljs index afaf172a1c..7f445a708a 100644 --- a/src/status_im/contacts/events.cljs +++ b/src/status_im/contacts/events.cljs @@ -12,6 +12,7 @@ [cljs.reader :refer [read-string]] [status-im.utils.js-resources :as js-res] [status-im.react-native.js-dependencies :as rn-dependencies] + [status-im.js-dependencies :as dependencies] [status-im.utils.identicon :refer [identicon]] [status-im.utils.gfycat.core :refer [generate-gfy]] [status-im.i18n :refer [label]] @@ -174,29 +175,17 @@ {: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- 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- public-key->address [public-key] (let [length (count public-key) normalized-key (case length @@ -205,7 +194,7 @@ 128 public-key nil)] (when normalized-key - (subs (.sha3 js/Web3.prototype normalized-key #js {:encoding "hex"}) 26)))) + (subs (.sha3 dependencies/Web3.prototype normalized-key #js {:encoding "hex"}) 26)))) (defn- prepare-default-groups-events [groups default-groups] [[:add-contact-groups @@ -264,26 +253,31 @@ :load-default-contacts! [(inject-cofx ::get-default-contacts-and-groups)] (fn [{:keys [db default-contacts default-groups]} _] - (let [{:keys [groups] :contacts/keys [contacts]} db] + (let [{:contacts/keys [contacts] :group/keys [contact-groups]} db] {:dispatch-n (concat - (prepare-default-groups-events groups default-groups) + (prepare-default-groups-events contact-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 + :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 :add-contacts @@ -301,12 +295,18 @@ [(keyword n) (assoc global-command :type :command :bot n)]))) - (into {})) - new-contacts-vals (vals new-contacts')] + (into {}))] {:db (-> db (update :global-commands merge global-commands) (update :contacts/contacts merge new-contacts')) - ::save-contacts! new-contacts-vals}))) + ::save-contacts! (vals new-contacts')}))) + +(register-handler-db + :remove-contacts-click-handler + (fn [db _] + (dissoc db + :contacts/click-handler + :contacts/click-action))) (register-handler-fx ::send-contact-request @@ -321,11 +321,11 @@ (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})) + {: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 @@ -337,16 +337,16 @@ (register-handler-fx :add-pending-contact - (fn [{:keys [db]} [_ chat-id]] + (fn [{:keys [db]} [_ chat-or-whisper-id]] (let [{:keys [chats] :contacts/keys [contacts]} db - contact (if-let [contact-info (get-in chats [chat-id :contact-info])] + contact (if-let [contact-info (get-in chats [chat-or-whisper-id :contact-info])] (read-string contact-info) - (get contacts chat-id)) - contact' (assoc contact :address (public-key->address chat-id) + (get contacts chat-or-whisper-id)) + contact' (assoc contact :address (public-key->address chat-or-whisper-id) :pending? false)] {:dispatch-n [[::add-new-contact contact'] [:watch-contact contact'] - [:discoveries-send-portions chat-id]]}))) + [:discoveries-send-portions chat-or-whisper-id]]}))) (register-handler-db :set-contact-identity-from-qr @@ -416,6 +416,7 @@ group' (update (contact-groups group-id) :contacts (remove-contact-from-group whisper-identity))] {:dispatch [:update-contact-group group']}))) +;;used only by status-dev-cli (register-handler-fx :remove-contact (fn [{:keys [db]} [_ whisper-identity pred]] diff --git a/src/status_im/utils/handlers.cljs b/src/status_im/utils/handlers.cljs index 2580b1e547..743543f4e4 100644 --- a/src/status_im/utils/handlers.cljs +++ b/src/status_im/utils/handlers.cljs @@ -1,6 +1,6 @@ (ns status-im.utils.handlers (:require [re-frame.core :refer [reg-event-db reg-event-fx]] - [re-frame.interceptor :refer [->interceptor get-coeffect]] + [re-frame.interceptor :refer [->interceptor get-coeffect get-effect]] [clojure.string :as str] [taoensso.timbre :as log] [cljs.spec.alpha :as s]) @@ -29,9 +29,9 @@ :after (fn check-handler [context] - (let [new-db (get-coeffect context :db) + (let [new-db (get-effect context :db) v (get-coeffect context :event)] - (when-not (s/valid? :status-im.specs/db new-db) + (when (and new-db (not (s/valid? :status-im.specs/db new-db))) (throw (ex-info (str "spec check failed on: " (first v) "\n " (s/explain-str :status-im.specs/db new-db)) {}))) context)))) diff --git a/test/cljs/status_im/test/contacts/handlers.cljs b/test/cljs/status_im/test/contacts/handlers.cljs index 7007b41c43..1c57e9016c 100644 --- a/test/cljs/status_im/test/contacts/handlers.cljs +++ b/test/cljs/status_im/test/contacts/handlers.cljs @@ -1,13 +1,16 @@ (ns status-im.test.contacts.handlers - (:require [cljs.test :refer-macros [deftest is]] + (:require [cljs.test :refer-macros [deftest is testing]] 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)) + [status-im.contacts.events :as contacts-events] + [status-im.group.events :as group-events] + [status-im.handlers :as handlers] + status-im.subs + [status-im.utils.js-resources :as js-res] + [status-im.utils.datetime :as datetime])) (def browse-contact-from-realm-db {:last-updated 0 @@ -91,103 +94,212 @@ :fullscreen true :suggestions-trigger "on-change"}}) -#_{:registered-only true - :has-handler false - :fullscreen true - :hidden? nil} +(def dapps-contact-group + {:group-id "dapps" + :name "ÐApps" + :order 0 + :timestamp 0 + :contacts [{:identity "wallet"} + {:identity "oaken-water-meter"} + {:identity "melonport"} + {:identity "bchat"} + {:identity "Dentacoin"} + {:identity "Augur"} + {:identity "Ethlance"} + {:identity "Commiteth"}] + :pending? false}) -(def browse-contatcs - { - "browse" - { - :last-updated 0 - :address nil - :name "Browse" +(def wallet-contact + {:address nil + :name "Wallet" + :global-command nil + :dapp-url "https://status.im/dapps/wallet/" + :dapp-hash nil + :photo-path "icon_wallet_avatar" + :bot-url nil + :pending? false + :whisper-identity "wallet" + :dapp? true + :unremovable? true + :public-key nil}) - :global-command - { - :description "Launch the browser" - :sequential-params false - :color "#ffa500" - :name "global" +(def contacts-browse-wallet + {"browse" browse-contact-from-realm-db + "wallet" wallet-contact}) - :params - { +(defn test-fixtures [] + (rf/reg-fx ::handlers/init-store #()) - :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 {} + (rf/reg-fx ::contacts-events/save-contacts! #()) + (rf/reg-fx ::contacts-events/save-contact #()) + (rf/reg-fx ::contacts-events/watch-contact #()) + (rf/reg-fx ::contacts-events/stop-watching-contact #()) + (rf/reg-fx ::contacts-events/send-contact-request-fx #()) - :commands - { + (rf/reg-fx ::group-events/save-contact-groups #()) + (rf/reg-fx ::group-events/add-contacts-to-contact-group #()) - :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 + ::contacts-events/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 + ;;TODO implement tests later for :add-chat? and :bot-url + (rf/reg-cofx + ::contacts-events/get-default-contacts-and-groups + (fn [coeffects _] + (assoc coeffects :default-contacts (update (select-keys js-res/default-contacts [:wallet]) + :wallet + dissoc :add-chat? :bot-url) + :default-groups (select-keys js-res/default-contact-groups [:dapps]))))) + +(deftest contacts-events + "load-contacts + load-default-contacts (add-contact-groups, add-contacts, add-contacts-to-group ;TODO add-chat, load-commands!) + add-contact-handler (add-new-contact-and-open-chat, status-im.contacts.events/add-new-contact, + status-im.contacts.events/send-contact-request ;TODO start-chat) + contact-request-received (update-contact, watch-contact ;TODO :update-chat!) + contact-update-received (update-contact ;TODO :update-chat!) + hide-contact (update-contact ;TODO :account-update-keys) + add-contact-handler (add-pending-contact, status-im.contacts.events/add-new-contact + status-im.contacts.events/send-contact-request ;TODO :discoveries-send-portions)" + (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)) + (let [contacts (rf/subscribe [:get-contacts]) + global-commands (rf/subscribe [:get :global-commands]) + contact-groups (rf/subscribe [:get-contact-groups])] - (rf/dispatch [:load-contacts]) + (testing ":load-contacts event" - (is (= {"browse" browse-contact-from-realm-db} @contacts)) - (is (= browse-global-commands @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))) + + (testing ":load-default-contacts! event" + + ;;Assert the initial state + (is (and (map? @contact-groups) (empty? @contact-groups))) + + ;; :load-default-contacts! event dispatches next 5 events + ;; + ;; :add-contact-groups + ;; :add-contacts + ;; :add-contacts-to-group + ;;TODO :add-chat + ;;TODO :load-commands! + (rf/dispatch [:load-default-contacts!]) + + (is (= {"dapps" dapps-contact-group} (update @contact-groups "dapps" assoc :timestamp 0))) + + (is (= contacts-browse-wallet + @contacts))) + + (let [new-contact-public-key "0x048f7d5d4bda298447bbb5b021a34832509bd1a8dbe4e06f9b7223d00a59b6dc14f6e142b21d3220ceb3155a6d8f40ec115cd96394d3cc7c55055b433a1758dc74" + new-contact-address "5392ccb49f2e9fef8b8068b3e3b5ba6c020a9aca" + new-contact {:name "" + :photo-path "" + :whisper-identity new-contact-public-key + :address new-contact-address} + contact (rf/subscribe [:contact-by-identity new-contact-public-key])] + + (testing ":add-contact-handler event - new contact" + + ;; :add-contact-handler event dispatches next 4 events for new contact + ;; + ;; :add-new-contact-and-open-chat + ;; :status-im.contacts.events/add-new-contact + ;; :status-im.contacts.events/send-contact-request + ;;TODO :start-chat + (rf/reg-event-db :start-chat (fn [db _] db)) + + (rf/dispatch [:add-contact-handler new-contact-public-key]) + + (is (= new-contact (assoc @contact :photo-path "" :name ""))) + + (is (= (assoc contacts-browse-wallet new-contact-public-key new-contact) + (update @contacts new-contact-public-key assoc :photo-path "" :name "")))) + + (testing ":contact-request-received event" + + ;; :contact-request-received event dispatches next 3 events + ;; + ;; :update-contact! + ;; :watch-contact + ;;TODO :update-chat! + (rf/reg-event-db :update-chat! (fn [db _] db)) + + (let [recieved-contact {:name "test" + :profile-image "" + :address new-contact-address + :status "test status"} + recieved-contact' (merge new-contact + (dissoc recieved-contact :profile-image) + {:public-key "" :private-key ""})] + + (rf/dispatch [:contact-request-received {:from new-contact-public-key + :payload {:contact recieved-contact + :keypair {:public "" + :private ""}}}]) + + (is (= (assoc contacts-browse-wallet new-contact-public-key recieved-contact') + @contacts)) + + (testing ":contact-update-received event" + + ;; :contact-update-received event dispatches next 2 events + ;; + ;; :update-contact! + ;;TODO :update-chat! + (let [timestamp (datetime/now-ms) + recieved-contact'' (assoc recieved-contact' :last-updated timestamp + :status "new status" + :name "new name")] + + (rf/dispatch [:contact-update-received {:from new-contact-public-key + :payload {:content {:profile {:profile-image "" + :status "new status" + :name "new name"}} + :timestamp timestamp}}]) + + (is (= (assoc contacts-browse-wallet new-contact-public-key recieved-contact'') + @contacts)) + + (testing ":hide-contact event" + + ;; :hide-contact event dispatches next 2 events + ;; + ;; :update-contact! + ;;TODO :account-update-keys + (rf/reg-event-db :account-update-keys (fn [db _] db)) + + (rf/dispatch [:hide-contact @contact]) + + (is (= (assoc contacts-browse-wallet new-contact-public-key (assoc recieved-contact'' + :pending? true)) + @contacts))) + + (testing ":add-contact-handler event - :add-pending-contact" + + ;; :add-contact-handler event dispatches next 4 events + ;; + ;; :add-pending-contact + ;; :status-im.contacts.events/add-new-contact + ;; :status-im.contacts.events/send-contact-request + ;;TODO :discoveries-send-portions + (rf/reg-event-db :discoveries-send-portions (fn [db _] db)) + + (rf/dispatch [:add-contact-handler new-contact-public-key]) + + (is (= (assoc contacts-browse-wallet new-contact-public-key (assoc recieved-contact'' + :pending? false)) + @contacts))))))))))) \ No newline at end of file