[#9789] Add a loading indicator and button to username input of start new chat
Signed-off-by: Andrey Shovkoplyas <motor4ik@gmail.com>
This commit is contained in:
parent
87ce82e10a
commit
d64ba1b540
|
@ -46,7 +46,6 @@
|
|||
(spec/def :contacts/contacts (spec/nilable (spec/map-of :global/not-empty-string :contact/contact)))
|
||||
;;public key of new contact during adding this new contact
|
||||
(spec/def :contacts/new-identity (spec/nilable map?))
|
||||
(spec/def :contacts/new-identity-error (spec/nilable string?))
|
||||
;;on showing this contact's profile (andrey: better to move into profile ns)
|
||||
(spec/def :contacts/identity (spec/nilable :global/not-empty-string))
|
||||
(spec/def :contacts/list-ui-props (spec/nilable (spec/keys :opt-un [:contact-list-ui/edit?])))
|
||||
|
|
|
@ -864,6 +864,13 @@
|
|||
(fn [cofx [_ public-key]]
|
||||
(contact.block/unblock-contact cofx public-key)))
|
||||
|
||||
(defn get-validation-label [value]
|
||||
(case value
|
||||
:invalid
|
||||
(i18n/label :t/use-valid-contact-code)
|
||||
:yourself
|
||||
(i18n/label :t/can-not-add-yourself)))
|
||||
|
||||
(handlers/register-handler-fx
|
||||
:contact/qr-code-scanned
|
||||
[(re-frame/inject-cofx :random-id-generator)]
|
||||
|
@ -883,7 +890,7 @@
|
|||
|
||||
:else
|
||||
{:utils/show-popup {:title (i18n/label :t/unable-to-read-this-code)
|
||||
:content validation-result
|
||||
:content (get-validation-label validation-result)
|
||||
:on-dismiss #(re-frame/dispatch [:navigate-to-clean :home])}}))))
|
||||
|
||||
(handlers/register-handler-fx
|
||||
|
|
|
@ -156,7 +156,6 @@
|
|||
;;contacts
|
||||
(reg-root-key-sub ::contacts :contacts/contacts)
|
||||
(reg-root-key-sub :contacts/current-contact-identity :contacts/identity)
|
||||
(reg-root-key-sub :new-identity-error :contacts/new-identity-error)
|
||||
(reg-root-key-sub :contacts/new-identity :contacts/new-identity)
|
||||
(reg-root-key-sub :group/selected-contacts :group/selected-contacts)
|
||||
;;wallet
|
||||
|
|
|
@ -1,10 +1,6 @@
|
|||
(ns status-im.ui.screens.add-new.new-chat.db
|
||||
(:require [status-im.utils.hex :as hex]
|
||||
[status-im.ethereum.ens :as ens]
|
||||
[status-im.utils.platform :as platform]
|
||||
[status-im.i18n :as i18n]
|
||||
[cljs.spec.alpha :as spec]
|
||||
[clojure.string :as string]))
|
||||
(:require [status-im.ethereum.ens :as ens]
|
||||
[cljs.spec.alpha :as spec]))
|
||||
|
||||
(defn own-public-key?
|
||||
[{:keys [multiaccount]} public-key]
|
||||
|
@ -14,8 +10,6 @@
|
|||
(cond
|
||||
(or (not (spec/valid? :global/public-key public-key))
|
||||
(= public-key ens/default-key))
|
||||
(i18n/label (if platform/desktop?
|
||||
:t/use-valid-contact-code-desktop
|
||||
:t/use-valid-contact-code))
|
||||
:invalid
|
||||
(own-public-key? db public-key)
|
||||
(i18n/label :t/can-not-add-yourself)))
|
||||
:yourself))
|
|
@ -6,7 +6,8 @@
|
|||
[status-im.ethereum.resolver :as resolver]
|
||||
[status-im.ui.screens.add-new.new-chat.db :as db]
|
||||
[status-im.utils.handlers :as handlers]
|
||||
[status-im.ethereum.stateofus :as stateofus]))
|
||||
[status-im.ethereum.stateofus :as stateofus]
|
||||
[status-im.utils.random :as random]))
|
||||
|
||||
(defn- ens-name-parse [contact-identity]
|
||||
(when (string? contact-identity)
|
||||
|
@ -22,18 +23,38 @@
|
|||
ens-name (ens-name-parse contact-identity)]
|
||||
(resolver/pubkey registry ens-name cb))))
|
||||
|
||||
;;NOTE we want to handle only last resolve
|
||||
(def resolve-last-id (atom nil))
|
||||
|
||||
(handlers/register-handler-fx
|
||||
:new-chat/set-new-identity
|
||||
(fn [{db :db} [_ new-identity new-ens-name]]
|
||||
(let [is-public-key? (and (string? new-identity)
|
||||
(string/starts-with? new-identity "0x"))]
|
||||
(merge {:db (assoc db
|
||||
:contacts/new-identity {:public-key new-identity
|
||||
:ens-name (ens-name-parse new-ens-name)}
|
||||
:contacts/new-identity-error (db/validate-pub-key db new-identity))}
|
||||
(when (and (not is-public-key?)
|
||||
(ens/valid-eth-name-prefix? new-identity))
|
||||
(let [chain (ethereum/chain-keyword db)]
|
||||
{:resolve-public-key {:chain chain
|
||||
:contact-identity new-identity
|
||||
:cb #(re-frame/dispatch [:new-chat/set-new-identity % new-identity])}}))))))
|
||||
(fn [{db :db} [_ new-identity new-ens-name id]]
|
||||
(when (or (not id) (= id @resolve-last-id))
|
||||
(let [is-public-key? (and (string? new-identity)
|
||||
(string/starts-with? new-identity "0x"))
|
||||
is-ens? (and (not is-public-key?)
|
||||
(ens/valid-eth-name-prefix? new-identity))
|
||||
error (db/validate-pub-key db new-identity)]
|
||||
(merge {:db (assoc db
|
||||
:contacts/new-identity
|
||||
{:public-key new-identity
|
||||
:state (cond is-ens?
|
||||
:searching
|
||||
(and (string/blank? new-identity) (not new-ens-name))
|
||||
:empty
|
||||
error
|
||||
:error
|
||||
:else
|
||||
:valid)
|
||||
:error error
|
||||
:ens-name (ens-name-parse new-ens-name)})}
|
||||
(when is-ens?
|
||||
(reset! resolve-last-id (random/id))
|
||||
(let [chain (ethereum/chain-keyword db)]
|
||||
{:resolve-public-key
|
||||
{:chain chain
|
||||
:contact-identity new-identity
|
||||
:cb #(re-frame/dispatch [:new-chat/set-new-identity
|
||||
%
|
||||
new-identity
|
||||
@resolve-last-id])}})))))))
|
|
@ -3,4 +3,4 @@
|
|||
|
||||
(defmethod navigation/preload-data! :new-chat
|
||||
[db _]
|
||||
(dissoc db :contacts/new-identity :contacts/new-identity-error))
|
||||
(dissoc db :contacts/new-identity))
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
(ns status-im.ui.screens.add-new.new-chat.styles
|
||||
(:require [status-im.ui.components.colors :as colors]))
|
||||
|
||||
(def error-message
|
||||
{:margin-horizontal 14
|
||||
:margin-top 4
|
||||
(def message
|
||||
{:margin-horizontal 16
|
||||
:align-self :center
|
||||
:font-size 12
|
||||
:color colors/red})
|
||||
:color colors/gray})
|
||||
|
||||
(def list-title
|
||||
{:margin-top 24
|
||||
:margin-left 16
|
||||
:font-size 14
|
||||
:color colors/gray})
|
||||
{:margin-top 24
|
||||
:margin-left 16
|
||||
:font-size 14
|
||||
:color colors/gray})
|
||||
|
|
|
@ -6,53 +6,98 @@
|
|||
[status-im.ui.components.icons.vector-icons :as vector-icons]
|
||||
[status-im.ui.components.list.views :as list]
|
||||
[status-im.ui.components.react :as react]
|
||||
[status-im.ui.components.toolbar.view :as toolbar.view]
|
||||
[status-im.ui.screens.add-new.styles :as add-new.styles]
|
||||
[status-im.ui.screens.add-new.new-chat.styles :as styles]
|
||||
[status-im.utils.platform :as platform]
|
||||
[status-im.ui.components.list-item.views :as list-item]
|
||||
[status-im.ui.components.chat-icon.screen :as chat-icon]
|
||||
[status-im.multiaccounts.core :as multiaccounts]
|
||||
[status-im.ui.components.topbar :as topbar]))
|
||||
[status-im.ui.components.topbar :as topbar]
|
||||
[status-im.utils.debounce :as debounce]
|
||||
[status-im.utils.utils :as utils]))
|
||||
|
||||
(defn- render-row [row _ _]
|
||||
[list-item/list-item {:title (multiaccounts/displayed-name row)
|
||||
:icon [chat-icon/contact-icon-contacts-tab row]
|
||||
:accessories [:chevron]
|
||||
:on-press #(re-frame/dispatch [:chat.ui/start-chat (:public-key row) {:navigation-reset? true}])}])
|
||||
[list-item/list-item
|
||||
{:title (multiaccounts/displayed-name row)
|
||||
:icon [chat-icon/contact-icon-contacts-tab row]
|
||||
:accessories [:chevron]
|
||||
:on-press #(re-frame/dispatch [:chat.ui/start-chat
|
||||
(:public-key row)
|
||||
{:navigation-reset? true}])}])
|
||||
|
||||
(defn- icon-wrapper [color icon]
|
||||
[react/view
|
||||
{:style {:margin-right 16
|
||||
:margin-top 11
|
||||
:width 32
|
||||
:height 32
|
||||
:border-radius 25
|
||||
:align-items :center
|
||||
:justify-content :center
|
||||
:background-color color}}
|
||||
icon])
|
||||
|
||||
(defn- input-icon
|
||||
[state]
|
||||
(case state
|
||||
:searching
|
||||
[icon-wrapper colors/gray
|
||||
[react/activity-indicator {:color colors/white}]]
|
||||
|
||||
:valid
|
||||
[react/touchable-highlight
|
||||
{:on-press #(debounce/dispatch-and-chill [:contact.ui/contact-code-submitted] 3000)}
|
||||
[icon-wrapper colors/blue
|
||||
[vector-icons/icon :main-icons/arrow-right {:color colors/white}]]]
|
||||
|
||||
[icon-wrapper colors/gray
|
||||
[vector-icons/icon :main-icons/arrow-right {:color colors/white}]]))
|
||||
|
||||
(defn get-validation-label [value]
|
||||
(case value
|
||||
:invalid
|
||||
(i18n/label :t/user-not-found)
|
||||
:yourself
|
||||
(i18n/label :t/can-not-add-yourself)))
|
||||
|
||||
(views/defview new-chat []
|
||||
(views/letsubs [contacts [:contacts/active]
|
||||
new-identity [:contacts/new-identity]
|
||||
error-message [:new-identity-error]]
|
||||
{:keys [state ens-name public-key error]} [:contacts/new-identity]]
|
||||
[react/view {:style {:flex 1}}
|
||||
[topbar/topbar {:title :t/new-chat :modal? true}]
|
||||
[topbar/topbar
|
||||
{:title :t/new-chat
|
||||
:modal? true
|
||||
:accessories [{:icon :qr
|
||||
:handler #(re-frame/dispatch [:qr-scanner.ui/scan-qr-code-pressed
|
||||
{:title (i18n/label :t/new-contact)
|
||||
:handler :contact/qr-code-scanned}])}]}]
|
||||
[react/view add-new.styles/new-chat-container
|
||||
[react/view add-new.styles/new-chat-input-container
|
||||
[react/text-input {:on-change-text #(re-frame/dispatch [:new-chat/set-new-identity %])
|
||||
:on-submit-editing #(when (and new-identity (not error-message))
|
||||
(re-frame/dispatch [:contact.ui/contact-code-submitted]))
|
||||
:placeholder (i18n/label :t/enter-contact-code)
|
||||
:style add-new.styles/input
|
||||
;; This input is fine to preserve inputs
|
||||
;; so its contents will not be erased
|
||||
;; in onWillBlur navigation event handler
|
||||
:preserve-input? true
|
||||
:accessibility-label :enter-contact-code-input
|
||||
:return-key-type :go}]]
|
||||
(when-not platform/desktop?
|
||||
[react/touchable-highlight {:on-press #(re-frame/dispatch [:qr-scanner.ui/scan-qr-code-pressed
|
||||
{:title (i18n/label :t/new-contact)
|
||||
:handler :contact/qr-code-scanned}])
|
||||
:style add-new.styles/button-container
|
||||
:accessibility-label :scan-contact-code-button}
|
||||
[react/view
|
||||
[vector-icons/icon :main-icons/camera {:color colors/blue}]]])]
|
||||
(when error-message
|
||||
[react/text {:style styles/error-message}
|
||||
error-message])
|
||||
(when (seq contacts)
|
||||
[list-item/list-item {:title :t/contacts :type :section-header}])
|
||||
[react/text-input
|
||||
{:on-change-text
|
||||
#(do
|
||||
(re-frame/dispatch [:set-in [:contacts/new-identity :state] :searching])
|
||||
(debounce/debounce-and-dispatch [:new-chat/set-new-identity %] 600))
|
||||
:on-submit-editing
|
||||
#(when (= state :valid)
|
||||
(debounce/dispatch-and-chill [:contact.ui/contact-code-submitted] 3000))
|
||||
:placeholder (i18n/label :t/enter-contact-code)
|
||||
:style add-new.styles/input
|
||||
;; This input is fine to preserve inputs
|
||||
;; so its contents will not be erased
|
||||
;; in onWillBlur navigation event handler
|
||||
:preserve-input? true
|
||||
:accessibility-label :enter-contact-code-input
|
||||
:return-key-type :go}]]
|
||||
[react/view {:width 16}]
|
||||
[input-icon state]]
|
||||
[react/view {:min-height 30 :justify-content :flex-end}
|
||||
[react/text {:style styles/message}
|
||||
(cond (= state :error)
|
||||
(get-validation-label error)
|
||||
(= state :valid)
|
||||
(str (when ens-name (str ens-name " • "))
|
||||
(utils/get-shortened-address public-key))
|
||||
:else "")]]
|
||||
[list/flat-list {:data contacts
|
||||
:key-fn :address
|
||||
:render-fn render-row
|
||||
|
|
|
@ -4,9 +4,9 @@
|
|||
[status-im.utils.styles :as styles]))
|
||||
|
||||
(def new-chat-container
|
||||
{:flex-direction :row
|
||||
:padding-vertical 16
|
||||
:padding-left 16})
|
||||
{:flex-direction :row
|
||||
:padding-top 16
|
||||
:padding-left 16})
|
||||
|
||||
(def input-container
|
||||
{:flex-direction :row
|
||||
|
|
|
@ -56,7 +56,7 @@
|
|||
{:browser? true}
|
||||
[toolbar.view/nav-button
|
||||
(actions/close (fn []
|
||||
(debounce/clear)
|
||||
(debounce/clear :browser/navigation-state-changed)
|
||||
(re-frame/dispatch [:navigate-back])
|
||||
(when error?
|
||||
(re-frame/dispatch [:browser.ui/remove-browser-pressed browser-id]))))]
|
||||
|
|
|
@ -187,7 +187,6 @@
|
|||
|
||||
(spec/def ::db (spec/keys :opt [:contacts/contacts
|
||||
:contacts/new-identity
|
||||
:contacts/new-identity-error
|
||||
:contacts/identity
|
||||
:contacts/ui-props
|
||||
:contacts/list-ui-props
|
||||
|
|
|
@ -1,24 +1,30 @@
|
|||
(ns status-im.utils.debounce
|
||||
(:require [re-frame.core :as re-frame]))
|
||||
|
||||
(def timeout (atom nil))
|
||||
(def timeout (atom {}))
|
||||
|
||||
(defn clear [event-key]
|
||||
(when-let [event-timeout (get @timeout event-key)]
|
||||
(js/clearTimeout event-timeout)))
|
||||
|
||||
(defn clear-all []
|
||||
(doseq [[_ v] @timeout]
|
||||
(js/clearTimeout v)))
|
||||
|
||||
(defn debounce-and-dispatch
|
||||
"Dispatches event only if there were no calls of this function in period of *time* ms"
|
||||
[event time]
|
||||
(when @timeout (js/clearTimeout @timeout))
|
||||
(reset! timeout (js/setTimeout #(re-frame/dispatch event) time)))
|
||||
(let [event-key (first event)]
|
||||
(clear event-key)
|
||||
(swap! timeout assoc event-key (js/setTimeout #(re-frame/dispatch event) time))))
|
||||
|
||||
(defn clear []
|
||||
(when @timeout (js/clearTimeout @timeout)))
|
||||
|
||||
(def chill? (atom false))
|
||||
(def chill (atom {}))
|
||||
|
||||
(defn dispatch-and-chill
|
||||
"Dispateches event and ignores next calls in period of *time* ms"
|
||||
[event time]
|
||||
(when-not @chill?
|
||||
(reset! chill? true)
|
||||
(js/setTimeout #(reset! chill? false) time)
|
||||
(re-frame/dispatch event)))
|
||||
|
||||
(let [event-key (first event)]
|
||||
(when-not (get @chill event-key)
|
||||
(swap! chill assoc event-key true)
|
||||
(js/setTimeout #(swap! chill assoc event-key false) time)
|
||||
(re-frame/dispatch event))))
|
|
@ -101,7 +101,7 @@
|
|||
"browsing-site-blocked-title": "This site is blocked",
|
||||
"browsing-title": "Browse",
|
||||
"camera-access-error": "To grant the required camera permission, please go to your system settings and make sure that Status > Camera is selected.",
|
||||
"can-not-add-yourself": "You can't add yourself",
|
||||
"can-not-add-yourself": "That's you, to start a chat choose someone else",
|
||||
"cancel": "Cancel",
|
||||
"cancel-keycard-setup": "Cancel Keycard setup",
|
||||
"cannot-read-card": "Can't read card.\nPlease hold it to the back of your phone",
|
||||
|
@ -1088,5 +1088,6 @@
|
|||
"cant-report-bug": "Can't report a bug",
|
||||
"mail-should-be-configured": "Mail client should be configured",
|
||||
"check-on-etherscan": "Check on etherscan",
|
||||
"transactions-load-more": "Load more"
|
||||
"transactions-load-more": "Load more",
|
||||
"user-not-found": "User not found"
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue