From 1e4311d4cba0567aaf912eaba94a512dbd1216f6 Mon Sep 17 00:00:00 2001 From: Andrea Maria Piana Date: Wed, 30 May 2018 14:11:30 +0200 Subject: [PATCH] Allow editing existing mailservers Signed-off-by: Andrea Maria Piana --- src/status_im/models/wnode.cljs | 22 ++++ .../edit_mailserver/events.cljs | 63 +++++++---- .../edit_mailserver/styles.cljs | 31 +++++- .../edit_mailserver/views.cljs | 71 +++++++----- .../offline_messaging_settings/views.cljs | 25 +++-- .../offline_messaging_settings/events.cljs | 102 +++++++++++++++++- 6 files changed, 246 insertions(+), 68 deletions(-) create mode 100644 src/status_im/models/wnode.cljs diff --git a/src/status_im/models/wnode.cljs b/src/status_im/models/wnode.cljs new file mode 100644 index 0000000000..634c1a25ad --- /dev/null +++ b/src/status_im/models/wnode.cljs @@ -0,0 +1,22 @@ +(ns status-im.models.wnode + (:require + [clojure.string :as string] + [status-im.utils.ethereum.core :as ethereum])) + +(defn- extract-address-components [address] + (rest (re-matches #"enode://(.*)@(.*)" address))) + +(defn- get-chain [db] + (let [network (get (:networks (:account/account db)) (:network db))] + (ethereum/network->chain-keyword network))) + +(defn get-wnode [wnode-id {:keys [db]}] + (get-in db [:inbox/wnodes (get-chain db) wnode-id])) + +(defn current-wnode? [wnode-id {:keys [db]}] + (let [current-wnode-id (get-in db [:account/account :settings :wnode (get-chain db)])] + (= current-wnode-id wnode-id))) + +(defn build-url [address password] + (let [[initial host] (extract-address-components address)] + (str "enode://" initial ":" password "@" host))) diff --git a/src/status_im/ui/screens/offline_messaging_settings/edit_mailserver/events.cljs b/src/status_im/ui/screens/offline_messaging_settings/edit_mailserver/events.cljs index 1cc8579af3..f4f3c0f5ee 100644 --- a/src/status_im/ui/screens/offline_messaging_settings/edit_mailserver/events.cljs +++ b/src/status_im/ui/screens/offline_messaging_settings/edit_mailserver/events.cljs @@ -1,6 +1,7 @@ (ns status-im.ui.screens.offline-messaging-settings.edit-mailserver.events (:require [clojure.string :as string] [re-frame.core :as re-frame] + [status-im.models.wnode :as models.wnode] [status-im.utils.handlers :refer [register-handler] :as handlers] [status-im.utils.handlers-macro :as handlers-macro] [status-im.ui.screens.accounts.utils :as accounts.utils] @@ -9,38 +10,57 @@ [status-im.utils.inbox :as utils.inbox] [status-im.data-store.mailservers :as data-store.mailservers])) -(defn- new-mailserver [id mailserver-name address] +(defn- build-mailserver [id mailserver-name address] (assoc (utils.inbox/address->mailserver address) :id (string/replace id "-" "") :name mailserver-name)) -(defn save-new-mailserver [{{:mailservers/keys [manage] :account/keys [account] :as db} :db :as cofx} _] - (let [{:keys [name url]} manage - network (get (:networks (:account/account db)) (:network db)) - chain (ethereum/network->chain-keyword network) - mailserver (new-mailserver - (string/replace (:random-id cofx) "-" "") - (:value name) - (:value url))] +(defn upsert-mailserver [{{:mailservers/keys [manage] :account/keys [account] :as db} :db :as cofx} _] + (let [{:keys [name url id]} manage + network (get (:networks (:account/account db)) (:network db)) + chain (ethereum/network->chain-keyword network) + mailserver (build-mailserver + (or (:value id) + (string/replace (:random-id cofx) "-" "")) + (:value name) + (:value url)) + connected-to-wnode? (models.wnode/current-wnode? (:id mailserver) cofx)] {:db (-> db (dissoc :mailservers/manage) (assoc-in [:inbox/wnodes chain (:id mailserver)] mailserver)) - :data-store/tx [(data-store.mailservers/save-mailserver-tx (assoc - mailserver - :chain - chain))] + :data-store/tx [{:transaction + (data-store.mailservers/save-mailserver-tx (assoc + mailserver + :chain + chain)) + ;; we naively logout if the user is connected to the edited mailserver + :success-event (when connected-to-wnode? [:logout])}] :dispatch [:navigate-back]})) (defn set-input [input-key value {:keys [db]}] {:db (update db :mailservers/manage assoc input-key {:value value - :error (if (= input-key :name) - (string/blank? value) - (not (utils.inbox/valid-enode-address? value)))})}) + :error (case input-key + :id false + :name (string/blank? value) + :url (not (utils.inbox/valid-enode-address? value)))})}) + +(defn edit-mailserver [wnode-id {:keys [db] :as cofx}] + (let [{:keys [id + address + password + name]} (models.wnode/get-wnode wnode-id cofx) + url (when address (models.wnode/build-url address password)) + fxs (handlers-macro/merge-fx + cofx + (set-input :id id) + (set-input :url (str url)) + (set-input :name (str name)))] + (assoc fxs :dispatch [:navigate-to :edit-mailserver]))) (handlers/register-handler-fx - :save-new-mailserver + :upsert-mailserver [(re-frame/inject-cofx :random-id)] - save-new-mailserver) + upsert-mailserver) (handlers/register-handler-fx :mailserver-set-input @@ -49,11 +69,8 @@ (handlers/register-handler-fx :edit-mailserver - (fn [{db :db} _] - {:db (update-in db [:mailservers/manage] assoc - :name {:error true} - :url {:error true}) - :dispatch [:navigate-to :edit-mailserver]})) + (fn [cofx [_ wnode-id]] + (edit-mailserver wnode-id cofx))) (handlers/register-handler-fx :set-mailserver-from-qr diff --git a/src/status_im/ui/screens/offline_messaging_settings/edit_mailserver/styles.cljs b/src/status_im/ui/screens/offline_messaging_settings/edit_mailserver/styles.cljs index d82ff140f3..c4c4248b46 100644 --- a/src/status_im/ui/screens/offline_messaging_settings/edit_mailserver/styles.cljs +++ b/src/status_im/ui/screens/offline_messaging_settings/edit_mailserver/styles.cljs @@ -1,6 +1,7 @@ (ns status-im.ui.screens.offline-messaging-settings.edit-mailserver.styles (:require-macros [status-im.utils.styles :refer [defstyle]]) - (:require [status-im.ui.components.styles :as styles])) + (:require [status-im.ui.components.styles :as styles] + [status-im.ui.components.colors :as colors])) (def edit-mailserver-view {:flex 1 @@ -28,3 +29,31 @@ {:flex-direction :row :margin-horizontal 12 :margin-vertical 15}) + +(def connect-button-container + {:margin-top 8 + :margin-bottom 16 + :margin-horizontal 16}) + +(defstyle connect-button + {:height 52 + :align-items :center + :justify-content :center + :background-color colors/blue + :border-radius 8 + :ios {:opacity 0.9}}) + +(defstyle connect-button-label + {:color colors/white + :ios {:font-size 17 + :letter-spacing -0.2} + :android {:font-size 14}}) + +(defstyle connect-button-description + {:color colors/gray + :ios {:margin-top 8 + :height 20 + :font-size 14 + :letter-spacing -0.2} + :android {:margin-top 12 + :font-size 12}}) diff --git a/src/status_im/ui/screens/offline_messaging_settings/edit_mailserver/views.cljs b/src/status_im/ui/screens/offline_messaging_settings/edit_mailserver/views.cljs index 8122df026e..891f59aa13 100644 --- a/src/status_im/ui/screens/offline_messaging_settings/edit_mailserver/views.cljs +++ b/src/status_im/ui/screens/offline_messaging_settings/edit_mailserver/views.cljs @@ -14,6 +14,15 @@ [status-im.ui.components.text-input.view :as text-input] [status-im.ui.screens.offline-messaging-settings.edit-mailserver.styles :as styles])) +(defn connect-button [id] + [react/touchable-highlight {:on-press #(re-frame/dispatch [:connect-wnode id])} + [react/view styles/connect-button-container + [react/view {:style styles/connect-button + :accessibility-label :mailserver-connect-button} + [react/text {:style styles/connect-button-label + :uppercase? true} + (i18n/label :t/connect)]]]]) + (def qr-code [react/touchable-highlight {:on-press #(re-frame/dispatch [:scan-qr-code {:toolbar-title (i18n/label :t/add-mailserver)} @@ -25,32 +34,36 @@ (views/defview edit-mailserver [] (views/letsubs [manage-mailserver [:get-manage-mailserver] is-valid? [:manage-mailserver-valid?]] - [react/view components.styles/flex - [status-bar/status-bar] - [react/keyboard-avoiding-view components.styles/flex - [toolbar/simple-toolbar (i18n/label :t/add-mailserver)] - [react/scroll-view - [react/view styles/edit-mailserver-view - [text-input/text-input-with-label - {:label (i18n/label :t/name) - :placeholder (i18n/label :t/specify-name) - :style styles/input - :container styles/input-container - :default-value (get-in manage-mailserver [:name :value]) - :on-change-text #(re-frame/dispatch [:mailserver-set-input :name %]) - :auto-focus true}] - [text-input/text-input-with-label - {:label (i18n/label :t/mailserver-address) - :placeholder (i18n/label :t/specify-mailserver-address) - :content qr-code - :style styles/input - :container styles/input-container - :default-value (get-in manage-mailserver [:url :value]) - :on-change-text #(re-frame/dispatch [:mailserver-set-input :url %])}]]] - [react/view styles/bottom-container - [react/view components.styles/flex] - [components.common/bottom-button - {:forward? true - :label (i18n/label :t/save) - :disabled? (not is-valid?) - :on-press #(re-frame/dispatch [:save-new-mailserver])}]]]])) + (let [url (get-in manage-mailserver [:url :value]) + id (get-in manage-mailserver [:id :value]) + name (get-in manage-mailserver [:name :value])] + [react/view components.styles/flex + [status-bar/status-bar] + [react/keyboard-avoiding-view components.styles/flex + [toolbar/simple-toolbar (i18n/label :t/add-mailserver)] + [react/scroll-view + [react/view styles/edit-mailserver-view + [text-input/text-input-with-label + {:label (i18n/label :t/name) + :placeholder (i18n/label :t/specify-name) + :style styles/input + :container styles/input-container + :default-value name + :on-change-text #(re-frame/dispatch [:mailserver-set-input :name %]) + :auto-focus true}] + [text-input/text-input-with-label + {:label (i18n/label :t/mailserver-address) + :placeholder (i18n/label :t/specify-mailserver-address) + :content qr-code + :style styles/input + :container styles/input-container + :default-value url + :on-change-text #(re-frame/dispatch [:mailserver-set-input :url %])}] + (when id [connect-button id])]] + [react/view styles/bottom-container + [react/view components.styles/flex] + [components.common/bottom-button + {:forward? true + :label (i18n/label :t/save) + :disabled? (not is-valid?) + :on-press #(re-frame/dispatch [:upsert-mailserver])}]]]]))) diff --git a/src/status_im/ui/screens/offline_messaging_settings/views.cljs b/src/status_im/ui/screens/offline_messaging_settings/views.cljs index 29c6b296d5..8fda592cf3 100644 --- a/src/status_im/ui/screens/offline_messaging_settings/views.cljs +++ b/src/status_im/ui/screens/offline_messaging_settings/views.cljs @@ -15,14 +15,19 @@ [react/view (styles/wnode-icon connected?) [vector-icons/icon :icons/wnode {:color (if connected? :white :gray)}]]) -(defn navigate-to-add-mailserver [] - (re-frame/dispatch [:edit-mailserver])) +(defn connect-to-mailserver [id] + (re-frame/dispatch [:connect-wnode id])) -(defn- render-row [current-wnode] - (fn [{:keys [name id]}] - (let [connected? (= id current-wnode)] +(defn navigate-to-add-mailserver [wnode-id] + (re-frame/dispatch [:edit-mailserver wnode-id])) + +(defn- render-row [current-wnode-id] + (fn [{:keys [name id user-defined]}] + (let [connected? (= id current-wnode-id)] [react/touchable-highlight - {:on-press #(re-frame/dispatch [:connect-wnode id]) + {:on-press #(if user-defined + (navigate-to-add-mailserver id) + (connect-to-mailserver id)) :accessibility-label :mailserver-item} [react/view styles/wnode-item [wnode-icon connected?] @@ -31,8 +36,8 @@ name]]]]))) (views/defview offline-messaging-settings [] - (views/letsubs [current-wnode [:settings/current-wnode] - wnodes [:settings/network-wnodes]] + (views/letsubs [current-wnode-id [:settings/current-wnode] + wnodes [:settings/network-wnodes]] [react/view {:flex 1} [status-bar/status-bar] [toolbar/toolbar {} @@ -40,9 +45,9 @@ [toolbar/content-title (i18n/label :t/offline-messaging-settings)] (when config/add-custom-mailservers-enabled? [toolbar/actions - [(toolbar.actions/add false navigate-to-add-mailserver)]])] + [(toolbar.actions/add false (partial navigate-to-add-mailserver nil))]])] [react/view styles/wrapper [list/flat-list {:data (vals wnodes) :default-separator? false :key-fn :id - :render-fn (render-row current-wnode)}]]])) + :render-fn (render-row current-wnode-id)}]]])) diff --git a/test/cljs/status_im/test/offline_messaging_settings/events.cljs b/test/cljs/status_im/test/offline_messaging_settings/events.cljs index 93c15e6a0d..7cb64a361b 100644 --- a/test/cljs/status_im/test/offline_messaging_settings/events.cljs +++ b/test/cljs/status_im/test/offline_messaging_settings/events.cljs @@ -2,7 +2,12 @@ (:require [cljs.test :refer-macros [deftest is testing]] [status-im.ui.screens.offline-messaging-settings.edit-mailserver.events :as events])) -(def valid-enode-url "enode://1da276e34126e93babf24ec88aac1a7602b4cbb2e11b0961d0ab5e989ca9c261aa7f7c1c85f15550a5f1e5a5ca2305b53b9280cf5894d5ecf7d257b173136d40:somepassword@167.99.209.61:30504") +(def enode-id "1da276e34126e93babf24ec88aac1a7602b4cbb2e11b0961d0ab5e989ca9c261aa7f7c1c85f15550a5f1e5a5ca2305b53b9280cf5894d5ecf7d257b173136d40") +(def password "password") +(def host "167.99.209.61:30504") + +(def valid-enode-address (str "enode://" enode-id "@" host)) +(def valid-enode-url (str "enode://" enode-id ":" password "@" host)) (deftest set-input (testing "it validates names" @@ -24,8 +29,59 @@ :error true}}}} (events/set-input :url "broken" {})))))) -(deftest save-new-mailserver - (testing "save a new mailserver" +(deftest edit-mailserver + (let [db {:network "mainnet_rpc" + :account/account {:networks {"mainnet_rpc" + {:config {:NetworkId 1}}}} + :inbox/wnodes + {:mainnet {"a" {:id "a" + :address valid-enode-address + :password password + :name "name"}}}} + cofx {:db db}] + (testing "when no id is given" + (let [actual (events/edit-mailserver nil cofx)] + (testing "it resets :mailserver/manage" + (is (= {:id {:value nil + :error false} + :url {:value "" + :error true} + :name {:value "" + :error true}} + (-> actual :db :mailservers/manage)))) + (testing "it navigates to edit-mailserver view" + (is (= [:navigate-to :edit-mailserver] + (-> actual :dispatch)))))) + (testing "when an id is given" + (testing "when the wnode is in the list" + (let [actual (events/edit-mailserver "a" cofx)] + (testing "it populates the fields with the correct values" + (is (= {:id {:value "a" + :error false} + :url {:value valid-enode-url + :error false} + :name {:value "name" + :error false}} + (-> actual :db :mailservers/manage)))) + (testing "it navigates to edit-mailserver view" + (is (= [:navigate-to :edit-mailserver] + (-> actual :dispatch)))))) + (testing "when the wnode is not in the list" + (let [actual (events/edit-mailserver "not-existing" cofx)] + (testing "it populates the fields with the correct values" + (is (= {:id {:value nil + :error false} + :url {:value "" + :error true} + :name {:value "" + :error true}} + (-> actual :db :mailservers/manage)))) + (testing "it navigates to edit-mailserver view" + (is (= [:navigate-to :edit-mailserver] + (-> actual :dispatch))))))))) + +(deftest upsert-mailserver + (testing "new mailserver" (let [cofx {:random-id "random-id" :db {:network "mainnet_rpc" :account/account {:networks {"mainnet_rpc" @@ -34,7 +90,8 @@ :url {:value "enode://test-id:test-password@url:port"}} :inbox/wnodes {}}} - actual (events/save-new-mailserver cofx nil)] + actual (events/upsert-mailserver cofx nil)] + (testing "it adds the enode to inbox/wnodes" (is (= {:mainnet {"randomid" {:password "test-password" :address "enode://test-id@url:port" @@ -42,5 +99,40 @@ :id "randomid" :user-defined true}}} (get-in actual [:db :inbox/wnodes])))) + (testing "it navigates back" + (is (= [:navigate-back] + (:dispatch actual)))) (testing "it stores it in the db" - (is (= 1 (count (:data-store/tx actual)))))))) + (is (= 1 (count (:data-store/tx actual))))))) + (testing "existing mailserver" + (let [cofx {:random-id "random-id" + :db {:network "mainnet_rpc" + :account/account {:networks {"mainnet_rpc" + {:config {:NetworkId 1}}}} + :mailservers/manage {:id {:value "a"} + :name {:value "new-name"} + :url {:value "enode://new-id:new-password@url:port"}} + + :inbox/wnodes {:mainnet {"a" {:id "a" + :name "old-name" + :address "enode://old-id:old-password@url:port"}}}}} + actual (events/upsert-mailserver cofx nil)] + (testing "it navigates back" + (is (= [:navigate-back] + (:dispatch actual)))) + (testing "it updates the enode to inbox/wnodes" + (is (= {:mainnet {"a" {:password "new-password" + :address "enode://new-id@url:port" + :name "new-name" + :id "a" + :user-defined true}}} + (get-in actual [:db :inbox/wnodes])))) + (testing "it stores it in the db" + (is (= 1 (count (:data-store/tx actual))))) + (testing "it logs the user out if connected to the current mailserver" + (let [actual (events/upsert-mailserver (assoc-in cofx + [:db :account/account :settings] + {:wnode + {:mainnet "a"}}) nil)] + (is (= [:logout] + (-> actual :data-store/tx first :success-event))))))))