Lookup/add a contact from home screen (#14477)

This commit is contained in:
Erik Seppanen 2023-01-19 11:59:33 -05:00 committed by Andrea Maria Piana
parent d16baca64f
commit fac9e644a9
No known key found for this signature in database
GPG Key ID: AA6CCA6DE0E06424
24 changed files with 434 additions and 47 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 822 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 112 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 192 KiB

View File

@ -1,7 +1,7 @@
(ns status-im.add-new.db
(:require [cljs.spec.alpha :as spec]
[status-im.ethereum.ens :as ens]
[status-im.utils.db :as utils.db]))
[status-im2.utils.validators :as validators]))
(defn own-public-key?
[{:keys [multiaccount]} public-key]
@ -10,7 +10,7 @@
(defn validate-pub-key
[db public-key]
(cond
(or (not (utils.db/valid-public-key? public-key))
(or (not (validators/valid-public-key? public-key))
(= public-key ens/default-key))
:invalid
(own-public-key? db public-key)
@ -27,4 +27,4 @@
[topic]
(and topic
(spec/valid? ::topic topic)
(not (utils.db/valid-public-key? topic))))
(not (validators/valid-public-key? topic))))

View File

@ -35,15 +35,16 @@
:on-error #(cb "0x")}))
(defn pubkey
[chain-id ens-name cb]
{:pre [(is-valid-eth-name? ens-name)]}
(json-rpc/call {:method "ens_publicKeyOf"
:params [chain-id ens-name]
:on-success cb
;;at some point infura started to return execution reverted error instead of "0x"
;;result
;;our code expects "0x" result
:on-error #(cb "0x")}))
([chain-id ens-name on-success on-error]
{:pre [(is-valid-eth-name? ens-name)]}
(json-rpc/call {:method "ens_publicKeyOf"
:params [chain-id ens-name]
:on-success on-success
:on-error on-error}))
;; At some point, infura started to return "execution reverted" error
;; instead of "0x" result. Our code expects "0x" result.
([chain-id ens-name cb]
(pubkey chain-id ens-name cb #(cb "0x"))))
(defn owner
[chain-id ens-name cb]

View File

@ -2,7 +2,6 @@
(:require
clojure.set
[re-frame.core :as re-frame]
status-im.add-new.core
[status-im.async-storage.core :as async-storage]
status-im.backup.core
status-im.bootnodes.core

View File

@ -1,7 +1,7 @@
(ns status-im.native-module.core
(:require ["react-native" :as react-native]
[re-frame.core :as re-frame]
[status-im.utils.db :as utils.db]
[status-im2.utils.validators :as validators]
[status-im.utils.platform :as platform]
[status-im.utils.react-native :as react-native-utils]
[status-im.utils.types :as types]
@ -472,13 +472,13 @@
"Generate a 3 words random name based on the user public-key, synchronously"
[public-key]
(log/debug "[native-module] generate-gfycat")
(when (utils.db/valid-public-key? public-key)
(when (validators/valid-public-key? public-key)
(.generateAlias ^js (status) public-key)))
(defn generate-gfycat-async
"Generate a 3 words random name based on the user public-key, asynchronously"
[public-key callback]
(when (utils.db/valid-public-key? public-key)
(when (validators/valid-public-key? public-key)
(.generateAliasAsync ^js (status) public-key callback)))
(defn identicon

View File

@ -5,6 +5,7 @@
{:empty-chats-header (js/require "../resources/images/ui/empty-chats-header.png")
:welcome (js/require "../resources/images/ui/welcome.jpg")
:welcome-dark (js/require "../resources/images/ui/welcome-dark.jpg")
:add-new-contact (js/require "../resources/images/ui/add-contact.png")
:chat (js/require "../resources/images/ui/chat.jpg")
:chat-dark (js/require "../resources/images/ui/chat-dark.jpg")
:wallet (js/require "../resources/images/ui/wallet.jpg")

View File

@ -9,7 +9,7 @@
[status-im.ethereum.eip681 :as eip681]
[status-im.ethereum.ens :as ens]
[status-im.ethereum.stateofus :as stateofus]
[status-im.utils.db :as utils.db]
[status-im2.utils.validators :as validators]
[status-im.utils.http :as http]
[status-im.utils.wallet-connect :as wallet-connect]
[taoensso.timbre :as log]
@ -71,7 +71,7 @@
(defn match-contact-async
[chain {:keys [user-id ens-name]} callback]
(let [valid-key (and (utils.db/valid-public-key? user-id)
(let [valid-key (and (validators/valid-public-key? user-id)
(not= user-id ens/default-key))]
(cond
valid-key
@ -113,8 +113,8 @@
(not (string/blank? chat-name))
(> (count chat-id-parts) 1)
(not (string/blank? (first chat-id-parts)))
(utils.db/valid-public-key? admin-pk)
(utils.db/valid-public-key? (last chat-id-parts)))
(validators/valid-public-key? admin-pk)
(validators/valid-public-key? (last chat-id-parts)))
{:type :group-chat
:chat-id chat-id
:invitation-admin admin-pk
@ -228,7 +228,7 @@
(= handler :group-chat)
(cb (match-group-chat chats query-params))
(utils.db/valid-public-key? uri)
(validators/valid-public-key? uri)
(match-contact-async chain {:user-id uri} cb)
(= handler :community-requests)

View File

@ -20,7 +20,7 @@
[status-im.ui.components.react :as react]
[status-im.ui.components.topbar :as topbar]
[status-im.ui.screens.chat.photos :as photos]
[status-im.utils.db :as utils.db]
[status-im2.utils.validators :as validators]
[status-im.utils.gfycat.core :as gfycat]
[status-im.utils.identicon :as identicon]
[status-im.utils.utils :as utils]
@ -172,7 +172,7 @@
#(do
(reset! search-value %)
(re-frame/dispatch [:set-in [:contacts/new-identity :state] :empty])
(debounce/debounce-and-dispatch [:new-chat/set-new-identity %] 600))
(debounce/debounce-and-dispatch [:contacts/set-new-identity %] 600))
:on-submit-editing
#(when (= state :valid)
(debounce/dispatch-and-chill [:contact.ui/contact-code-submitted false nil] 3000))
@ -301,7 +301,7 @@
(fn []
(let [{:keys [state ens-name public-key error]} @(re-frame/subscribe [:contacts/new-identity])
blocked? (and
(utils.db/valid-public-key? (or public-key ""))
(validators/valid-public-key? (or public-key ""))
@(re-frame/subscribe [:contacts/contact-blocked?
public-key]))]
[react/view {:style {:flex 1}}
@ -326,7 +326,7 @@
{:on-change-text
#(do
(re-frame/dispatch [:set-in [:contacts/new-identity :state] :searching])
(debounce/debounce-and-dispatch [:new-chat/set-new-identity %] 600))
(debounce/debounce-and-dispatch [:contacts/set-new-identity %] 600))
:on-submit-editing
#(when (= state :valid)
(debounce/dispatch-and-chill [:contact.ui/contact-code-submitted true @entered-nickname]

View File

@ -7,7 +7,6 @@
[quo2.foundations.colors :as quo2.colors]
[re-frame.core :as re-frame]
[reagent.core :as reagent]
[status-im.add-new.core :as new-chat]
[status-im.add-new.db :as db]
[utils.i18n :as i18n]
[status-im.qr-scanner.core :as qr-scanner]
@ -82,15 +81,15 @@
:on-blur (fn []
(when chats-empty
(re-frame/dispatch [:search/home-filter-changed nil]))
(re-frame/dispatch [::new-chat/clear-new-identity]))
(re-frame/dispatch [:contacts/clear-new-identity]))
:on-focus (fn [search-filter]
(when-not search-filter
(re-frame/dispatch [:search/home-filter-changed ""])
(re-frame/dispatch [::new-chat/clear-new-identity])))
(re-frame/dispatch [:contacts/clear-new-identity])))
:on-change (fn [text]
(re-frame/dispatch [:search/home-filter-changed text])
(re-frame/dispatch [:set-in [:contacts/new-identity :state] :searching])
(debounce/debounce-and-dispatch [:new-chat/set-new-identity text] 300))}]])
(debounce/debounce-and-dispatch [:contacts/set-new-identity text] 300))}]])
(defn search-input-wrapper-old
[search-filter chats-empty]
@ -104,11 +103,11 @@
:on-blur (fn []
(when chats-empty
(re-frame/dispatch [:search/home-filter-changed nil]))
(re-frame/dispatch [::new-chat/clear-new-identity]))
(re-frame/dispatch [:contacts/clear-new-identity]))
:on-focus (fn [search-filter]
(when-not search-filter
(re-frame/dispatch [:search/home-filter-changed ""])
(re-frame/dispatch [::new-chat/clear-new-identity])))
(re-frame/dispatch [:contacts/clear-new-identity])))
:on-change (fn [text]
(re-frame/dispatch [:search/home-filter-changed text])
(re-frame/dispatch [:set-in [:contacts/new-identity :state] :searching])

View File

@ -1,7 +1,6 @@
(ns status-im.ui.screens.screens
(:require
[quo.design-system.colors :as colors]
[status-im.add-new.core :as new-chat.events]
[utils.i18n :as i18n]
[status-im.ui.components.icons.icons :as icons]
[status-im.ui.screens.about-app.views :as about-app]
@ -545,7 +544,7 @@
;[Chat] New Chat
{:name :new-chat
:on-focus [::new-chat.events/new-chat-focus]
:on-focus [:contacts/new-chat-focus]
;;TODO accessories
:options {:topBar {:visible false}}
:component new-chat/new-chat}
@ -569,7 +568,7 @@
:component contact/nickname}
{:name :new-chat-aio
:on-focus [::new-chat.events/new-chat-focus]
:on-focus [:contacts/new-chat-focus]
;;TODO accessories
:options {:topBar {:visible false}}
:component new-chat-aio/contact-selection-list}
@ -595,13 +594,6 @@
:component communities.invite/invite
:insets {:bottom true}}
;New Contact
{:name :new-contact
:on-focus [::new-chat.events/new-chat-focus]
;;TODO accessories
:options {:topBar {:visible false}}
:component new-chat/new-contact}
;[Wallet] Recipient
{:name :recipient
:insets {:bottom true}

View File

@ -53,7 +53,7 @@
:on-change (fn [text]
(re-frame/dispatch [:search/recipient-filter-changed text])
(re-frame/dispatch [:set-in [:contacts/new-identity :state] :searching])
(debounce/debounce-and-dispatch [:new-chat/set-new-identity text] 300))}]]))
(debounce/debounce-and-dispatch [:contacts/set-new-identity text] 300))}]]))
(defn section
[_ _ _]

View File

@ -1,5 +0,0 @@
(ns status-im.utils.db)
(defn valid-public-key?
[s]
(and (string? s) (not-empty s) (boolean (re-matches #"0x04[0-9a-f]{128}" s))))

View File

@ -0,0 +1,96 @@
(ns status-im2.contexts.add-new-contact.events
(:require [utils.re-frame :as rf]
[status-im.utils.types :as types]
[re-frame.core :as re-frame]
[status-im.ethereum.core :as ethereum]
[status-im.ethereum.ens :as ens]
[status-im.ethereum.stateofus :as stateofus]
[status-im.native-module.core :as status]
[status-im2.utils.validators :as validators]
[status-im.utils.utils :as utils]))
(re-frame/reg-fx
:contacts/resolve-public-key-from-ens-name
(fn [{:keys [chain-id ens-name on-success on-error]}]
(ens/pubkey chain-id ens-name on-success on-error)))
(re-frame/reg-fx
:contacts/decompress-public-key
(fn [{:keys [public-key on-success on-error]}]
(status/compressed-key->public-key
public-key
(fn [resp]
(let [{:keys [error]} (types/json->clj resp)]
(if error
(on-error error)
(on-success
(str "0x" (subs resp 5)))))))))
(rf/defn set-new-identity
{:events [:contacts/set-new-identity]}
[{:keys [db]} input]
(let [input (utils/safe-trim input)
public-key? (validators/valid-public-key? input)
compressed-key? (validators/valid-compressed-key? input)
ens-name (if compressed-key? nil (stateofus/ens-name-parse input))
on-success (fn [pubkey]
(rf/dispatch
[:contacts/set-new-identity-success
input ens-name pubkey]))
on-error (fn [err]
(rf/dispatch
[:contacts/set-new-identity-error err input]))]
(cond
(empty? input) {:db (dissoc db :contacts/new-identity)}
public-key? {:db (assoc db
:contacts/new-identity
{:input input
:public-key input
:ens-name nil
:state :error
:error :uncompressed-key})}
:else
(cond->
{:db (assoc db
:contacts/new-identity
{:input input
:public-key nil
:ens-name nil
:state :searching
:error nil})}
compressed-key? (assoc :contacts/decompress-public-key
{:public-key input
:on-success on-success
:on-error on-error})
(not compressed-key?) (assoc :contacts/resolve-public-key-from-ens-name
{:chain-id (ethereum/chain-id db)
:ens-name ens-name
:on-success on-success
:on-error on-error})))))
(rf/defn set-new-identity-success
{:events [:contacts/set-new-identity-success]}
[{:keys [db]} input ens-name pubkey]
{:db (assoc db
:contacts/new-identity
{:input input
:public-key pubkey
:ens-name ens-name
:state :valid
:error nil})})
(rf/defn set-new-identity-error
{:events [:contacts/set-new-identity-error]}
[{:keys [db]} error input]
{:db (assoc db
:contacts/new-identity
{:input input
:public-key nil
:ens-name nil
:state :error
:error :invalid})})
(rf/defn clear-new-identity
{:events [:contacts/clear-new-identity :contacts/new-chat-focus]}
[{:keys [db]}]
{:db (dissoc db :contacts/new-identity)})

View File

@ -0,0 +1,53 @@
(ns status-im2.contexts.add-new-contact.events-test
(:require [cljs.test :refer-macros [deftest is]]
[status-im2.contexts.add-new-contact.events :as core]))
(def init-db
{:networks/current-network "mainnet_rpc"
:networks/networks {"mainnet_rpc"
{:id "mainnet_rpc"
:config {:NetworkId 1}}}})
(defn search-db
[input]
{:contacts/new-identity {:input input
:public-key nil
:ens-name nil
:state :searching
:error nil}})
(deftest search-empty-string
(let [input ""
expected {:db init-db}
actual (core/set-new-identity {:db init-db} input)]
(is (= actual expected))))
(deftest search-ens
(let [input "esep"
clean (fn [db]
(-> db
(assoc-in [:contacts/resolve-public-key-from-ens-name :on-success] nil)
(assoc-in [:contacts/resolve-public-key-from-ens-name :on-error] nil)))
expected {:db (merge init-db (search-db input))
:contacts/resolve-public-key-from-ens-name
{:chain-id 1
:ens-name (str input ".stateofus.eth")
:on-success nil
:on-error nil}}
actual (core/set-new-identity {:db init-db} input)]
(is (= (clean actual) expected))))
(deftest search-compressed-key
(let [input "zQ3shWj4WaBdf2zYKCkXe6PHxDxNTzZyid1i75879Ue9cX9gA"
clean (fn [db]
(-> db
(assoc-in [:contacts/decompress-public-key :on-success] nil)
(assoc-in [:contacts/decompress-public-key :on-error] nil)))
expected {:db (merge init-db (search-db input))
:contacts/decompress-public-key
{:public-key input
:on-success nil
:on-error nil}}
actual (core/set-new-identity {:db init-db} input)]
(is (= (clean actual) expected))))

View File

@ -0,0 +1,138 @@
(ns status-im2.contexts.add-new-contact.style
(:require [quo2.foundations.colors :as colors]
[status-im.react-native.resources :as resources]
[react-native.platform :as platform]
[quo2.foundations.typography :as typography]))
(defn container-kbd
[]
{:style {:flex 1}
:keyboardVerticalOffset 60})
(def container-image
{:style {:flex 1
:flex-direction :row}})
(defn image
[file]
{:source (resources/get-image file)
:style {:flex 1}})
(defn container-outer
[]
{:style {:flex (if platform/ios? 4.5 5)
:background-color (colors/theme-colors colors/white colors/neutral-95)
:justify-content :space-between
:align-items :center
:padding 16
:border-radius 20}})
(def container-inner
{:style {:flex-direction :column
:align-items :flex-start}})
(def container-text-input
{:style {:flex-direction :row
:justify-content :space-between}})
(def container-error
{:style {:flex-direction :row
:align-items :center
:margin-top 8}})
(defn text-title
[]
{:size :heading-1
:weight :semi-bold
:style {:margin-bottom 6
:color (colors/theme-colors
colors/neutral-100
colors/white)}})
(defn text-subtitle
[]
{:size :paragraph-1
:weight :regular
:style {:margin-bottom 20
:color (colors/theme-colors
colors/neutral-100
colors/white)}})
(defn text-description
[]
{:size :paragraph-2
:weight :medium
:style {:margin-bottom 6
:color (colors/theme-colors
colors/neutral-50
colors/neutral-40)}})
(def icon-error
{:size 16
:color colors/danger-50})
(def text-error
{:size :paragraph-2
:align :left
:style {:margin-left 4
:color colors/danger-50}})
(defn text-input
[error?]
{:accessibility-label :enter-contact-code-input
:auto-capitalize :none
:placeholder-text-color (colors/theme-colors
colors/neutral-40
colors/neutral-50)
:multiline true
:style
(merge typography/monospace
typography/paragraph-1
{:padding-top 8
:padding-left 12
:padding-right 12
:padding-bottom 8
:margin-right 10
:flex 1
:color (colors/theme-colors
colors/black
colors/white)
:background-color (colors/theme-colors
colors/white
colors/neutral-95)
:border-width 1
:border-radius 12
:border-color (if error?
colors/danger-50-opa-40
(colors/theme-colors
colors/neutral-20
colors/neutral-80))})})
(defn button-close
[]
{:type :grey
:icon true
:accessibility-label :new-contact-close-button
:size 32
:override-background-color (colors/theme-colors
colors/white-opa-60
colors/neutral-80-opa-60)
:style {:position :absolute
:left 20
:top 20}})
(def button-qr
{:type :outline
:icon true
:size 40})
(defn button-view-profile
[state]
{:type :primary
:size 40
:width 335
:style {:margin-top 24
:margin-bottom 24}
:accessibility-label :new-contact-button
:before :i/profile
:disabled (not= state :valid)})

View File

@ -0,0 +1,56 @@
(ns status-im2.contexts.add-new-contact.views
(:require [status-im2.contexts.add-new-contact.style :as style]
[utils.i18n :as i18n]
[utils.debounce :as debounce]
[utils.re-frame :as rf]
[react-native.core :as rn]
[quo2.core :as quo]))
(defn new-contact
[]
(let [{:keys [input public-key state error]} (rf/sub
[:contacts/new-identity])
error? (and (= state :error)
(= error :uncompressed-key))]
[rn/keyboard-avoiding-view (style/container-kbd)
[rn/view style/container-image
[rn/image (style/image :add-new-contact)]
[quo/button
(merge (style/button-close)
{:on-press
(fn []
(rf/dispatch [:contacts/clear-new-identity])
(rf/dispatch [:navigate-back]))}) :i/close]]
[rn/view (style/container-outer)
[rn/view style/container-inner
[quo/text (style/text-title)
(i18n/label :t/add-a-contact)]
[quo/text (style/text-subtitle)
(i18n/label :t/find-your-friends)]
[quo/text (style/text-description)
(i18n/label :t/ens-or-chat-key)]
[rn/view style/container-text-input
[rn/text-input
(merge (style/text-input error?)
{:default-value input
:placeholder (i18n/label :t/type-some-chat-key)
:on-change-text #(debounce/debounce-and-dispatch
[:contacts/set-new-identity %]
600)})]
[quo/button
(merge style/button-qr
{:on-press #(js/alert "TODO: to be implemented")})
:i/scan]]
(when error?
[rn/view style/container-error
[quo/icon :i/alert style/icon-error]
[quo/text style/text-error (i18n/label :t/not-a-chatkey)]])]
[rn/view
[quo/button
(merge (style/button-view-profile state)
{:on-press
(fn []
(rf/dispatch [:contacts/clear-new-identity])
(rf/dispatch [:navigate-back])
(rf/dispatch [:chat.ui/show-profile public-key]))})
(i18n/label :t/view-profile)]]]]))

View File

@ -9,6 +9,7 @@
[status-im2.common.json-rpc.events]
[status-im2.common.theme.core :as theme]
[status-im2.common.toasts.events]
[status-im2.contexts.add-new-contact.events]
[status-im2.navigation.events :as navigation]
[status-im2.db :as db]
[utils.re-frame :as rf]

View File

@ -3,6 +3,7 @@
[status-im.ui.screens.screens :as old-screens]
[status-im2.contexts.activity-center.view :as activity-center]
[status-im2.contexts.chat.messages.view :as chat]
[status-im2.contexts.add-new-contact.views :as add-new-contact]
[status-im2.contexts.communities.discover.view :as communities.discover]
[status-im2.contexts.communities.overview.view :as communities.overview]
[status-im2.contexts.quo-preview.main :as quo.preview]
@ -47,6 +48,10 @@
:decelerate}}]}}}
:component images-horizontal/images-horizontal}
{:name :new-contact
:options {:topBar {:visible false}}
:component add-new-contact/new-contact}
{:name :discover-communities
:options {:topBar {:visible false}}
:component communities.discover/discover}

View File

@ -0,0 +1,14 @@
(ns status-im2.utils.validators)
(defn valid-public-key?
[s]
(and (string? s)
(not-empty s)
(boolean (re-matches #"0x04[0-9a-f]{128}" s))))
(defn valid-compressed-key?
[s]
(and (string? s)
(not-empty s)
(boolean (re-matches #"^zQ3[0-9A-HJ-NP-Za-km-z]{46}" s))))

View File

@ -0,0 +1,33 @@
(ns status-im2.utils.validators-test
(:require [cljs.test :refer-macros [deftest testing is]]
[status-im2.utils.validators :refer [valid-compressed-key?]]))
(deftest test-valid-compressed-key
(testing "valid"
(is (valid-compressed-key?
"zQ3shWj4WaBdf2zYKCkXe6PHxDxNTzZyid1i75879Ue9cX9gA")))
(testing "uncompressed key"
(is
(not
(valid-compressed-key?
"0x048a6773339d11ccf5fd81677b7e54daeec544a1287bd92b725047ad6faa9a9b9f8ea86ed5a226d2a994f5f46d0b43321fd8de7b7997a166e67905c8c73cd37ce"))))
(testing "nil"
(is (not (valid-compressed-key? nil))))
(testing "empty string"
(is (not (valid-compressed-key? ""))))
(testing "too short"
(is (not (valid-compressed-key? "zQ3FGR5y6U5a6"))))
(testing "too long"
(is (not (valid-compressed-key?
"zQ3shWj4WaBdf2zYKCkXe6PHxDxNTzZyid1i75879Ue9cX9gA2"))))
(testing "0x prefix"
(is (not (valid-compressed-key? "0xFGR5y6U5a6"))))
(testing "contains I"
(is (not (valid-compressed-key?
"zQ3shWj4WaBdf2zYKCkXe6PHxDxNTzZyid1i75879Ue9cX9gI"))))
(testing "contains O"
(is (not (valid-compressed-key?
"zQ3shWj4WaBdf2zYKCkXe6PHxDxNTzZyid1i75879Ue9cX9gO"))))
(testing "contains l"
(is (not (valid-compressed-key?
"zQ3shWj4WaBdf2zYKCkXe6PHxDxNTzZyid1i75879Ue9cX9gl")))))

View File

@ -570,6 +570,7 @@
"skip": "Skip",
"password-placeholder": "Password...",
"confirm-password-placeholder": "Confirm your password...",
"ens-or-chat-key": "ENS or Chat key",
"enter-pin": "Enter 6-digit passcode",
"enter-puk-code": "Enter PUK code",
"enter-puk-code-description": "6-digit passcode has been blocked.\n Please enter PUK code to unblock passcode.",
@ -597,6 +598,7 @@
"fetch-messages": "Fetch messages",
"fetch-timeline": "↓ Fetch",
"find": "Find",
"find-your-friends": "Find your friends with their ENS or Chatkey",
"finish": "Finish",
"finishing-card-setup": "Finishing card setup",
"fleet": "Fleet",
@ -986,6 +988,7 @@
"node-version": "Node version",
"nonce": "Nonce",
"none": "None",
"not-a-chatkey": "This is not a chatkey",
"not-applicable": "Not applicable for unsigned transactions",
"not-keycard-text": "The card you used is not a Keycard. You need to purchase a Keycard to use it",
"not-keycard-title": "Not a Keycard",
@ -1871,6 +1874,7 @@
"identity-verification-request": "Identity verification",
"identity-verification-request-sent": "asks",
"type-something": "Type something",
"type-some-chat-key": "0x123abc",
"your-answer": "Your answer",
"membership": "Membership",
"jump-to": "Jump to",