Compare commits

..
101 changed files with 1505 additions and 1216 deletions
+3 -5
View File
@@ -331,14 +331,13 @@ shadow-server:##@ Start shadow-cljs in server mode for watching
_test-clojure: export TARGET := clojure
_test-clojure: export WATCH ?= false
_test-clojure: status-go-library
_test-clojure:
ifeq ($(WATCH), true)
yarn node-pre-gyp rebuild && \
yarn install && \
yarn shadow-cljs compile mocks && \
nodemon --exec "yarn shadow-cljs compile test && node --require ./test-resources/override.js $$SHADOW_OUTPUT_TO" -e cljs
else
yarn node-pre-gyp rebuild && \
yarn install && \
yarn shadow-cljs compile mocks && \
yarn shadow-cljs compile test && \
node --require ./test-resources/override.js "$$SHADOW_OUTPUT_TO"
@@ -351,9 +350,8 @@ test: _test-clojure
test-watch-for-repl: export SHADOW_OUTPUT_TO := target/test/test.js
test-watch-for-repl: export SHADOW_NS_REGEXP := .*-test$$
test-watch-for-repl: status-go-library
test-watch-for-repl: ##@test Watch all Clojure tests and support REPL connections
yarn node-pre-gyp rebuild
yarn install
rm -f target/test/test.js
yarn shadow-cljs compile mocks && \
concurrently --kill-others --prefix-colors 'auto' --names 'build,repl' \
-10
View File
@@ -53,14 +53,6 @@ copyNodeModules() {
# Find files that were modified and should cause a re-copying of node modules.
# Some files are generated/modified by build processes and should be ignored.
#
# react-native/ReactCommon/react/renderer/components/rncore/*
# generated at runtime by react-native
# related code https://github.com/facebook/react-native/blob/v0.72.5/packages/react-native/ReactCommon/React-rncore.podspec#L19
#
# react-native-config/ios/ReactNativeConfig/GeneratedDotEnv.m
# generated at runtime by react-native-config
# related code https://github.com/lugg/react-native-config/blob/v1.5.0/react-native-config.podspec#L52
findFilesNewerThan() {
local sentinel="${1}"
local dir="${2}"
@@ -69,8 +61,6 @@ findFilesNewerThan() {
-not -ipath "*/*android/build/*" -prune \
-not -ipath "*/xcuserdata/*" -prune \
-not -ipath "*/scripts/.packager.env" \
-not -path "*/node_modules/react-native/ReactCommon/react/renderer/components/rncore/*" \
-not -path "*/node_modules/react-native-config/ios/ReactNativeConfig/GeneratedDotEnv.m" \
-print
}
+4
View File
@@ -3,6 +3,7 @@
["eth-phishing-detect" :as eth-phishing-detect]
[clojure.string :as string]
[legacy.status-im.bottom-sheet.events :as bottom-sheet]
[legacy.status-im.browser.eip3085 :as eip3085]
[legacy.status-im.browser.eip3326 :as eip3326]
[legacy.status-im.browser.permissions :as browser.permissions]
[legacy.status-im.browser.webview-ref :as webview-ref]
@@ -442,6 +443,9 @@
(= method "wallet_switchEthereumChain")
(eip3326/handle-switch-ethereum-chain cofx dapp-name id message-id (first params))
(= method "wallet_addEthereumChain")
(eip3085/handle-add-ethereum-chain cofx dapp-name id message-id (first params))
:else
{:browser/call-rpc [payload
#(re-frame/dispatch [:browser.callback/call-rpc
+38
View File
@@ -2,6 +2,10 @@
;(`wallet_addEthereumChain`)
(ns legacy.status-im.browser.eip3085
(:require
[clojure.string :as string]
[legacy.status-im.network.core :as network]
[legacy.status-im.ui.screens.browser.eip3085.sheet :as sheet]
[legacy.status-im.utils.random :as random]
[re-frame.core :as re-frame]
[status-im.constants :as constants]
[taoensso.timbre :as log]
@@ -36,3 +40,37 @@
:message "User rejected the request."}}
:dispatch [:bottom-sheet/hide-old]})
(rf/defn handle-add-ethereum-chain
{:events [:eip3085/handle-add-ethereum-chain]}
[{{:networks/keys [networks] :as db} :db :as cofx}
dapp-name id message-id
{:keys [chainId blockExplorerUrls chainName iconUrls nativeCurrency rpcUrls] :as params}]
(let [manage {:name {:value chainName}
:symbol {:value (:symbol nativeCurrency)}
:url {:value (first rpcUrls)}
:network-id {:value chainId}
:chain {:value :custom}}]
(if (network/valid-manage? manage)
(let [{:keys [name url chain network-id symbol]} manage
random-id (string/replace (random/id) "-" "")
network (network/new-network random-id
(:value name)
(:value symbol)
(:value url)
(:value chain)
(:value network-id))
new-networks (assoc networks random-id network)
params (assoc params
:new-networks new-networks
:id id
:new-network network)]
(if (network/chain-id-available? networks network)
{:dispatch [:bottom-sheet/show-sheet-old
{:content (fn []
[sheet/permissions-panel dapp-name message-id params])}]}
(send-success-call-to-bridge cofx id message-id)))
{:browser/send-to-bridge {:type constants/web3-send-async-callback
:messageId message-id
:error {:code -32602
:message "invalid network parameters"}}})))
+3 -3
View File
@@ -45,12 +45,12 @@
(query-fn (comp participant-set :public-key) (vals all-contacts))))
(defn get-all-contacts-in-group-chat
[members admins contacts {:keys [public-key preferred-name name display-name] :as current-account}]
[members admins contacts {:keys [public-key preferred-name name] :as current-account}]
(let [current-contact (some->
current-account
(select-keys [:name :preferred-name :public-key :images :compressed-key])
(select-keys [:name :preferred-name :public-key :images])
(set/rename-keys {:name :alias :preferred-name :name})
(assoc :primary-name (or display-name preferred-name name)))
(assoc :primary-name (or preferred-name name)))
all-contacts (cond-> contacts
current-contact
(assoc public-key current-contact))]
@@ -29,39 +29,37 @@
(testing "transforms messages from RPC response"
(is
(= {:last-message {:quoted-message nil
:outgoing-status nil
:command-parameters nil
:link-previews []
:status-link-previews []
:content {:sticker nil
:rtl? nil
:ens-name nil
:parsed-text nil
:response-to nil
:chat-id nil
:image nil
:line-count nil
:links nil
:text nil}
:outgoing false}
(= {:last-message {:quoted-message nil
:outgoing-status nil
:command-parameters nil
:link-previews []
:content {:sticker nil
:rtl? nil
:ens-name nil
:parsed-text nil
:response-to nil
:chat-id nil
:image nil
:line-count nil
:links nil
:text nil}
:outgoing false}
:message nil
:reply-message {:quoted-message nil
:outgoing-status nil
:command-parameters nil
:link-previews []
:status-link-previews []
:content {:sticker nil
:rtl? nil
:ens-name nil
:parsed-text nil
:response-to nil
:chat-id nil
:image nil
:line-count nil
:links nil
:text nil}
:outgoing false}}
:reply-message {:quoted-message nil
:outgoing-status nil
:command-parameters nil
:link-previews []
:content {:sticker nil
:rtl? nil
:ens-name nil
:parsed-text nil
:response-to nil
:chat-id nil
:image nil
:line-count nil
:links nil
:text nil}
:outgoing false}}
(-> raw-notification
store/<-rpc
(select-keys [:last-message :message :reply-message])))))
+3 -43
View File
@@ -16,51 +16,13 @@
:community-id :communityId
:clock-value :clock})))
(defn- <-status-link-previews-rpc
[preview]
(-> preview
(update :community
set/rename-keys
{:communityId :community-id
:displayName :display-name
:membersCount :members-count
:activeMembersCount :active-members-count})
(update-in [:community :banner] set/rename-keys {:dataUri :data-uri})
(update-in [:community :icon] set/rename-keys {:dataUri :data-uri})
(update-in [:contact :icon] set/rename-keys {:dataUri :data-uri})
(update :contact
set/rename-keys
{:publicKey :public-key
:displayName :display-name})))
(defn ->status-link-previews-rpc
[preview]
(-> preview
(update :community
set/rename-keys
{:community-id :communityId
:display-name :displayName
:members-count :membersCount
:active-members-count :activeMembersCount})
(update-in [:community :banner] set/rename-keys {:data-uri :dataUri})
(update-in [:community :icon] set/rename-keys {:data-uri :dataUri})
(update :contact
set/rename-keys
{:public-key :publicKey
:display-name :displayName})
(update-in [:contact :icon] set/rename-keys {:data-uri :dataUri})))
(defn- <-link-preview-rpc
[preview]
(-> preview
(update :thumbnail set/rename-keys {:dataUri :data-uri})
(update :favicon set/rename-keys {:dataUri :data-uri})))
(update preview :thumbnail set/rename-keys {:dataUri :data-uri}))
(defn ->link-preview-rpc
[preview]
(-> preview
(update :thumbnail set/rename-keys {:data-uri :dataUri})
(update :favicon set/rename-keys {:data-uri :dataUri})))
(update preview :thumbnail set/rename-keys {:data-uri :dataUri}))
(defn <-rpc
[message]
@@ -91,10 +53,8 @@
:new :new?
:albumImagesCount :album-images-count
:displayName :display-name
:linkPreviews :link-previews
:statusLinkPreviews :status-link-previews})
:linkPreviews :link-previews})
(update :link-previews #(map <-link-preview-rpc %))
(update :status-link-previews #(map <-status-link-previews-rpc %))
(update :quoted-message
set/rename-keys
{:parsedText :parsed-text
@@ -37,19 +37,7 @@
:compressed-key "c"
:timestamp 3
:link-previews [{:thumbnail {:url "http://localhost"
:data-uri "data:image/png"}
:favicon nil}]
:status-link-previews [{:url "http://localhost"
:community {:community-id "0x01"
:display-name "Test Comm"
:members-count 12
:active-members-count 12
:banner
{:data-uri
"data:image/png"}
:icon
{:data-uri
"data:image/png"}}}]}
:data-uri "data:image/png"}}]}
message {:id message-id
:whisperTimestamp 1
:parsedText "parsed-text"
@@ -73,17 +61,5 @@
:timestamp 3
:outgoingStatus "sending"
:linkPreviews [{:thumbnail {:url "http://localhost"
:dataUri "data:image/png"}
:favicon nil}]
:statusLinkPreviews [{:url "http://localhost"
:community {:communityId "0x01"
:displayName "Test Comm"
:membersCount 12
:activeMembersCount 12
:banner
{:dataUri
"data:image/png"}
:icon
{:dataUri
"data:image/png"}}}]}]
:dataUri "data:image/png"}}]}]
(is (= expected (m/<-rpc message))))))
+132 -1
View File
@@ -1,6 +1,11 @@
(ns legacy.status-im.network.core
(:require
[clojure.string :as string]))
[clojure.string :as string]
[re-frame.core :as re-frame]
[status-im.navigation.events :as navigation]
[utils.ethereum.chain :as chain]
[utils.i18n :as i18n]
[utils.re-frame :as rf]))
(def url-regex
#"https?://(www\.)?[-a-zA-Z0-9@:%._\+~#=]{2,256}(\.[a-z]{2,6})?\b([-a-zA-Z0-9@:%_\+.~#?&//=]*)")
@@ -9,6 +14,12 @@
[url]
(boolean (re-matches url-regex (str url))))
(def default-manage
{:name {:value ""}
:url {:value ""}
:symbol {:value ""}
:chain {:value :mainnet}})
(defn validate-string
[{:keys [value]}]
{:value value
@@ -45,3 +56,123 @@
[current-networks network]
(let [chain-id (get-in network [:config :NetworkId])]
(every? #(not= chain-id (get-in % [1 :config :NetworkId])) current-networks)))
(defn get-network
[{:keys [db]} network-id]
(get-in db [:networks/networks network-id]))
(rf/defn set-input
{:events [::input-changed]}
[{:keys [db]} input-key value]
{:db (-> db
(update-in [:networks/manage input-key] assoc :value value)
(update-in [:networks/manage] validate-manage))})
;; No edit functionality actually implemented
(rf/defn edit
{:events [::add-network-pressed]}
[{db :db}]
{:db (assoc db :networks/manage (validate-manage default-manage))
:dispatch [:navigate-to :edit-network]})
(rf/defn connect-success
{:events [::connect-success]}
[_ network-id]
{:ui/show-confirmation
{:title (i18n/label :t/close-app-title)
:content (i18n/label :t/logout-app-content)
:confirm-button-text (i18n/label :t/close-app-button)
:on-accept #(re-frame/dispatch [::save-network-settings-pressed network-id])
:on-cancel nil}})
(rf/defn connect-failure
{:events [::connect-failure]}
[_ reason]
{:effects.utils/show-popup
{:title (i18n/label :t/error)
:content (str reason)}})
(rf/defn connect
{:events [::connect-network-pressed]}
[{:keys [db] :as cofx} network-id]
(if (get-in db [:networks/networks network-id :config])
(connect-success cofx network-id)
(connect-failure cofx "A network with the specified id doesn't exist")))
(rf/defn delete
{:events [::delete-network-pressed]}
[{:keys [db]} network]
(let [current-network? (= (:networks/current-network db) network)]
(if (or current-network?
(not (get-in db [:networks/networks network])))
{:ui/show-error (i18n/label :t/delete-network-error)}
{:ui/show-confirmation {:title (i18n/label :t/delete-network-title)
:content (i18n/label :t/delete-network-confirmation)
:confirm-button-text (i18n/label :t/delete)
:on-accept #(re-frame/dispatch [::remove-network-confirmed
network])
:on-cancel nil}})))
(rf/defn save-network-settings
{:events [::save-network-settings-pressed]}
[{:keys [db] :as cofx} network]
{:db (assoc db :networks/current-network network)
:json-rpc/call [{:method "settings_saveSetting"
:params [:networks/current-network network]
:on-success #(re-frame/dispatch [:logout])}]})
(rf/defn remove-network
{:events [::remove-network-confirmed]}
[{:keys [db] :as cofx} network]
(let [networks (dissoc (:networks/networks db) network)]
{:db (assoc db :networks/networks networks)
:json-rpc/call [{:method "settings_saveSetting"
:params [:networks/networks (vals networks)]
:on-success #(re-frame/dispatch [:navigate-back])}]}))
(defn new-network
[random-id network-name sym upstream-url chain-type chain-id]
(let [data-dir (str "/ethereum/" (name chain-type) "_rpc")
config {:NetworkId (or (when chain-id (int chain-id))
(chain/chain-keyword->chain-id chain-type))
:DataDir data-dir
:UpstreamConfig {:Enabled true
:URL upstream-url}}]
{:id random-id
:name network-name
:symbol sym
:config config}))
(rf/defn save
{:events [::save-network-pressed]
:interceptors [(re-frame/inject-cofx :random-id-generator)]}
[{{:networks/keys [manage networks] :as db} :db
random-id-generator :random-id-generator
:as cofx}]
(if (valid-manage? manage)
;; rename network-id from UI to chain-id
(let [{:keys [name url chain network-id symbol]} manage
random-id (string/replace (random-id-generator) "-" "")
network (new-network random-id
(:value name)
(:value symbol)
(:value url)
(:value chain)
(:value network-id))
custom-chain-type? (= :custom (:value chain))
new-networks (assoc networks random-id network)]
(if (or (not custom-chain-type?)
(chain-id-available? networks network))
{:db (-> db
(dissoc :networks/manage)
(assoc :networks/networks new-networks))
:json-rpc/call [{:method "settings_saveSetting"
:params [:networks/networks (vals new-networks)]
:on-success #(re-frame/dispatch [:navigate-back])}]}
{:ui/show-error "chain-id already defined"}))
{:ui/show-error "invalid network parameters"}))
(rf/defn open-network-details
{:events [::network-entry-pressed]}
[cofx network]
(navigation/navigate-to cofx :network-details {:networks/selected-network network}))
+130
View File
@@ -31,6 +31,38 @@
(testing "an https url not on the default port"
(is (network.core/valid-rpc-url? "https://valid.something.else:65323"))))
(deftest new-network-test
(let [actual (network.core/new-network "randomid"
"network-name"
"network-symbol"
"upstream-url"
:mainnet
nil)]
(is (= {:id "randomid"
:name "network-name"
:symbol "network-symbol"
:config {:NetworkId 1
:DataDir "/ethereum/mainnet_rpc"
:UpstreamConfig {:Enabled true
:URL "upstream-url"}}}
actual))))
(deftest new-network-id-test
(let [actual (network.core/new-network "randomid"
"network-name"
"network-symbol"
"upstream-url"
:mainnet
"5777")]
(is (= {:id "randomid"
:name "network-name"
:symbol "network-symbol"
:config {:NetworkId 5777
:DataDir "/ethereum/mainnet_rpc"
:UpstreamConfig {:Enabled true
:URL "upstream-url"}}}
actual))))
(deftest valid-manage-test
(testing "a valid manage"
(is (network.core/valid-manage? {:url {:value "http://valid.com"}
@@ -60,3 +92,101 @@
:name {:value "valid"}
:symbol {:value ""}
:chain {:value "valid"}})))))
(deftest set-input-test
(testing "it updates and validate a field"
(is
(= {:db {:networks/manage {:url {:value "http://valid.com"
:error false}
:name {:value ""
:error true}
:symbol {:value "symbol"
:error false}
:chain {:value "mainnet"
:error false}
:network-id {:value nil
:error false}}}}
(network.core/set-input {:db {:networks/manage {:url {:value "something"
:error true}
:name {:value ""
:error false}
:symbol {:value "symbol"
:error false}
:chain {:value "mainnet"
:error false}}}}
:url
"http://valid.com")))))
(deftest not-save-invalid-url
(testing "it does not save a network with an invalid url"
(is
(:ui/show-error (network.core/save {:random-id-generator (constantly "random")
:db {:networks/manage {:url {:value "wrong"}
:chain {:value "1"}
:name {:value "empty"}}
:profile/profile {}}})))))
(deftest save-valid-network
(testing "save a valid network"
(let [fx (network.core/save
{:random-id-generator (constantly "random-id")
:db {:networks/manage {:url {:value "http://valid.com"}
:chain {:value :mainnet}
:symbol {:value "symbol"}
:name {:value "valid"}}
:profile/profile {}
:networks/networks {"random2"
{:id "random2"
:name "network-name"
:symbol "symbol"
:config {:NetworkId 1
:DataDir "/ethereum/mainnet_rpc"
:UpstreamConfig
{:Enabled true
:URL "upstream-url"}}}}}})]
(is (= "settings_saveSetting" (:method (first (:json-rpc/call fx)))))
(is (nil? (:networks/manage (:db fx))))
(testing "and check that it has an id with `-` and the correct mainnet NetworkId"
(is (= 1 (get-in fx [:db :networks/networks "randomid" :config :NetworkId])))))))
(deftest not-save-custom-chain-with-non-unique-id
(testing "it does not save a custom chain with network-id already defined"
(let [result (network.core/save
{:random-id-generator (constantly "already-defined")
:db {:networks/manage {:url {:value "http://valid.com"}
:chain {:value :custom}
:name {:value "valid"}
:network-id {:value 1}}
:profile/profile {}
:networks/networks {"random"
{:id "random"
:name "network-name"
:config {:NetworkId 1
:DataDir "/ethereum/mainnet_rpc"
:UpstreamConfig
{:Enabled true
:URL "upstream-url"}}}}}})]
(is (:ui/show-error result)))))
(deftest save-valid-network-with-unique-chain-id-check
(testing "save a valid network with chain-id not already defined"
(let [fx (network.core/save
{:random-id-generator (constantly "random")
:db {:networks/manage {:url {:value "http://valid.com"}
:chain {:value :mainnet}
:name {:value "valid"}
:symbol {:value "symbol"}
:network-id {:value 5}}
:profile/profile {}
:networks/networks {"randomid"
{:id "randomid"
:name "network-name"
:symbol "symbol"
:config {:NetworkId 3
:DataDir "/ethereum/mainnet_rpc"
:UpstreamConfig
{:Enabled true
:URL "upstream-url"}}}}}})]
(is (= "settings_saveSetting" (:method (first (:json-rpc/call fx)))))
(is (nil? (:networks/manage (:db fx))))
(is (get-in fx [:db :networks/networks "random"])))))
+39
View File
@@ -0,0 +1,39 @@
(ns legacy.status-im.subs.networks
(:require
[re-frame.core :as re-frame]
[status-im.config :as config]
[utils.ethereum.chain :as chain]))
(defn- filter-networks
[network-type]
(fn [network]
(let [chain-id (chain/network->chain-id network)
testnet? (chain/testnet? chain-id)
custom? (:custom? network)]
(case network-type
:custom custom?
:mainnet (and (not custom?) (not testnet?))
:testnet (and (not custom?) testnet?)))))
(defn- label-networks
[default-networks]
(fn [network]
(let [custom? (not (default-networks (:id network)))]
(assoc network :custom? custom?))))
(re-frame/reg-sub
:get-networks
:<- [:networks/networks]
(fn [networks]
(let [networks (map (label-networks (into #{} (map :id config/default-networks)))
(sort-by :name (vals networks)))
types [:mainnet :testnet :custom]]
(zipmap
types
(map #(filter (filter-networks %) networks) types)))))
(re-frame/reg-sub
:manage-network-valid?
:<- [:networks/manage]
(fn [manage]
(not-any? :error (vals manage))))
+1 -1
View File
@@ -5,6 +5,7 @@
legacy.status-im.subs.ens
legacy.status-im.subs.keycard
legacy.status-im.subs.mailservers
legacy.status-im.subs.networks
legacy.status-im.subs.stickers
[re-frame.core :as re-frame]))
@@ -99,7 +100,6 @@
;;; Link previews
(reg-root-key-sub :link-previews-whitelist :link-previews-whitelist)
(reg-root-key-sub :chat/link-previews :chat/link-previews)
(reg-root-key-sub :chat/status-link-previews :chat/status-link-previews)
;;commands
(reg-root-key-sub :commands/select-account :commands/select-account)
@@ -13,7 +13,8 @@
(re-frame/dispatch event))
(defn- normal-mode-settings-data
[{:keys [current-log-level
[{:keys [network-name
current-log-level
light-client-enabled?
transactions-management-enabled?
current-fleet
@@ -24,6 +25,22 @@
(keep
identity
[{:size :small
:title (i18n/label :t/network)
:accessibility-label :network-button
:container-margin-top 8
:on-press
#(re-frame/dispatch [:open-modal :network-settings])
:accessory :text
:accessory-text network-name
:chevron true}
{:size :small
:title (i18n/label :t/network-info)
:accessibility-label :network-button
:container-margin-top 8
:on-press
#(re-frame/dispatch [:open-modal :network-info])
:chevron true}
{:size :small
:title (i18n/label :t/log-level)
:accessibility-label :log-level-settings-button
:on-press
@@ -134,13 +151,15 @@
is-goerli-enabled? [:profile/is-goerli-enabled?]
light-client-enabled? [:profile/light-client-enabled?]
webview-debug [:profile/webview-debug]
network-name [:network-name]
transactions-management-enabled? [:wallet-legacy/transactions-management-enabled?]
current-log-level [:log-level/current-log-level]
current-fleet [:fleets/current-fleet]
peer-syncing-enabled? [:profile/peer-syncing-enabled?]]
[list/flat-list
{:data (flat-list-data
{:current-log-level current-log-level
{:network-name network-name
:current-log-level current-log-level
:transactions-management-enabled? transactions-management-enabled?
:light-client-enabled? light-client-enabled?
:current-fleet current-fleet
@@ -85,7 +85,9 @@
:accessibility-label :share-community-link
:type :secondary
:on-press #(debounce/throttle-and-dispatch
[::communities/share-community-confirmation-pressed @user-pk
[(if can-invite?
::communities/invite-people-confirmation-pressed
::communities/share-community-confirmation-pressed) @user-pk
selected]
3000)}
(i18n/label (if can-invite? :t/invite :t/share))]}]]))))
@@ -0,0 +1,12 @@
(ns legacy.status-im.ui.screens.network.edit-network.styles)
(def edit-network-view
{:flex 1
:margin-horizontal 16
:margin-vertical 15})
(def bottom-container
{:flex-direction :row
:padding-vertical 4})
(def container {:flex 1})
@@ -0,0 +1,72 @@
(ns legacy.status-im.ui.screens.network.edit-network.views
(:require
[legacy.status-im.network.core :as network]
[legacy.status-im.ui.components.core :as quo]
[legacy.status-im.ui.components.list.item :as list.item]
[legacy.status-im.ui.components.list.views :as list]
[legacy.status-im.ui.components.react :as react]
[legacy.status-im.ui.screens.network.edit-network.styles :as styles]
[re-frame.core :as re-frame]
[utils.i18n :as i18n])
(:require-macros [legacy.status-im.utils.views :as views]))
(defn- render-network-type
[type _ _ manage-network]
(let [name (case type
:mainnet (i18n/label :t/mainnet-network)
:goerli (i18n/label :t/goerli-network)
:custom (i18n/label :t/custom))]
[list.item/list-item
{:title name
:accessory :radio
:active (= (get-in manage-network [:chain :value]) type)
:on-press #(re-frame/dispatch [::network/input-changed :chain type])}]))
(views/defview edit-network
[]
(views/letsubs [manage-network [:networks/manage]
is-valid? [:manage-network-valid?]]
(let [custom? (= (get-in manage-network [:chain :value]) :custom)]
[react/keyboard-avoiding-view {:flex 1}
[react/scroll-view
[react/view styles/edit-network-view
[react/view {:padding-vertical 8}
[quo/text-input
{:label (i18n/label :t/name)
:placeholder (i18n/label :t/specify-name)
:default-value (get-in manage-network [:name :value])
:on-change-text #(re-frame/dispatch [::network/input-changed :name %])
:auto-focus true}]]
[react/view {:padding-vertical 8}
[quo/text-input
{:label (i18n/label :t/symbol)
:placeholder (i18n/label :t/specify-symbol)
:default-value (get-in manage-network [:symbol :value])
:on-change-text #(re-frame/dispatch [::network/input-changed :symbol %])
:auto-focus true}]]
[react/view {:padding-vertical 8}
[quo/text-input
{:label (i18n/label :t/rpc-url)
:placeholder (i18n/label :t/specify-rpc-url)
:default-value (get-in manage-network [:url :value])
:on-change-text #(re-frame/dispatch [::network/input-changed :url %])}]]]
[quo/list-header (i18n/label :t/network-chain)]
[list/flat-list
{:data [:mainnet :goerli :custom]
:key-fn (fn [_ i] (str i))
:render-data manage-network
:render-fn render-network-type}]
(when custom?
[react/view styles/edit-network-view
[quo/text-input
{:label (i18n/label :t/network-id)
:placeholder (i18n/label :t/specify-network-id)
:on-change-text #(re-frame/dispatch [::network/input-changed :network-id %])}]])]
[react/view styles/bottom-container
[react/view {:flex 1}]
[quo/button
{:after :main-icons/next
:type :secondary
:disabled (not is-valid?)
:on-press #(re-frame/dispatch [::network/save-network-pressed])}
(i18n/label :t/save)]]])))
@@ -0,0 +1,47 @@
(ns legacy.status-im.ui.screens.network.network-details.views
(:require
[legacy.status-im.network.core :as network]
[legacy.status-im.ui.components.core :as quo]
[legacy.status-im.ui.components.react :as react]
[legacy.status-im.ui.screens.network.styles :as st]
[legacy.status-im.ui.screens.network.views :as network-settings]
[re-frame.core :as re-frame]
[utils.debounce :refer [throttle-and-dispatch]]
[utils.i18n :as i18n])
(:require-macros [legacy.status-im.utils.views :as views]))
(views/defview network-details
[]
(views/letsubs [{:keys [networks/selected-network]} [:get-screen-params]
current-network [:networks/current-network]
networks [:get-networks]]
(let [{:keys [id name config]} selected-network
connected? (= id current-network)
custom? (seq (filter #(= (:id %) id) (:custom networks)))]
[:<>
[react/view {:flex 1}
[network-settings/network-badge
{:name name
:connected? connected?}]
(when-not connected?
[react/touchable-highlight
{:on-press #(throttle-and-dispatch [::network/connect-network-pressed id] 1000)}
[react/view st/connect-button-container
[react/view
{:style st/connect-button
:accessibility-label :network-connect-button}
[react/text {:style st/connect-button-label}
(i18n/label :t/connect)]]
[react/i18n-text
{:style st/connect-button-description
:key :connecting-requires-login}]]])
[react/view (st/network-config-container)
[react/text
{:style st/network-config-text
:accessibility-label :network-details-text}
config]]]
(when custom?
[react/view st/bottom-container
[react/view {:flex 1}
[quo/button {:on-press #(re-frame/dispatch [::network/delete-network-pressed id])}
(i18n/label :t/delete)]]])])))
@@ -0,0 +1,90 @@
(ns legacy.status-im.ui.screens.network.styles
(:require
[legacy.status-im.ui.components.colors :as colors]
[legacy.status-im.utils.styles :as styles]))
(def wrapper
{:flex 1})
(def badge-name-text
{:font-size 17})
(styles/def badge-connected-text
{:color colors/gray
:ios {:margin-top 5}})
(def connect-button-container
{:margin-top 8
:margin-bottom 16
:margin-horizontal 16})
(def connect-button
{:height 52
:align-items :center
:justify-content :center
:background-color colors/blue
:border-radius 8
:ios {:opacity 0.9}})
(def connect-button-label
{:color colors/white-persist
:font-size 17})
(def connect-button-description
{:color colors/gray
:margin-top 8
:height 20})
(styles/defn network-config-container
[]
{:height 160
:margin-top 8
:padding-top 16
:padding-left 16
:margin-horizontal 16
:background-color colors/gray-lighter
:ios {:border-radius 9
:opacity 0.9}
:android {:border-radius 4}})
(styles/def network-config-text
{:font-size 17
:ios {:opacity 0.8}
:android {:opacity 0.4}})
(defn network-icon
[connected? size]
{:width size
:height size
:border-radius (/ size 2)
:background-color (if connected? colors/blue colors/gray-lighter)
:align-items :center
:justify-content :center})
(def network-badge
{:height 88
:padding-left 16
:flex-direction :row
:align-items :center})
(styles/def network-item
{:flex-direction :row
:align-items :center
:padding-horizontal 16
:ios {:height 64}
:android {:height 56}})
(def network-item-name-text
{:font-size 17})
(def network-item-connected-text
{:color colors/gray
:font-size 14
:margin-top 6})
(def bottom-container
{:flex-direction :row
:margin-horizontal 12
:margin-vertical 15})
(def container {:flex 1})
@@ -0,0 +1,71 @@
(ns legacy.status-im.ui.screens.network.views
(:require
[legacy.status-im.network.core :as network]
[legacy.status-im.ui.components.colors :as colors]
[legacy.status-im.ui.components.icons.icons :as icons]
[legacy.status-im.ui.components.list.item :as list.item]
[legacy.status-im.ui.components.list.views :as list]
[legacy.status-im.ui.components.react :as react]
[legacy.status-im.ui.components.topbar :as topbar]
[legacy.status-im.ui.screens.network.styles :as styles]
[re-frame.core :as re-frame]
[utils.i18n :as i18n])
(:require-macros [legacy.status-im.utils.views :as views]))
(defn- network-icon
[connected? size]
[react/view (styles/network-icon connected? size)
[icons/icon :main-icons/network {:color (if connected? colors/white-persist colors/gray)}]])
(defn network-badge
[& [{:keys [name connected?]}]]
[react/view styles/network-badge
[network-icon connected? 56]
[react/view {:padding-left 16}
[react/text {:style styles/badge-name-text}
(or name (i18n/label :t/new-network))]
(when connected?
[react/i18n-text
{:style styles/badge-connected-text
:key :connected}])]])
(def mainnet?
#{"mainnet" "mainnet_rpc"})
(defn render-network
[{:keys [id name] :as network} _ _ current-network]
(let [connected? (= id current-network)]
[list.item/list-item
{:on-press #(re-frame/dispatch [::network/network-entry-pressed network])
:icon :main-icons/network
:icon-bg-color (if connected? colors/blue colors/gray-lighter)
:icon-color (if connected? colors/white-persist colors/gray)
:title name
:subtitle (when connected? (i18n/label :t/connected))
:chevron true}]))
(views/defview network-settings
[]
(views/letsubs [current-network [:networks/current-network]
networks [:get-networks]]
[:<>
[topbar/topbar
{:title (i18n/label :t/network-settings)
:right-accessories
[{:icon :main-icons/add
:on-press #(re-frame/dispatch [::network/add-network-pressed])}]}]
[react/view styles/wrapper
[list/section-list
{:sections [{:title (i18n/label :t/main-networks)
:key :mainnet
:data (:mainnet networks)}
{:title (i18n/label :t/test-networks)
:key :testnet
:data (:testnet networks)}
{:title (i18n/label :t/custom-networks)
:key :custom
:data (:custom networks)}]
:key-fn :id
:default-separator? true
:render-data current-network
:render-fn render-network}]]]))
@@ -0,0 +1,84 @@
(ns legacy.status-im.ui.screens.network-info.views
(:require
[legacy.status-im.ethereum.decode :as decode]
[legacy.status-im.ui.components.react :as react]
[native-module.core :as native-module]
[re-frame.core :as re-frame]
[reagent.core :as reagent]
[status-im.common.json-rpc.events :as json-rpc]
[utils.datetime :as datetime]))
(defn get-block
[block callback]
(json-rpc/call
{:method "eth_getBlockByNumber"
:params [block false]
:on-success callback
:on-error #(js/alert (str "can't fetch latest block" %))}))
(defn to-date
[timestamp]
(datetime/timestamp->long-date
(* 1000 timestamp)))
(defn check-lag
[]
(let [latest-block (reagent/atom nil)
last-loaded-block (reagent/atom nil)
on-press
(fn []
(get-block
"latest"
(fn [res]
(reset! latest-block res)
(get-block
(str "0x"
(native-module/number-to-hex
@(re-frame/subscribe [:ethereum/current-block])))
(fn [res]
(reset! last-loaded-block res))))))]
(fn []
[react/view
{:style {:flex 1
:margin-horizontal 16}}
(if-not @latest-block
[react/text
{:on-press on-press}
"PRESS TO REFRESH"]
[react/text
{:on-press on-press}
(let [latest-block-number
(decode/uint (:number @latest-block))
latest-block-timestamp
(decode/uint (:timestamp @latest-block))
last-loaded-block-number
(decode/uint (:number @last-loaded-block))
last-loaded-block-timestamp
(decode/uint (:timestamp @last-loaded-block))]
(str "Latest block number: "
latest-block-number
"\n"
"Latest block time: "
(to-date latest-block-timestamp)
"\n"
"Last loaded block: "
last-loaded-block-number
"\n"
"Last loaded block time: "
(to-date last-loaded-block-timestamp)
"\n"
"Seconds diff: "
(- latest-block-timestamp
last-loaded-block-timestamp)
"\n"
"Blocks diff: " (- latest-block-number
last-loaded-block-number)
"\n"
"PRESS TO REFRESH"))])])))
(defn network-info
[]
[check-lag])
@@ -24,6 +24,10 @@
[legacy.status-im.ui.screens.link-previews-settings.views :as link-previews-settings]
[legacy.status-im.ui.screens.log-level-settings.views :as log-level-settings]
[legacy.status-im.ui.screens.mobile-network-settings.view :as mobile-network-settings]
[legacy.status-im.ui.screens.network-info.views :as network-info]
[legacy.status-im.ui.screens.network.edit-network.views :as edit-network]
[legacy.status-im.ui.screens.network.network-details.views :as network-details]
[legacy.status-im.ui.screens.network.views :as network]
[legacy.status-im.ui.screens.notifications-settings.views :as notifications-settings]
[legacy.status-im.ui.screens.offline-messaging-settings.edit-mailserver.views :as edit-mailserver]
[legacy.status-im.ui.screens.offline-messaging-settings.views :as offline-messaging-settings]
@@ -223,6 +227,18 @@
;;TODO dynamic title
:options {:insets {:top? true}}
:component dapps-permissions/manage}
{:name :network-settings
;;TODO accessories
:options {:insets {:top? true}}
:component network/network-settings}
{:name :network-details
:options {:topBar {:title {:text (i18n/label :t/network-details)}}
:insets {:top? true}}
:component network-details/network-details}
{:name :network-info
:options {:topBar {:title {:text (i18n/label :t/network-info)}}
:insets {:top? true}}
:component network-info/network-info}
{:name :rpc-usage-info
:options {:topBar {:title {:text (i18n/label :t/rpc-usage-info)}}
:insets {:top? true}}
@@ -231,6 +247,10 @@
:options {:topBar {:title {:text (i18n/label :t/peers-stats)}}
:insets {:top? true}}
:component peers-stats/peers-stats}
{:name :edit-network
:options {:topBar {:title {:text (i18n/label :t/add-network)}}
:insets {:top? true}}
:component edit-network/edit-network}
{:name :log-level-settings
:options {:topBar {:title {:text (i18n/label :t/log-level-settings)}}
:insets {:top? true}}
@@ -20,8 +20,8 @@
[text/text {:size :paragraph-1 :weight :medium :number-of-lines 2} label]]])
(defn channel-actions
[{:keys [container-style actions]}]
[rn/view {:style (assoc container-style :flex-direction :row)}
[{:keys [style actions]}]
[rn/view {:style (merge {:flex-direction :row :flex 1} style)}
(map-indexed
(fn [index action]
^{:key index}
+1
View File
@@ -142,6 +142,7 @@
[color]
{:padding 12
:height 102
:flex 1
:border-radius 16
:background-color (colors/custom-color color 50 10)
:justify-content :space-between})
@@ -76,8 +76,7 @@
(str description " · " (i18n/label :t/on-device))])
(defn- context-tag-subtitle
[{:keys [context-tag-type community-logo community-name account-name emoji customization-color
full-name profile-picture]}]
[{:keys [context-tag-type community-logo community-name account-name emoji customization-color]}]
(let [tag-type (or context-tag-type :account)]
[rn/view
{:accessibility-label :context-tag-wrapper
@@ -89,9 +88,7 @@
:community-name community-name
:community-logo community-logo
:size 24
:customization-color customization-color
:profile-picture profile-picture
:full-name full-name}]]))
:customization-color customization-color}]]))
(defn- description-subtitle
[{:keys [theme blur? description]}]
@@ -103,7 +100,7 @@
(defn- subtitle
[{:keys [type theme blur? keycard? networks description community-name community-logo
context-tag-type account-name emoji customization-color full-name profile-picture]}]
context-tag-type account-name emoji customization-color]}]
(cond
(= :keypair type)
[keypair-subtitle
@@ -131,9 +128,7 @@
:community-name community-name
:account-name account-name
:emoji emoji
:customization-color customization-color
:profile-picture profile-picture
:full-name full-name}]
:customization-color customization-color}]
(and (not= :label type) description)
[description-subtitle
@@ -192,7 +187,7 @@
on-button-press
on-button-long-press
button-disabled? account-avatar-emoji account-avatar-type customization-color icon-avatar
profile-picture keycard? networks label full-name]}]
profile-picture keycard? networks label]}]
[rn/view {:style style/container}
(when (left-image-supported-types type)
[rn/view {:style style/left-container}
@@ -223,9 +218,7 @@
:context-tag-type context-tag-type
:customization-color customization-color
:account-name account-name
:emoji emoji
:full-name full-name
:profile-picture profile-picture}]]
:emoji emoji}]]
[right-icon
{:theme theme
:type type
@@ -2,10 +2,5 @@
(defn root-container
[opacity height]
{:height (or height 252)
:opacity opacity
:position :absolute
:top 0
:left 0
:right 0
:z-index -1})
{:height (or height 252)
:opacity opacity})
+3 -4
View File
@@ -55,17 +55,16 @@
(def ^:private custom-props
"Custom properties that must be removed from properties map passed to InputText."
[:type :blur? :error? :right-icon :left-icon :disabled? :small? :button
[:type :blur? :theme :error? :right-icon :left-icon :disabled? :small? :button
:label :char-limit :on-char-limit-reach :icon-name :multiline? :on-focus :on-blur
:container-style])
(defn- base-input
[{:keys [blur? error? right-icon left-icon disabled? small? button
[{:keys [blur? theme error? right-icon left-icon disabled? small? button
label char-limit multiline? clearable? on-focus on-blur container-style
on-change-text on-char-limit-reach weight default-value]
:as props}]
(let [theme (quo.theme/use-theme-value)
[status set-status] (rn/use-state :default)
(let [[status set-status] (rn/use-state :default)
internal-on-focus (rn/use-callback #(set-status :focus))
internal-on-blur (rn/use-callback #(set-status :default))
[multiple-lines?
@@ -60,8 +60,8 @@
:padding-horizontal 12
:padding-top 10
:padding-bottom 12
:width (if (= :message size) 295 335)
:flex 1})
:height (if (= :message size) 245 266)
:width (if (= :message size) 295 335)})
(def header-container
{:flex-direction :row
@@ -7,6 +7,7 @@
{:title "Some title"
:description "Some description"
:link "status.im"
:logo "data:image/png,logo-x"
:thumbnail "data:image/png,whatever"})
(h/describe "Links - Link Preview"
@@ -20,12 +21,17 @@
(h/is-truthy (h/query-by-text (:title props)))
(h/is-truthy (h/query-by-text (:description props)))
(h/is-truthy (h/query-by-text (:link props)))
(h/is-truthy (h/query-by-label-text :logo))
(h/is-truthy (h/query-by-label-text :thumbnail)))
(h/test "does not render thumbnail if prop is not present"
(h/render [view/view (dissoc props :thumbnail)])
(h/is-null (h/query-by-label-text :thumbnail)))
(h/test "does not render logo if prop is not present"
(h/render [view/view (dissoc props :logo)])
(h/is-null (h/query-by-label-text :logo)))
(h/test "shows button to enable preview when preview is disabled"
(h/render [view/view
(assoc props
@@ -1,15 +1,9 @@
(ns quo.components.links.link-preview.view
(:require
["react-native-blob-util" :default ReactNativeBlobUtil]
[oops.core :as oops]
[quo.components.buttons.button.view :as button]
[quo.components.links.link-preview.style :as style]
[quo.components.markdown.text :as text]
[react-native.core :as rn]
[react-native.platform :as platform]
[react-native.svg :as svg]
[reagent.core :as reagent]
[taoensso.timbre :as log]))
[react-native.core :as rn]))
(defn- button-disabled
[disabled-text on-enable]
@@ -59,25 +53,12 @@
(defn- logo-comp
[logo]
(let [image-data (reagent/atom nil)
is-svg? (reagent/atom nil)
on-success (fn [data-uri]
(reset! image-data data-uri))
_get-svg (-> (.config ReactNativeBlobUtil (clj->js {:trusty platform/ios?}))
(.fetch "GET" logo)
(.then (fn [imgObj]
(reset! is-svg? (= "image/svg"
(oops/oget imgObj
["respInfo" "headers" "Content-Type"])))
(on-success (oops/oget imgObj "data"))))
(.catch #(log/error "could not fetch favicon " logo)))]
(fn []
(if @is-svg?
[svg/svg-xml (merge style/logo {:xml @image-data})]
[rn/image
{:accessibility-label :logo
:source {:uri (str "data:image/png;base64," @image-data)}
:style style/logo}]))))
[rn/image
{:accessibility-label :logo
:source (if (string? logo)
{:uri logo}
logo)
:style style/logo}])
(defn view
[{:keys [title logo description link thumbnail
@@ -21,7 +21,6 @@
:border-radius 12
:border-style :dashed
:align-items :center
:flex-direction :row
:justify-content :center
:align-self :stretch
:padding horizontal-padding
+8 -27
View File
@@ -1,31 +1,19 @@
(ns quo.components.links.url-preview.view
(:require
[clojure.string :as string]
[quo.components.icon :as icon]
[quo.components.links.url-preview.style :as style]
[quo.components.markdown.text :as text]
[quo.foundations.colors :as colors]
[react-native.core :as rn]
[react-native.svg :as svg]))
(def base64-svg-prefix "data:image/svg;base64,")
[react-native.core :as rn]))
(defn- logo-comp
[{:keys [logo]}]
(if (and (string? logo)
(string/starts-with? logo base64-svg-prefix))
[svg/svg-xml
(merge style/logo
{:xml (-> logo
(string/replace base64-svg-prefix "")
js/atob)})]
[rn/image
{:accessibility-label :logo
:source (if (string? logo)
{:uri logo}
logo)
:style style/logo
:resize-mode :cover}]))
[rn/image
{:accessibility-label :logo
:source (if (string? logo)
{:uri logo}
logo)
:style style/logo}])
(defn- content
[{:keys [title body]}]
@@ -62,10 +50,6 @@
[rn/view
{:accessibility-label :url-preview-loading
:style (merge (style/loading-container) container-style)}
[icon/icon :i/loading
{:size 12
:color (colors/theme-colors colors/neutral-50 colors/neutral-40)
:container-style {:margin-right 8}}]
[rn/text
{:size :paragraph-2
:weight :medium
@@ -76,9 +60,6 @@
{:accessibility-label :url-preview
:style (merge (style/container) container-style)}
(when logo
[logo-comp
{:logo (if (map? logo)
(:data-uri logo)
logo)}])
[logo-comp {:logo logo}])
[content {:title title :body body}]
[clear-button {:on-press on-clear}]]))
@@ -32,7 +32,7 @@
[rn/view {:style style/url-preview-separator}])
(defn- item-component
[{:keys [title body loading? logo url]} _ _
[{:keys [title body loading? logo]} _ _
{:keys [width on-clear loading-message container-style]}]
[url-preview/view
{:logo logo
@@ -41,8 +41,7 @@
:loading? loading?
:loading-message loading-message
:on-clear on-clear
:container-style (merge container-style {:width width})
:url url}])
:container-style (merge container-style {:width width})}])
(defn- f-view
[]
@@ -35,7 +35,7 @@
:on-press on-press
:background (if behind-overlay?
:blur
(when (button-properties/backgrounds background) background))
(button-properties/backgrounds background))
:accessibility-label accessibility-label}
icon-name])]
children))
@@ -1,9 +1,9 @@
(ns quo.components.profile.collectible.view
(:require
[clojure.string :as string]
[quo.components.markdown.text :as text]
[quo.components.profile.collectible.style :as style]
[react-native.core :as rn]))
[react-native.core :as rn]
[react-native.svg :as svg]))
(defn remaining-tiles
[amount]
@@ -19,28 +19,11 @@
(let [svg? (and (map? resource) (:svg? resource))
image-style (style/tile-style-by-size size)]
[rn/view {:style style}
(cond
svg?
(if svg?
[rn/view
{:style (assoc image-style
:border-radius (:border-radius image-style)
:overflow :hidden
:justify-content :center
:align-items :center
:background-color :lightblue)}
[text/text "SVG Content"]]
(or (string/blank? resource) (string/blank? (:uri resource)))
[rn/view
{:style (assoc image-style
:border-radius (:border-radius image-style)
:overflow :hidden
:justify-content :center
:align-items :center
:background-color :lightgray)}
[text/text "Missing image"]]
:else
{:style {:border-radius (:border-radius image-style)
:overflow :hidden}}
[svg/svg-uri (assoc image-style :uri (:uri resource))]]
;; NOTE: using react-native-fast-image here causes a crash on devices when used inside a
;; large flatlist. The library seems to have issues with memory consumption when used with
;; large images/GIFs.
@@ -150,7 +150,8 @@
profile-picture emoji on-share-press address]
:as props}]
[:<>
[gradient-cover/view {:customization-color customization-color :height 463}]
[rn/view {:style style/gradient-bg}
[gradient-cover/view {:customization-color customization-color :height 463}]]
[rn/view {:style style/content-container}
[rn/view {:style style/share-qr-container}
[rn/view {:style style/share-qr-inner-container}
+4 -9
View File
@@ -25,15 +25,10 @@
(defn image
[{:keys [source] :as props}]
(let [props (cond-> props
platform/ios?
(dissoc :resize-method)
(and (:style props) platform/ios?)
(update :style dissoc :resize-method))]
[image-native
(if (string? source)
(assoc props :source {:uri source})
props)]))
[image-native
(if (string? source)
(assoc props :source {:uri source})
props)])
(defn image-get-size
[uri]
+4 -3
View File
@@ -132,9 +132,10 @@
:shell? shell?
:padding-bottom content-padding-bottom})}
(when (and gradient-cover? customization-color)
[quo/gradient-cover
{:customization-color customization-color
:opacity 0.4}])
[rn/view {:style style/gradient-bg}
[quo/gradient-cover
{:customization-color customization-color
:opacity 0.4}]])
(when-not hide-handle?
[quo/drawer-bar])
[content]]]]]))
+2 -11
View File
@@ -29,7 +29,7 @@
(defn validation-name
[s]
(cond
(string/blank? s) nil
(or (= s nil) (= s "")) nil
(string/ends-with? s "-eth") (i18n/label :t/ending-not-allowed {:ending "-eth"})
(string/ends-with? s "_eth") (i18n/label :t/ending-not-allowed {:ending "_eth"})
(string/ends-with? s ".eth") (i18n/label :t/ending-not-allowed {:ending ".eth"})
@@ -45,17 +45,8 @@
(defn validation-bio
[s]
(cond
(string/blank? s) nil
(or (= s nil) (= s "")) nil
(has-emojis? s) (i18n/label :t/are-not-allowed {:check (i18n/label :t/emojis)})
(has-special-characters? s) (i18n/label :t/are-not-allowed
{:check (i18n/label :t/special-characters)})
(bio-too-long? s) (i18n/label :t/bio-is-too-long)))
(defn validation-nickname
[s]
(cond
(string/blank? s) nil
(has-emojis? s) (i18n/label :t/are-not-allowed {:check (i18n/label :t/emojis)})
(has-special-characters? s) (i18n/label :t/are-not-allowed
{:check (i18n/label :t/special-characters)})
(name-too-long? s) (i18n/label :t/nickname-is-too-long)))
-5
View File
@@ -425,9 +425,6 @@
(def ^:const optimism-goerli-chain-id 420)
(def ^:const optimism-sepolia-chain-id 11155420)
(def ^:const mainnet-chain-ids
#{ethereum-mainnet-chain-id arbitrum-mainnet-chain-id optimism-mainnet-chain-id})
(def ^:const mainnet-short-name "eth")
(def ^:const optimism-short-name "opt")
(def ^:const arbitrum-short-name "arb1")
@@ -465,5 +462,3 @@
(def ^:const bridge-name-transfer "Transfer")
(def ^:const bridge-name-erc-721-transfer "ERC721Transfer")
(def ^:const status-hostname "status.app")
@@ -2,9 +2,11 @@
(:require
[quo.foundations.colors :as colors]))
(def actions-view
{:margin-top 8
:margin-bottom 20
(defn actions-view
[]
{:flex-direction :row
:justify-content :space-between
:margin-vertical 20
:padding-horizontal 20})
(defn action-container
@@ -12,6 +12,53 @@
[utils.i18n :as i18n]
[utils.re-frame :as rf]))
(defn back-button
[]
[quo/button
{:type :grey
:size 32
:icon-only? true
:container-style {:margin-left 20}
:accessibility-label :back-button
:on-press #(rf/dispatch [:navigate-back])}
:i/arrow-left])
(defn options-button
[group]
[quo/button
{:type :grey
:size 32
:icon-only? true
:container-style {:margin-right 20}
:accessibility-label :options-button
:on-press #(rf/dispatch [:show-bottom-sheet
{:content (fn [] [actions/group-details-actions group])}])}
:i/options])
(defn count-container
[amount accessibility-label]
[rn/view
{:style (style/count-container)
:accessibility-label accessibility-label}
[quo/text
{:size :label
:weight :medium
:style {:text-align :center}}
amount]])
(defn contacts-section-header
[{:keys [title]}]
[rn/view
{:style {:padding-horizontal 20
:border-top-width 1
:border-top-color colors/neutral-20
:padding-vertical 8
:margin-top 8}}
[quo/text
{:size :paragraph-2
:weight :medium
:style {:color (colors/theme-colors colors/neutral-50 colors/neutral-40)}} title]])
(defn group-chat-member-toggle
[member? selected? public-key]
(if-not member?
@@ -96,74 +143,79 @@
:on-press show-profile-actions}})
item]))
(defn contacts-section-header
[{:keys [title]}]
[quo/divider-label {:tight? true} title])
(defn contacts-section-footer
[_]
[rn/view {:style {:height 8}}])
(defn group-details
[]
(let [chat-id (rf/sub [:get-screen-params :group-chat-profile])
{:keys [admins chat-id chat-name color muted contacts]
{:keys [admins chat-id chat-name color public?
muted contacts]
:as group} (rf/sub [:chats/chat-by-id chat-id])
members (rf/sub [:contacts/group-members-sections chat-id])
pinned-messages (rf/sub [:chats/pinned chat-id])
current-pk (rf/sub [:multiaccount/public-key])
admin? (get admins current-pk)]
[:<>
[quo/gradient-cover
{:height 286
:customization-color color}]
[quo/page-nav
{:type :no-title
:background :photo
:right-side [{:icon-name :i/options
:on-press #(rf/dispatch [:show-bottom-sheet
{:content (fn [] [actions/group-details-actions
group])}])}]
:icon-name :i/arrow-left
:on-press #(rf/dispatch [:navigate-back])}]
[quo/page-top
{:title chat-name
:avatar {:customization-color color}}]
[quo/channel-actions
{:container-style style/actions-view
:actions [{:accessibility-label :pinned-messages
:label (i18n/label :t/pinned-messages)
:color color
:icon :i/pin
:counter-value (count pinned-messages)
:on-press (fn []
(rf/dispatch [:dismiss-keyboard])
(rf/dispatch [:pin-message/show-pins-bottom-sheet
chat-id]))}
{:accessibility-label :toggle-mute
:color color
:icon (if muted :i/muted :i/activity-center)
:label (i18n/label (if muted :unmute-group :mute-group))
:on-press #(rf/dispatch [:chat.ui/mute chat-id (not muted)
(when-not muted
constants/mute-till-unmuted)])}
{:accessibility-label :manage-members
:color color
:icon :i/add-user
:label (i18n/label (if admin? :t/manage-members :t/add-members))
:counter-value (count contacts)
:on-press (fn []
(rf/dispatch [:group/clear-added-participants])
(rf/dispatch [:group/clear-removed-members])
(rf/dispatch [:open-modal :group-add-manage-members
chat-id]))}]}]
[rn/view
{:style {:flex 1
:background-color (colors/theme-colors colors/white colors/neutral-95)}}
[quo/header
{:left-component [back-button]
:right-component [options-button group]
:background (colors/theme-colors colors/white colors/neutral-95)}]
[rn/view
{:style {:flex-direction :row
:margin-top 24
:padding-horizontal 20}}
[quo/group-avatar
{:customization-color color
:size :size-32}]
[quo/text
{:weight :semi-bold
:size :heading-1
:style {:margin-horizontal 8}} chat-name]
[rn/view {:style {:margin-top 8}}
[quo/icon (if public? :i/world :i/privacy)
{:size 20 :color (colors/theme-colors colors/neutral-50 colors/neutral-40)}]]]
[rn/view {:style (style/actions-view)}
[rn/touchable-opacity
{:style (style/action-container color)
:accessibility-label :pinned-messages
:on-press (fn []
(rf/dispatch [:dismiss-keyboard])
(rf/dispatch [:pin-message/show-pins-bottom-sheet chat-id]))}
[rn/view
{:style {:flex-direction :row
:justify-content :space-between}}
[quo/icon :i/pin {:size 20 :color (colors/theme-colors colors/neutral-100 colors/white)}]
[count-container (count pinned-messages) :pinned-count]]
[quo/text {:style {:margin-top 16} :size :paragraph-1 :weight :medium}
(i18n/label :t/pinned-messages)]]
[rn/touchable-opacity
{:style (style/action-container color)
:accessibility-label :toggle-mute
:on-press #(rf/dispatch [:chat.ui/mute chat-id (not muted)
(when-not muted constants/mute-till-unmuted)])}
[quo/icon (if muted :i/muted :i/activity-center)
{:size 20 :color (colors/theme-colors colors/neutral-100 colors/white)}]
[quo/text {:style {:margin-top 16} :size :paragraph-1 :weight :medium}
(i18n/label (if muted :unmute-group :mute-group))]]
[rn/touchable-opacity
{:style (style/action-container color)
:accessibility-label :manage-members
:on-press (fn []
(rf/dispatch [:group/clear-added-participants])
(rf/dispatch [:group/clear-removed-members])
(rf/dispatch [:open-modal :group-add-manage-members chat-id]))}
[rn/view
{:style {:flex-direction :row
:justify-content :space-between}}
[quo/icon :i/add-user {:size 20 :color (colors/theme-colors colors/neutral-100 colors/white)}]
[count-container (count contacts) :members-count]]
[quo/text {:style {:margin-top 16} :size :paragraph-1 :weight :medium}
(i18n/label (if admin? :t/manage-members :t/add-members))]]]
[rn/section-list
{:key-fn :title
:sticky-section-headers-enabled false
:sections members
:render-section-header-fn contacts-section-header
:render-section-footer-fn contacts-section-footer
:render-data {:chat-id chat-id
:admin? admin?}
:render-fn contact-item-render}]]))
@@ -1,7 +1,7 @@
(ns status-im.contexts.chat.messenger.composer.events
(:require [clojure.string :as string]
[legacy.status-im.chat.models.mentions :as mentions]
[legacy.status-im.data-store.messages :as data-store.messages]
[legacy.status-im.data-store.messages :as data-store-messages]
[status-im.constants :as constants]
[status-im.contexts.chat.messenger.composer.link-preview.events :as link-preview]
[status-im.contexts.chat.messenger.messages.transport.events :as messages.transport]
@@ -132,43 +132,6 @@
:response-to message-id})
images)))
(defn- build-status-link-previews
[status-link-previews]
(map (fn [{{:keys [community-id color description display-name
members-count active-members-count icon banner]} :community
url :url
{contact-description :description
contact-icon :icon
contact-display-name :display-name
pk :public-key} :contact}]
{:url url
:community {:community-id community-id
:color color
:description description
:display-name display-name
:members-count members-count
:active-members-count active-members-count
:icon {:data-uri (:data-uri icon)
:width (:width icon)
:height (:height icon)}
:banner {:data-uri (:data-uri banner)
:width (:width banner)
:height (:height banner)}}
:contact {:public-key pk
:display-name contact-display-name
:description contact-description
:icon {:data-uri (:data-uri contact-icon)}}})
status-link-previews))
(defn- build-link-previews
[link-previews]
(->> link-previews
(map #(select-keys %
[:url :title :description :thumbnail
:status-link-preview? :favicon]))
(filter (fn [{:keys [status-link-preview?]}]
(not status-link-preview?)))))
(defn build-text-message
[{:keys [db]} input-text current-chat-id]
(when-not (string/blank? input-text)
@@ -177,16 +140,15 @@
preferred-name (get-in db [:profile/profile :preferred-name])
emoji? (emoji-only-content? {:text input-text
:response-to message-id})]
{:chat-id current-chat-id
:content-type (if emoji?
constants/content-type-emoji
constants/content-type-text)
:text input-text
:response-to message-id
:ens-name preferred-name
:link-previews (build-link-previews (get-in db [:chat/link-previews :unfurled]))
:status-link-previews (build-status-link-previews
(get-in db [:chat/status-link-previews :unfurled]))})))
{:chat-id current-chat-id
:content-type (if emoji?
constants/content-type-emoji
constants/content-type-text)
:text input-text
:response-to message-id
:ens-name preferred-name
:link-previews (map #(select-keys % [:url :title :description :thumbnail])
(get-in db [:chat/link-previews :unfurled]))})))
(rf/defn send-messages
[{:keys [db] :as cofx} input-text current-chat-id]
@@ -219,41 +181,29 @@
(when message-id
{:response-to message-id}))])))))
(defn- process-link-previews
[link-previews]
(->> link-previews
(map #(select-keys %
[:url :title :description :thumbnail
:status-link-preview? :favicon]))
(map data-store.messages/->link-preview-rpc)
(filter (fn [{:keys [status-link-preview?]}]
(not status-link-preview?)))))
(rf/defn send-edited-message
[{:keys [db]
:as cofx} text {:keys [message-id quoted-message chat-id]}]
(rf/merge
cofx
{:json-rpc/call
[{:method "wakuext_editMessage"
:params [{:id message-id
:text text
:content-type (if (emoji-only-content?
{:text text
:response-to quoted-message})
constants/content-type-emoji
constants/content-type-text)
:linkPreviews (process-link-previews (get-in db
[:chat/link-previews :unfurled]))
:statusLinkPreviews (map data-store.messages/->status-link-previews-rpc
(get-in db
[:chat/status-link-previews
:unfurled]))}]
:js-response true
:on-error #(log/error "failed to edit message " %)
:on-success (fn [result]
(rf/dispatch [:sanitize-messages-and-process-response
result]))}]}
{:json-rpc/call [{:method "wakuext_editMessage"
:params [{:id message-id
:text text
:content-type (if (emoji-only-content?
{:text text
:response-to quoted-message})
constants/content-type-emoji
constants/content-type-text)
:linkPreviews (map #(-> %
(select-keys [:url :title :description
:thumbnail])
data-store-messages/->link-preview-rpc)
(get-in db [:chat/link-previews :unfurled]))}]
:js-response true
:on-error #(log/error "failed to edit message " %)
:on-success (fn [result]
(rf/dispatch [:sanitize-messages-and-process-response
result]))}]}
(link-preview/reset-unfurled)
(cancel-message-edit)))
@@ -80,53 +80,24 @@
[]
curr-previews))
(defn- merge-preview-types
[new-previews new-status-link-previews]
(-> (map
(fn [{{:keys [display-name members-count banner icon
description]} :community
{contact-display-name :display-name
contact-icon :icon} :contact
url :url}]
;; Other types of previews need to be done here too
;; Like User link preview and Channel link preview
{:url url
:display-name (or contact-display-name display-name)
:members-count members-count
:banner banner
:icon (or (:data-uri contact-icon) icon)
:description description
;; Need to set status-link-preview?
;; to true to not send status-link-previews
;; with normal link-previews
:status-link-preview? true})
new-status-link-previews)
(concat new-previews)))
(rf/defn unfurl-parsed-urls-success
{:events [:link-preview/unfurl-parsed-urls-success]}
[{:keys [db]} request-id {new-previews :linkPreviews status-link-previews :statusLinkPreviews}]
[{:keys [db]} request-id {new-previews :linkPreviews}]
(when (= request-id (get-in db [:chat/link-previews :request-id]))
(let [new-previews (map data-store.messages/<-link-preview-rpc new-previews)
new-status-link-previews (map data-store.messages/<-status-link-previews-rpc
status-link-previews)
merged-preview-types (merge-preview-types new-previews new-status-link-previews)
curr-previews (get-in db [:chat/link-previews :unfurled])
indexed-new-previews (utils.collection/index-by :url merged-preview-types)]
(let [new-previews (map data-store.messages/<-link-preview-rpc new-previews)
curr-previews (get-in db [:chat/link-previews :unfurled])
indexed-new-previews (utils.collection/index-by :url new-previews)]
(log/debug "URLs unfurled"
:chat/link-previews
{:event :link-preview/unfurl-parsed-urls-success
:previews (map #(update % :thumbnail dissoc :data-uri) merged-preview-types)
:previews (map #(update % :thumbnail dissoc :data-uri) new-previews)
:request-id request-id})
{:db (-> db
(assoc-in [:chat/status-link-previews :unfurled] new-status-link-previews)
(update-in [:chat/link-previews :unfurled] reconcile-unfurled indexed-new-previews)
(update-in [:chat/link-previews :cache]
merge
indexed-new-previews
(utils.collection/index-by :url
(failed-previews curr-previews
merged-preview-types))))})))
(failed-previews curr-previews new-previews))))})))
(rf/defn unfurl-parsed-urls-error
{:events [:link-preview/unfurl-parsed-urls-error]}
@@ -6,8 +6,6 @@
(def url-github "https://github.com")
(def url-gitlab "https://gitlab.com")
(def url-community
"https://status.app/c/iyKACkQKB0Rvb2RsZXMSJ0NvbG9yaW5nIHRoZSB3b3JsZCB3aXRoIGpveSDigKIg4bSXIOKAohiYohsiByMxMzFEMkYqAwEhMwM=#zQ3shYSHp7GoiXaauJMnDcjwU2yNjdzpXLosAWapPS4CFxc11")
(def preview-github {:url url-github :thumbnail nil})
(def preview-gitlab {:url url-gitlab :thumbnail nil})
(def request-id "abc123")
@@ -33,15 +31,7 @@
(is (match? {:json-rpc/call [{:method "wakuext_getTextURLs"
:params [url-github]}]}
(remove-rpc-callbacks
(events/unfurl-urls cofx url-github))))))
(testing "fetches status community URLs"
(let [cofx {:db {:chat/link-previews {:unfurled {}
:cache {}
:request-id "123"}}}]
(is (match? {:json-rpc/call [{:method "wakuext_getTextURLs"
:params [url-community]}]}
(remove-rpc-callbacks
(events/unfurl-urls cofx url-community)))))))
(events/unfurl-urls cofx url-github)))))))
(deftest unfurl-parsed-urls-test
(with-redefs [events/new-request-id (constantly request-id)]
@@ -1,11 +1,8 @@
(ns status-im.contexts.chat.messenger.composer.link-preview.view
(:require
[clojure.string :as string]
[quo.core :as quo]
[react-native.core :as rn]
[react-native.reanimated :as reanimated]
[status-im.common.resources :as resources]
[status-im.constants]
[status-im.contexts.chat.messenger.composer.constants :as constants]
[status-im.contexts.chat.messenger.composer.link-preview.events]
[status-im.contexts.chat.messenger.composer.link-preview.style :as style]
@@ -36,19 +33,13 @@
:horizontal-spacing style/padding-horizontal
:loading-message (i18n/label :t/link-preview-loading-message)
:on-clear #(rf/dispatch [:link-preview/clear])
:data (map
(fn [{:keys [title display-name thumbnail hostname loading? url favicon
icon]}]
{:title (or display-name title)
:body (or (when-not display-name hostname)
status-im.constants/status-hostname)
:logo (if (string/starts-with? url "https://status.app/c")
(resources/get-mock-image :status-logo)
(or icon favicon))
:loading? loading?
:thumbnail (:data-uri thumbnail)
:url url})
previews)}]]))
:data (map (fn [{:keys [title thumbnail hostname loading? url]}]
{:title title
:body hostname
:loading? loading?
:thumbnail (:data-uri thumbnail)
:url url})
previews)}]]))
(defn view
[]
@@ -16,13 +16,12 @@
(let [previews (rf/sub [:chats/message-link-previews chat-id message-id])]
(when (seq previews)
[:<>
(for [{:keys [url title description thumbnail hostname favicon]} previews]
(for [{:keys [url title description thumbnail hostname]} previews]
^{:key url}
[quo/link-preview
{:title title
:description description
:link hostname
:logo (:url favicon)
:thumbnail (:url thumbnail)
:thumbnail-size (when (nearly-square? thumbnail) :large)
:container-style {:margin-top 8}}])])))
@@ -1,28 +0,0 @@
(ns status-im.contexts.chat.messenger.messages.content.status-link-preview.view
(:require [quo.core :as quo]
[react-native.core :as rn]
[utils.re-frame :as rf]))
(defn view
[{:keys [chat-id message-id]}]
(let [status-link-previews (rf/sub [:chats/message-status-link-previews chat-id message-id])
link-previews? (rf/sub [:chats/message-link-previews? chat-id message-id])]
(when (seq status-link-previews)
[rn/view {:style {:margin-top (when link-previews? 8)}}
(for [{:keys [url community]} status-link-previews]
(when community
(let [{community-description :description
community-icon :icon
community-banner :banner
community-name :display-name
members-count :members-count} community]
^{:key url}
[quo/internal-link-card
{:type :community
:size :message
:description community-description
:members-count members-count
:title community-name
:banner (:url community-banner)
:icon (:url community-icon)
:on-press #(rf/dispatch [:universal-links/handle-url url])}])))])))
@@ -14,18 +14,18 @@
(defn mention-tag-wrapper
[first-child-mention]
{:flex-direction :row
:align-items :center
:height (if platform/ios? 22 21)
:border-radius 6
:transform [{:translateY (if platform/ios? (if first-child-mention 4.5 3) 4.5)}]})
{:flex-direction :row
:align-items :center
:height (if platform/ios? 22 21)
:background-color colors/primary-50-opa-10
:padding-horizontal 3
:border-radius 6
:transform [{:translateY (if platform/ios? (if first-child-mention 4.5 3) 4.5)}]})
(def mention-tag-text
{:color (colors/theme-colors colors/primary-50
colors/primary-60)
:selection-color :transparent
:background-color colors/primary-50-opa-10
:padding-horizontal 3
:suppress-highlighting true})
(defn code
@@ -5,7 +5,6 @@
[react-native.core :as rn]
[react-native.platform :as platform]
[status-im.contexts.chat.messenger.messages.content.link-preview.view :as link-preview]
[status-im.contexts.chat.messenger.messages.content.status-link-preview.view :as status-link-preview]
[status-im.contexts.chat.messenger.messages.content.text.style :as style]
[utils.i18n :as i18n]
[utils.re-frame :as rf]))
@@ -57,16 +56,14 @@
:mention
(conj
units
(let [resolved-mention (rf/sub [:messages/resolve-mention literal])]
[rn/pressable
{:on-press #(rf/dispatch [:chat.ui/show-profile literal])
:key resolved-mention
:style (style/mention-tag-wrapper first-child-mention)}
[quo/text
{:weight :medium
:style style/mention-tag-text
:size :paragraph-1}
resolved-mention]]))
[rn/pressable
{:on-press #(rf/dispatch [:chat.ui/show-profile literal])
:style (style/mention-tag-wrapper first-child-mention)}
[quo/text
{:weight :medium
:style style/mention-tag-text
:size :paragraph-1}
(rf/sub [:messages/resolve-mention literal])]])
:edited
(conj units
@@ -166,5 +163,4 @@
[message-data]
[:<>
[render-parsed-text message-data]
[link-preview/view message-data]
[status-link-preview/view message-data]])
[link-preview/view message-data]])
@@ -81,9 +81,7 @@
(fn []
(when-not cached-preview-data
(let [community-id (community-id-from-link community-link)]
(rf/dispatch [:communities/fetch-community
{:community-id community-id
:update-last-opened-at? false}]))))
(rf/dispatch [:communities/fetch-community community-id]))))
:reagent-render
(fn []
(when cached-preview-data
@@ -143,28 +143,28 @@
mute-chat-label (if community-channel? :t/mute-channel :t/mute-chat)
unmute-chat-label (if community-channel? :t/unmute-channel :t/unmute-chat)]
[quo/channel-actions
{:container-style {:margin-top 16}
:actions [{:accessibility-label :action-button-pinned
:big? true
:label (or latest-pin-text (i18n/label :t/no-pinned-messages))
:color cover-bg-color
:icon :i/pin
:counter-value pins-count
:on-press (fn []
(rf/dispatch [:pin-message/show-pins-bottom-sheet
chat-id]))}
{:accessibility-label :action-button-mute
:label (i18n/label (if muted
unmute-chat-label
mute-chat-label))
:color cover-bg-color
:icon (if muted? :i/activity-center :i/muted)
:on-press (fn []
(if muted?
(home.actions/unmute-chat-action chat-id)
(home.actions/mute-chat-action chat-id
chat-type
muted?)))}]}]))
{:style {:margin-top 16}
:actions [{:accessibility-label :action-button-pinned
:big? true
:label (or latest-pin-text (i18n/label :t/no-pinned-messages))
:color cover-bg-color
:icon :i/pin
:counter-value pins-count
:on-press (fn []
(rf/dispatch [:pin-message/show-pins-bottom-sheet
chat-id]))}
{:accessibility-label :action-button-mute
:label (i18n/label (if muted
unmute-chat-label
mute-chat-label))
:color cover-bg-color
:icon (if muted? :i/activity-center :i/muted)
:on-press (fn []
(if muted?
(home.actions/unmute-chat-action chat-id)
(home.actions/mute-chat-action chat-id
chat-type
muted?)))}]}]))
(defn f-list-footer
[{:keys [chat distance-from-list-top theme customization-color]}]
@@ -8,7 +8,6 @@
[legacy.status-im.data-store.chats :as data-store.chats]
[legacy.status-im.data-store.communities :as data-store.communities]
[legacy.status-im.data-store.invitations :as data-store.invitations]
[legacy.status-im.data-store.messages :as data-store.messages]
[legacy.status-im.group-chats.core :as models.group]
[legacy.status-im.multiaccounts.update.core :as update.core]
[legacy.status-im.pairing.core :as models.pairing]
@@ -357,35 +356,32 @@
(conj set-hash-fxs
#(sanitize-messages-and-process-response % response-js false)))))
(defn- image->rpc [image] (set/rename-keys image {:data-uri :dataUri}))
(defn- link-preview->rpc
[preview]
(-> preview
(update :thumbnail image->rpc)
(update :favicon image->rpc)))
(update preview
:thumbnail
(fn [thumbnail]
(set/rename-keys thumbnail {:data-uri :dataUri}))))
(defn build-message
[msg]
(-> msg
(update :link-previews #(map link-preview->rpc %))
(update :status-link-previews #(set (map data-store.messages/->status-link-previews-rpc %)))
(set/rename-keys
{:album-id :albumId
:audio-duration-ms :audioDurationMs
:audio-path :audioPath
:chat-id :chatId
:community-id :communityId
:content-type :contentType
:ens-name :ensName
:image-height :imageHeight
:image-path :imagePath
:image-width :imageWidth
:link-previews :linkPreviews
:status-link-previews :statusLinkPreviews
:response-to :responseTo
:sticker :sticker
:text :text})))
{:album-id :albumId
:audio-duration-ms :audioDurationMs
:audio-path :audioPath
:chat-id :chatId
:community-id :communityId
:content-type :contentType
:ens-name :ensName
:image-height :imageHeight
:image-path :imagePath
:image-width :imageWidth
:link-previews :linkPreviews
:response-to :responseTo
:sticker :sticker
:text :text})))
(rf/defn send-chat-messages
[_ messages]
@@ -14,6 +14,6 @@
(defn bottom-actions
[]
{:padding-vertical 12
{:padding-top 12
:padding-horizontal screen-horizontal-padding
:background-color (colors/theme-colors colors/white colors/neutral-95)})
@@ -30,4 +30,6 @@
(defn gradient-cover-wrapper
[width]
{:width (gradient-cover-size width)
:border-radius 12})
:position :absolute
:border-radius 12
:z-index -1})
+10 -24
View File
@@ -22,18 +22,15 @@
[{:keys [db]} [community-js]]
(when community-js
(let [{:keys [token-permissions
token-permissions-check joined id last-opened-at]
token-permissions-check joined id]
:as community} (data-store.communities/<-rpc community-js)
has-channel-perm? (fn [id-perm-tuple]
(let [{:keys [type]} (second id-perm-tuple)]
(or (= type constants/community-token-permission-can-view-channel)
(=
type
constants/community-token-permission-can-view-and-post-channel))))
previous-last-opened-at (get-in db [:communities id :last-opened-at])]
{:db (assoc-in db
[:communities id]
(assoc community :last-opened-at (max last-opened-at previous-last-opened-at)))
constants/community-token-permission-can-view-and-post-channel))))]
{:db (assoc-in db [:communities id] community)
:fx [[:dispatch [:communities/initialize-permission-addresses id]]
(when (not joined)
[:dispatch [:chat.ui/spectate-community id]])
@@ -302,7 +299,6 @@
(when community
{:db (update db :communities/fetching-community dissoc community-id)
:fx [[:dispatch [:communities/handle-community community]]
[:dispatch [:communities/update-last-opened-at community-id]]
[:dispatch
[:chat.ui/cache-link-preview-data (link-preview.events/community-link community-id)
community]]]}))
@@ -316,7 +312,7 @@
(rf/reg-event-fx :chat.ui/community-failed-to-fetch community-failed-to-fetch)
(defn fetch-community
[{:keys [db]} [{:keys [community-id update-last-opened-at?]}]]
[{:keys [db]} [community-id]]
(when (and community-id (not (get-in db [:communities/fetching-community community-id])))
{:db (assoc-in db [:communities/fetching-community community-id] true)
:json-rpc/call [{:method "wakuext_fetchCommunity"
@@ -324,8 +320,6 @@
:TryDatabase true
:WaitForResponse true}]
:on-success (fn [community]
(when update-last-opened-at?
(rf/dispatch [:communities/update-last-opened-at community-id]))
(rf/dispatch [:chat.ui/community-fetched community-id community]))
:on-error (fn [err]
(rf/dispatch [:chat.ui/community-failed-to-fetch community-id])
@@ -338,11 +332,7 @@
[:catn
[:cofx :schema.re-frame/cofx]
[:args
[:schema
[:catn
[:map
[:community-id [:? :string]]
[:update-last-opened-at? [:? :boolean]]]]]]]
[:schema [:catn [:community-id [:? :string]]]]]]
[:maybe
[:map
[:db map?]
@@ -410,21 +400,17 @@
(navigate-to-serialized-community cofx deserialized-key)
(rf/merge
cofx
{:fx [[:dispatch
[:communities/fetch-community
{:community-id deserialized-key
:update-last-opened-at? true}]]
[:dispatch [:navigate-to :community-overview deserialized-key]]]}
{:fx [[:dispatch [:communities/fetch-community deserialized-key]]
[:dispatch [:navigate-to :community-overview deserialized-key]]
[:dispatch [:communities/update-last-opened-at deserialized-key]]]}
(navigation/pop-to-root :shell-stack)))))
(rf/reg-event-fx :communities/navigate-to-community-chat
(fn [{:keys [db]} [chat-id pop-to-root?]]
(let [{:keys [community-id]} (get-in db [:chats chat-id])]
{:fx [(when community-id
[:dispatch
[:communities/fetch-community
{:community-id community-id
:update-last-opened-at? true}]])
[:dispatch [:communities/fetch-community community-id]]
[:dispatch [:communities/update-last-opened-at community-id]])
(if pop-to-root?
[:dispatch [:chat/pop-to-root-and-navigate-to-chat chat-id]]
[:dispatch [:chat/navigate-to-chat chat-id]])]})))
@@ -106,14 +106,14 @@
(testing "update fetching indicator in db"
(is (match?
{:db {:communities/fetching-community {community-id true}}}
(events/fetch-community {} [{:community-id community-id}]))))
(events/fetch-community {} [community-id]))))
(testing "call the fetch community rpc method with correct community id"
(is (match?
{:json-rpc/call [{:method "wakuext_fetchCommunity"
:params [{:CommunityKey community-id
:TryDatabase true
:WaitForResponse true}]}]}
(events/fetch-community {} [{:community-id community-id}])))))
(events/fetch-community {} [community-id])))))
(testing "with no community id"
(testing "do nothing"
(is (match?
@@ -143,7 +143,6 @@
(testing "dispatch fxs"
(is (match?
{:fx [[:dispatch [:communities/handle-community {:id community-id}]]
[:dispatch [:communities/update-last-opened-at community-id]]
[:dispatch
[:chat.ui/cache-link-preview-data "community-link+community-id"
{:id community-id}]]]}
@@ -154,7 +153,6 @@
(testing "dispatch fxs, do not spectate community"
(is (match?
{:fx [[:dispatch [:communities/handle-community {:id community-id}]]
[:dispatch [:communities/update-last-opened-at community-id]]
[:dispatch
[:chat.ui/cache-link-preview-data "community-link+community-id"
{:id community-id}]]]}
@@ -165,7 +163,6 @@
(testing "dispatch fxs, do not spectate community"
(is (match?
{:fx [[:dispatch [:communities/handle-community {:id community-id}]]
[:dispatch [:communities/update-last-opened-at community-id]]
[:dispatch
[:chat.ui/cache-link-preview-data "community-link+community-id"
{:id community-id}]]]}
@@ -66,6 +66,11 @@
:source (resources/get-mock-image :dark-blur-bg)}])
[(if @blur? blur/view rn/view)
{:style {:height 332
:position :absolute
:top 0
:left 0
:right 0
:bottom 0
:padding-vertical 40}
:blur-type :dark}
[quo/gradient-cover @state]]]
@@ -1,7 +1,6 @@
(ns status-im.contexts.preview.quo.preview
(:require
[camel-snake-kebab.core :as camel-snake-kebab]
cljs.pprint
[clojure.string :as string]
[quo.core :as quo]
[quo.foundations.colors :as colors]
@@ -1,58 +1,31 @@
(ns status-im.contexts.profile.contact.actions.view
(:require [clojure.string :as string]
[quo.core :as quo]
[react-native.core :as rn]
(:require [quo.core :as quo]
[status-im.common.not-implemented :as not-implemented]
[status-im.contexts.profile.contact.add-nickname.view :as add-nickname]
[utils.i18n :as i18n]
[utils.re-frame :as rf]))
[utils.i18n :as i18n]))
(defn view
[]
(let [{:keys [nickname public-key]} (rf/sub [:contacts/current-contact])
on-add-nickname (rn/use-callback #(rf/dispatch [:show-bottom-sheet
{:content
(fn [] [add-nickname/view])}]))
on-remove-nickname (rn/use-callback
(fn []
(rf/dispatch [:hide-bottom-sheet])
(rf/dispatch [:toasts/upsert
{:id :remove-nickname
:type :positive
:text (i18n/label :t/nickname-removed)}])
(rf/dispatch [:contacts/update-nickname public-key ""]))
[public-key])
has-nickname? (rn/use-memo (fn [] (not (string/blank? nickname))) [nickname])]
[quo/action-drawer
[[{:icon :i/edit
:label (if has-nickname?
(i18n/label :t/edit-nickname)
(i18n/label :t/add-nickname-title))
:on-press on-add-nickname
:accessibility-label (if nickname :edit-nickname :add-nickname)}
{:icon :i/qr-code
:label (i18n/label :t/show-qr)
:on-press not-implemented/alert
:accessibility-label :show-qr-code}
{:icon :i/share
:label (i18n/label :t/share-profile)
:on-press not-implemented/alert
:accessibility-label :share-profile}
(when has-nickname?
{:icon :i/delete
:label (i18n/label :t/remove-nickname)
:on-press on-remove-nickname
:add-divider? true
:accessibility-label :remove-nickname
:danger? true})
{:icon :i/untrustworthy
:label (i18n/label :t/mark-untrustworthy)
:on-press not-implemented/alert
:accessibility-label :mark-untrustworthy
:add-divider? (when-not has-nickname? true)
:danger? true}
{:icon :i/block
:label (i18n/label :t/block-user)
:on-press not-implemented/alert
:accessibility-label :block-user
:danger? true}]]]))
[quo/action-drawer
[[{:icon :i/edit
:label (i18n/label :t/add-nickname-title)
:on-press not-implemented/alert
:accessibility-label :add-nickname}
{:icon :i/qr-code
:label (i18n/label :t/show-qr)
:on-press not-implemented/alert
:accessibility-label :show-qr-code}
{:icon :i/share
:label (i18n/label :t/share-profile)
:on-press not-implemented/alert
:accessibility-label :share-profile}
{:icon :i/untrustworthy
:label (i18n/label :t/mark-untrustworthy)
:on-press not-implemented/alert
:accessibility-label :mark-untrustworthy
:add-divider? true
:danger? true}
{:icon :i/block
:label (i18n/label :t/block-user)
:on-press not-implemented/alert
:accessibility-label :block-user
:danger? true}]]])
@@ -1,6 +0,0 @@
(ns status-im.contexts.profile.contact.add-nickname.style)
(def input-wrapper
{:padding-vertical 8
:padding-horizontal 20
:gap 8})
@@ -1,81 +0,0 @@
(ns status-im.contexts.profile.contact.add-nickname.view
(:require [clojure.string :as string]
[quo.core :as quo]
[react-native.core :as rn]
[status-im.common.validation.profile :as profile-validator]
[status-im.constants :as constants]
[status-im.contexts.profile.contact.add-nickname.style :as style]
[status-im.contexts.profile.utils :as profile.utils]
[utils.debounce :as debounce]
[utils.i18n :as i18n]
[utils.re-frame :as rf]))
(defn view
[]
(let [{:keys [public-key primary-name nickname customization-color]
:as profile} (rf/sub [:contacts/current-contact])
;; TODO(@mohsen): remove :blue, https://github.com/status-im/status-mobile/issues/18733
customization-color (or customization-color :blue)
full-name (profile.utils/displayed-name profile)
profile-picture (profile.utils/photo profile)
[unsaved-nickname set-unsaved-nickname] (rn/use-state nickname)
[error-msg set-error-msg] (rn/use-state nil)
validate-nickname (rn/use-callback
(debounce/debounce
(fn [name]
(set-error-msg
(profile-validator/validation-nickname name)))
300))
on-cancel (rn/use-callback #(rf/dispatch [:hide-bottom-sheet]))
on-nickname-change (rn/use-callback (fn [text]
(set-unsaved-nickname text)
(validate-nickname text)))
on-nickname-submit (rn/use-callback
(fn []
(rf/dispatch [:hide-bottom-sheet])
(rf/dispatch [:toasts/upsert
{:id :add-nickname
:type :positive
:text (i18n/label
(if (string/blank? nickname)
:t/nickname-added
:t/nickname-updated)
{:primary-name primary-name})}])
(rf/dispatch [:contacts/update-nickname public-key
(string/trim unsaved-nickname)]))
[public-key unsaved-nickname])]
[:<>
[quo/drawer-top
{:type :context-tag
:context-tag-type :default
:title (i18n/label :t/add-nickname-title)
:full-name full-name
:profile-picture profile-picture
:customization-color customization-color}]
[rn/view {:style style/input-wrapper}
[quo/input
{:type :text
:char-limit constants/profile-name-max-length
:max-length constants/profile-name-max-length
:auto-focus true
:default-value unsaved-nickname
:error? (not (string/blank? error-msg))
:label (i18n/label :t/nickname)
:on-change-text on-nickname-change}
:on-submit-editing on-nickname-submit]
[quo/info-message
{:icon :i/info
:size :default
:type (if-not (string/blank? error-msg) :error :default)}
(if-not (string/blank? error-msg)
error-msg
(i18n/label :t/nickname-visible-to-you))]]
[quo/bottom-actions
{:actions :two-actions
:button-one-label (i18n/label :t/add-nickname-title)
:button-one-props {:disabled? (or (string/blank? unsaved-nickname)
(not (string/blank? error-msg)))
:on-press on-nickname-submit}
:button-two-label (i18n/label :t/cancel)
:button-two-props {:type :grey
:on-press on-cancel}}]]))
@@ -1,10 +0,0 @@
(ns status-im.contexts.profile.contact.contact-request.style)
(def message-prompt-wrapper
{:padding-top 4
:padding-bottom 8
:padding-horizontal 20})
(def message-input-wrapper
{:padding-vertical 8
:padding-horizontal 20})
@@ -1,46 +0,0 @@
(ns status-im.contexts.profile.contact.contact-request.view
(:require [clojure.string :as string]
[quo.core :as quo]
[react-native.core :as rn]
[status-im.contexts.profile.contact.contact-request.style :as style]
[status-im.contexts.profile.utils :as profile.utils]
[utils.i18n :as i18n]
[utils.re-frame :as rf]))
(defn view
[]
(let [{:keys [public-key customization-color]
:as profile} (rf/sub [:contacts/current-contact])
;; TODO: remove :blue when #18733 merged.
customization-color (or customization-color :blue)
full-name (profile.utils/displayed-name profile)
profile-picture (profile.utils/photo profile)
[message set-message] (rn/use-state "")
on-message-change (rn/use-callback #(set-message %))
on-message-submit (rn/use-callback (fn []
(rf/dispatch [:hide-bottom-sheet])
(rf/dispatch [:contact.ui/send-contact-request
public-key message]))
[public-key message])]
[:<>
[quo/drawer-top
{:type :context-tag
:context-tag-type :default
:title (i18n/label :t/send-contact-request)
:full-name full-name
:profile-picture profile-picture
:customization-color customization-color}]
[rn/text {:style style/message-prompt-wrapper}
(i18n/label :t/contact-request-message-prompt)]
[rn/view {:style style/message-input-wrapper}
[quo/input
{:type :text
:multiline? true
:char-limit 280
:label (i18n/label :t/message)
:on-change-text on-message-change}]]
[quo/bottom-actions
{:actions :one-action
:button-one-props {:disabled? (string/blank? message)
:on-press on-message-submit}
:button-one-label (i18n/label :t/send-contact-request)}]]))
@@ -7,11 +7,6 @@
:padding-left 20
:align-items :flex-start})
(def button-wrapper
{:padding-top 8
:padding-bottom 16
:padding-horizontal 20})
(defn header-container
[border-radius theme margin-top]
(reanimated/apply-animations-to-style
@@ -3,10 +3,8 @@
[quo.foundations.colors :as colors]
[quo.theme]
[react-native.core :as rn]
[status-im.common.not-implemented]
[status-im.common.scalable-avatar.view :as avatar]
[status-im.constants :as constants]
[status-im.contexts.profile.contact.contact-request.view :as contact-request]
[status-im.contexts.profile.contact.header.style :as style]
[status-im.contexts.profile.utils :as profile.utils]
[utils.i18n :as i18n]
@@ -21,9 +19,7 @@
full-name (profile.utils/displayed-name profile)
profile-picture (profile.utils/photo profile)
online? (rf/sub [:visibility-status-updates/online? public-key])
theme (quo.theme/use-theme-value)
on-contact-request (rn/use-callback #(rf/dispatch [:show-bottom-sheet
{:content (fn [] [contact-request/view])}]))]
theme (quo.theme/use-theme-value)]
[rn/view {:style style/header-container}
[rn/view {:style style/header-top-wrapper}
[rn/view {:style style/avatar-wrapper}
@@ -44,15 +40,4 @@
{:title full-name
:description :text
:description-text bio
:emoji-dash emoji-hash}]
(cond
(or (not contact-request-state)
(= contact-request-state constants/contact-request-state-none))
[rn/view {:style style/button-wrapper}
[quo/button
{:on-press on-contact-request
:icon-left :i/add-user}
(i18n/label :t/send-contact-request)]]
:else nil)]))
:emoji-dash emoji-hash}]]))
@@ -21,7 +21,7 @@
{:navigate-back? true
:height 148
:on-scroll #(reanimated/set-shared-value scroll-y %)
;; TODO(@mohsen): remove colors/primary-50, https://github.com/status-im/status-mobile/issues/18733
;TODO remove colors/primary-50 when #18733 merged.
:cover-color (or customization-color colors/primary-50)
:background-color (colors/theme-colors colors/white colors/neutral-95 theme)
:page-nav-props {:right-side [{:icon-name :i/options
@@ -44,7 +44,8 @@
[rn/view {:style {:gap 22}}
[quo/text-combinations {:title (i18n/label :t/bio)}]
[quo/input
{:blur? true
{:theme :dark
:blur? true
:multiline? true
:error? (not (string/blank? @error-msg))
:container-style {:margin-bottom -11}
@@ -45,7 +45,8 @@
[rn/view {:style {:gap 22}}
[quo/text-combinations {:title (i18n/label :t/name)}]
[quo/input
{:blur? true
{:theme :dark
:blur? true
:error? (not (string/blank? @error-msg))
:container-style {:margin-bottom -11}
:default-value @full-name
@@ -7,6 +7,7 @@
status-im.contexts.shell.jump-to.effects
[status-im.contexts.shell.jump-to.utils :as shell.utils]
[status-im.navigation.state :as navigation.state]
[status-im.navigation.utils :as navigation.utils]
[utils.re-frame :as rf]))
;;;; Events
@@ -149,10 +150,12 @@
(not hidden-screen?)
(:current-chat-id db))
(conj [:chat/close]))})
{:db (-> db
(assoc :view-id go-to-view-id)
(dissoc :modal-view-ids))
:navigate-to go-to-view-id}))
(let [modal-view-ids (navigation.utils/remove-current-modal-stack (:modal-view-ids db))]
{:navigate-to go-to-view-id
:db (cond-> db
true (assoc :view-id go-to-view-id)
(seq modal-view-ids) (assoc :modal-view-ids modal-view-ids)
(not (seq modal-view-ids)) (dissoc :modal-view-ids))})))
(rf/defn shell-navigate-back
{:events [:shell/navigate-back]}
@@ -176,8 +179,11 @@
shell.constants/close-screen-with-slide-to-right-animation))}
(when (and current-chat-id community-id)
{:dispatch [:shell/add-switcher-card shell.constants/community-screen community-id]}))
{:navigate-back nil
:db (dissoc db :modal-view-ids)})))
(let [modal-view-ids (navigation.utils/remove-current-modal-stack (:modal-view-ids db))]
{:navigate-back nil
:db (if (seq modal-view-ids)
(assoc db :modal-view-ids modal-view-ids)
(dissoc db :modal-view-ids))}))))
(rf/defn floating-screen-opened
{:events [:shell/floating-screen-opened]}
@@ -39,7 +39,7 @@
(fn []
[network-preferences/view
{:blur? true
:selected-networks (set @selected-networks)
:selected-networks (set selected-networks)
:on-save (fn [chain-ids]
(rf/dispatch [:hide-bottom-sheet])
(reset! selected-networks (map #(get utils/id->network %)
@@ -48,10 +48,7 @@
[props]
(let [{:keys [account width index]} props
selected-networks (reagent/atom [:ethereum :optimism :arbitrum])
wallet-type (reagent/atom :legacy)
on-settings-press #(open-preferences selected-networks)
on-legacy-press #(reset! wallet-type :legacy)
on-multichain-press #(reset! wallet-type :multichain)]
wallet-type (reagent/atom :legacy)]
(fn []
(let [share-title (str (:name account) " " (i18n/label :t/address))
qr-url (utils/get-wallet-qr {:wallet-type @wallet-type
@@ -75,9 +72,9 @@
:full-name (:name account)
:customization-color (:color account)
:emoji (:emoji account)
:on-multichain-press on-multichain-press
:on-legacy-press on-legacy-press
:on-settings-press on-settings-press}]]]))))
:on-multichain-press #(reset! wallet-type :multichain)
:on-legacy-press #(reset! wallet-type :legacy)
:on-settings-press #(open-preferences @selected-networks)}]]]))))
(def wallet-qr-code-item (memoize wallet-qr-code-item-internal))
@@ -130,5 +130,5 @@
[quo/divider-label {:tight? false} (i18n/label :t/have-a-sync-code?)]
[quo/action-drawer
[[{:icon :i/scan
:on-press #(rf/dispatch [:open-modal :scan-sync-code-page])
:on-press #(rf/dispatch [:navigate-to :scan-sync-code-page])
:label (i18n/label :t/scan-or-enter-sync-code)}]]]]]])))
@@ -17,9 +17,7 @@
(case selected-tab
:assets [assets/view]
:collectibles [collectibles/view
{:collectibles collectible-list
:on-end-reached #(rf/dispatch
[:wallet/request-collectibles-for-current-viewing-account])
{:collectibles collectible-list
:on-collectible-press (fn [{:keys [id]}]
(rf/dispatch [:wallet/get-collectible-details id]))}]
:activity [activity/view]
@@ -8,7 +8,7 @@
[utils.i18n :as i18n]))
(defn- view-internal
[{:keys [theme collectibles filtered? on-collectible-press on-end-reached]}]
[{:keys [theme collectibles filtered? on-collectible-press]}]
(let [no-results-match-query? (and filtered? (empty? collectibles))]
(cond
no-results-match-query?
@@ -26,17 +26,14 @@
:else
[rn/flat-list
{:data collectibles
:style {:flex 1}
:content-container-style {:align-items :center}
:window-size 11
:num-columns 2
:render-fn (fn [{:keys [preview-url] :as collectible}]
[quo/collectible
{:images [preview-url]
:on-press #(when on-collectible-press
(on-collectible-press collectible))}])
:on-end-reached on-end-reached
:on-end-reached-threshold 4}])))
{:data collectibles
:style {:flex 1}
:content-container-style {:align-items :center}
:num-columns 2
:render-fn (fn [{:keys [preview-url] :as collectible}]
[quo/collectible
{:images [preview-url]
:on-press #(when on-collectible-press
(on-collectible-press collectible))}])}])))
(def view (quo.theme/with-theme view-internal))
@@ -6,6 +6,14 @@
{:flex 1
:margin-top top})
(defn gradient-cover-container
[top]
{:position :absolute
:top (- top)
:left 0
:right 0
:z-index -1})
(def account-avatar-container
{:padding-horizontal 20
:padding-top 12})
@@ -2,6 +2,14 @@
(:require
[quo.foundations.colors :as colors]))
(defn gradient-cover-container
[top]
{:position :absolute
:top (- top)
:left 0
:right 0
:z-index -1})
(def account-avatar-container
{:padding-horizontal 20
:padding-top 12})
@@ -78,7 +78,7 @@
:on-press #(rf/dispatch [:navigate-back])}]
[quo/gradient-cover
{:customization-color @account-color
:container-style {:top (- top)}}]
:container-style (style/gradient-cover-container top)}]
[rn/view
{:style style/account-avatar-container}
[quo/account-avatar
@@ -1,12 +1,12 @@
(ns status-im.contexts.wallet.data-store
(:require
[camel-snake-kebab.core :as csk]
[camel-snake-kebab.extras :as cske]
[clojure.set :as set]
[clojure.string :as string]
[status-im.constants :as constants]
[utils.money :as money]
[utils.number :as utils.number]
[utils.transforms :as transforms]))
[utils.number :as utils.number]))
(defn chain-ids-string->set
[ids-string]
@@ -60,7 +60,8 @@
:color :colorId})
(update :prodPreferredChainIds chain-ids-set->string)
(update :testPreferredChainIds chain-ids-set->string)
(dissoc :watch-only? :default-account? :tokens :collectibles)))
(dissoc :watch-only?)
(dissoc :default-account?)))
(defn- rpc->balances-per-chain
[token]
@@ -78,7 +79,7 @@
[tokens]
(-> tokens
(update-keys name)
(update-vals #(cske/transform-keys transforms/->kebab-case-keyword %))
(update-vals #(cske/transform-keys csk/->kebab-case %))
(update-vals remove-tokens-with-empty-values)
(update-vals #(mapv rpc->balances-per-chain %))))
@@ -122,4 +123,4 @@
(defn parse-keypairs
[keypairs]
(let [renamed-data (rename-color-id-in-data keypairs)]
(cske/transform-keys transforms/->kebab-case-keyword renamed-data)))
(cske/transform-keys csk/->kebab-case-keyword renamed-data)))
+3 -40
View File
@@ -3,8 +3,6 @@
[clojure.string :as string]
[react-native.background-timer :as background-timer]
[react-native.platform :as platform]
[status-im.constants :as constants]
[status-im.contexts.wallet.common.utils :as utils]
[status-im.contexts.wallet.data-store :as data-store]
[status-im.contexts.wallet.events.collectibles]
[status-im.contexts.wallet.item-types :as item-types]
@@ -14,8 +12,7 @@
[utils.ethereum.eip.eip55 :as eip55]
[utils.i18n :as i18n]
[utils.number]
[utils.re-frame :as rf]
[utils.transforms :as transforms]))
[utils.re-frame :as rf]))
(rf/reg-event-fx :wallet/show-account-created-toast
(fn [{:keys [db]} [address]]
@@ -59,7 +56,7 @@
[:wallet :accounts]
(utils.collection/index-by :address (data-store/rpc->accounts wallet-accounts)))
:fx [[:dispatch [:wallet/get-wallet-token]]
[:dispatch [:wallet/request-collectibles-for-all-accounts {:new-request? true}]]
[:dispatch [:wallet/request-collectibles {:start-at-index 0 :new-request? true}]]
(when new-account?
[:dispatch [:wallet/navigate-to-new-account navigate-to-account]])]})))
@@ -429,6 +426,7 @@
(rf/reg-event-fx :wallet/store-secret-phrase store-secret-phrase)
(defn new-keypair-created
[{:keys [db]} [{:keys [new-keypair]}]]
{:db (assoc-in db [:wallet :ui :create-account :new-keypair] new-keypair)
@@ -450,38 +448,3 @@
{:db (update-in db [:wallet :ui :create-account] dissoc :new-keypair)})
(rf/reg-event-fx :wallet/clear-new-keypair clear-new-keypair)
(rf/reg-event-fx
:wallet/blockchain-status-changed
(fn [{:keys [db]} [{:keys [message]}]]
(let [chains (-> (transforms/json->clj message)
(update-keys (comp utils.number/parse-int name)))
down-chains (-> (select-keys chains
(for [[k v] chains :when (= v "down")] k))
keys)
test-networks-enabled? (get-in db [:profile/profile :test-networks-enabled?])
disabled-chain-id? (fn [chain-id]
(let [mainnet-chain? (contains? constants/mainnet-chain-ids chain-id)]
(if test-networks-enabled?
mainnet-chain?
(not mainnet-chain?))))
chains-filtered-by-mode (remove disabled-chain-id? down-chains)
chains-down? (seq chains-filtered-by-mode)
chain-names (when chains-down?
(->> (map #(-> (utils/id->network %)
name
string/capitalize)
chains-filtered-by-mode)
distinct
(string/join ", ")))]
(when (seq down-chains)
(log/info "[wallet] Chain(s) down: " down-chains)
(log/info "[wallet] Test network enabled: " (boolean test-networks-enabled?)))
{:db (assoc-in db [:wallet :statuses :blockchains] chains)
:fx (when chains-down?
[[:dispatch
[:toasts/upsert
{:id :chains-down
:type :negative
:text (i18n/label :t/provider-is-down {:chains chain-names})
:duration 10000}]]])})))
@@ -1,9 +1,11 @@
(ns status-im.contexts.wallet.events.collectibles
(:require [camel-snake-kebab.extras :as cske]
(:require [camel-snake-kebab.core :as csk]
[camel-snake-kebab.extras :as cske]
[clojure.string :as string]
[taoensso.timbre :as log]
[utils.ethereum.chain :as chain]
[utils.re-frame :as rf]
[utils.transforms :as transforms]))
[utils.transforms :as types]))
(def collectible-data-types
{:unique-id 0
@@ -18,23 +20,33 @@
:fetch-if-cache-old 3})
(def max-cache-age-seconds 3600)
(def collectibles-request-batch-size 25)
(def collectibles-request-batch-size 1000)
(defn- move-collectibles-to-accounts
[accounts new-collectibles-per-account]
(reduce-kv (fn [acc account new-collectibles]
(update-in acc [account :collectibles] #(reduce conj (or % []) new-collectibles)))
accounts
new-collectibles-per-account))
(defn displayable-collectible?
[collectible]
(let [{{:keys [image-url animation-url]} :collectible-data
{collection-image-url :image-url} :collection-data} collectible]
(or (not (string/blank? animation-url))
(not (string/blank? image-url))
(not (string/blank? collection-image-url)))))
(defn flush-collectibles
[{:keys [db]}]
(let [collectibles-per-account (get-in db [:wallet :ui :collectibles :fetched])]
{:db (-> db
(update-in [:wallet :ui :collectibles] dissoc :pending-requests :fetched)
(update-in [:wallet :accounts] move-collectibles-to-accounts collectibles-per-account))}))
(defn- add-collectibles-to-accounts
[accounts collectibles]
(reduce (fn [acc {:keys [ownership] :as collectible}]
(->> ownership
(map :address) ; In ERC1155 tokens a collectible can be owned by multiple addresses.
(reduce (fn add-collectible-to-address [acc address]
(update-in acc [address :collectibles] conj collectible))
acc)))
accounts
collectibles))
(rf/reg-event-fx :wallet/flush-collectibles-fetched flush-collectibles)
(defn store-collectibles
[{:keys [db]} [collectibles]]
(let [displayable-collectibles (filter displayable-collectible? collectibles)]
{:db (update-in db [:wallet :accounts] add-collectibles-to-accounts displayable-collectibles)}))
(rf/reg-event-fx :wallet/store-collectibles store-collectibles)
(defn clear-stored-collectibles
[{:keys [db]}]
@@ -50,110 +62,48 @@
(rf/reg-event-fx :wallet/store-last-collectible-details store-last-collectible-details)
(rf/reg-event-fx
:wallet/request-new-collectibles-for-account
(fn [{:keys [db]} [{:keys [request-id account amount]}]]
(let [current-collectible-idx (get-in db [:wallet :accounts account :current-collectible-idx] 0)
collectibles-filter nil
data-type (collectible-data-types :header)
fetch-criteria {:fetch-type (fetch-type :fetch-if-not-cached)
:max-cache-age-seconds max-cache-age-seconds}
chain-ids (chain/chain-ids db)
request-params [request-id
chain-ids
[account]
collectibles-filter
current-collectible-idx
amount
data-type
fetch-criteria]]
:wallet/request-collectibles
(fn [{:keys [db]} [{:keys [start-at-index new-request?]}]]
(let [request-id 0
collectibles-filter nil
data-type (collectible-data-types :header)
fetch-criteria {:fetch-type (fetch-type :fetch-if-not-cached)
:max-cache-age-seconds max-cache-age-seconds}
chain-ids (chain/chain-ids db)
request-params [request-id
chain-ids
(keys (get-in db [:wallet :accounts]))
collectibles-filter
start-at-index
collectibles-request-batch-size
data-type
fetch-criteria]]
{:fx [[:json-rpc/call
[{:method "wallet_getOwnedCollectiblesAsync"
:params request-params
:on-error (fn [error]
(log/error "failed to request collectibles for account"
{:event :wallet/request-new-collectibles-for-account
(log/error "failed to request collectibles"
{:event :wallet/request-collectibles
:error error
:params request-params}))}]]]})))
(defonce collectibles-request-ids (atom 0))
(defn- get-unique-collectible-request-id
[amount]
(let [initial-id (deref collectibles-request-ids)
last-id (+ initial-id amount)]
(reset! collectibles-request-ids last-id)
(range initial-id last-id)))
(rf/reg-event-fx
:wallet/request-collectibles-for-all-accounts
(fn [{:keys [db]} [{:keys [new-request?]}]]
(let [accounts (->> (get-in db [:wallet :accounts])
(filter (fn [[_ {:keys [has-more-collectibles?]}]]
(or (nil? has-more-collectibles?)
(true? has-more-collectibles?))))
(keys))
num-accounts (count accounts)
collectibles-per-account (quot collectibles-request-batch-size num-accounts)
;; We need to pass unique IDs for simultaneous requests, otherwise they'll fail
request-ids (get-unique-collectible-request-id num-accounts)
collectible-requests (map (fn [id account]
[:dispatch
[:wallet/request-new-collectibles-for-account
{:request-id id
:account account
:amount collectibles-per-account}]])
request-ids
accounts)]
{:db (cond-> db
:always (assoc-in [:wallet :ui :collectibles :pending-requests] num-accounts)
new-request? (update-in [:wallet :accounts] update-vals #(dissoc % :collectibles)))
:fx collectible-requests})))
(rf/reg-event-fx
:wallet/request-collectibles-for-current-viewing-account
(fn [{:keys [db]} _]
(let [current-viewing-account (-> db :wallet :current-viewing-account-address)
[request-id] (get-unique-collectible-request-id 1)]
{:db (assoc-in db [:wallet :ui :collectibles :pending-requests] 1)
:fx [[:dispatch
[:wallet/request-new-collectibles-for-account
{:request-id request-id
:account current-viewing-account
:amount collectibles-request-batch-size}]]]})))
(defn- update-fetched-collectibles-progress
[db owner-address collectibles offset has-more?]
(-> db
(assoc-in [:wallet :ui :collectibles :fetched owner-address] collectibles)
(assoc-in [:wallet :accounts owner-address :current-collectible-idx]
(+ offset (count collectibles)))
(assoc-in [:wallet :accounts owner-address :has-more-collectibles?] has-more?)))
:params request-params}))}]]
(when new-request?
[:dispatch [:wallet/clear-stored-collectibles]])]})))
(rf/reg-event-fx
:wallet/owned-collectibles-filtering-done
(fn [{:keys [db]} [{:keys [message]}]]
(let [{:keys [offset ownershipStatus collectibles
hasMore]} (transforms/json->clj message)
collectibles (cske/transform-keys transforms/->kebab-case-keyword collectibles)
pending-requests (dec (get-in db [:wallet :ui :collectibles :pending-requests]))
owner-address (some->> ownershipStatus
first
key
name)]
{:db (cond-> db
:always (assoc-in [:wallet :ui :collectibles :pending-requests] pending-requests)
owner-address (update-fetched-collectibles-progress owner-address
collectibles
offset
hasMore))
:fx [(when (zero? pending-requests)
[:dispatch [:wallet/flush-collectibles-fetched]])]})))
(fn [_ [{:keys [message]}]]
(let [{:keys [has-more offset
collectibles]} (cske/transform-keys csk/->kebab-case-keyword (types/json->clj message))
start-at-index (+ offset (count collectibles))]
{:fx [[:dispatch [:wallet/store-collectibles collectibles]]
(when has-more
[:dispatch [:wallet/request-collectibles {:start-at-index start-at-index}]])]})))
(rf/reg-event-fx
:wallet/get-collectible-details
(fn [_ [collectible-id]]
(let [request-id 0
collectible-id-converted (cske/transform-keys transforms/->PascalCaseKeyword collectible-id)
collectible-id-converted (cske/transform-keys csk/->PascalCaseKeyword collectible-id)
data-type (collectible-data-types :details)
request-params [request-id [collectible-id-converted] data-type]]
{:fx [[:json-rpc/call
@@ -168,8 +118,8 @@
(rf/reg-event-fx
:wallet/get-collectible-details-done
(fn [_ [{:keys [message]}]]
(let [response (cske/transform-keys transforms/->kebab-case-keyword
(transforms/json->clj message))
(let [response (cske/transform-keys csk/->kebab-case-keyword
(types/json->clj message))
{:keys [collectibles]} response
collectible (first collectibles)]
(if collectible
+22 -12
View File
@@ -56,8 +56,24 @@
(is (match? (:db effects) expected-db))))
(deftest store-collectibles
(testing "flush-collectibles"
(let [collectible-1 {:collectible-data {:image-url "https://..." :animation-url "https://..."}
(testing "(displayable-collectible?) helper function"
(let [expected-results [[true
{:collectible-data {:image-url "https://..." :animation-url "https://..."}}]
[true {:collectible-data {:image-url "" :animation-url "https://..."}}]
[true {:collectible-data {:image-url nil :animation-url "https://..."}}]
[true {:collectible-data {:image-url "https://..." :animation-url ""}}]
[true {:collectible-data {:image-url "https://..." :animation-url nil}}]
[false {:collectible-data {:image-url "" :animation-url nil}}]
[false {:collectible-data {:image-url nil :animation-url nil}}]
[false {:collectible-data {:image-url nil :animation-url ""}}]
[false {:collectible-data {:image-url "" :animation-url ""}}]]]
(doseq [[result collection] expected-results]
(is (match? result (collectibles/displayable-collectible? collection))))))
(testing "save-collectibles-request-details"
(let [db {:wallet {:accounts {"0x1" {}
"0x3" {}}}}
collectible-1 {:collectible-data {:image-url "https://..." :animation-url "https://..."}
:ownership [{:address "0x1"
:balance "1"}]}
collectible-2 {:collectible-data {:image-url "" :animation-url "https://..."}
@@ -66,18 +82,12 @@
collectible-3 {:collectible-data {:image-url "" :animation-url nil}
:ownership [{:address "0x2"
:balance "1"}]}
db {:wallet {:ui {:collectibles {:pending-requests 0
:fetched {"0x1" [collectible-1
collectible-2]
"0x2" [collectible-3]}}}
:accounts {"0x1" {}
"0x3" {}}}}
expected-db {:wallet {:ui {:collectibles {}}
:accounts {"0x1" {:collectibles (list collectible-1 collectible-2)}
collectibles [collectible-1 collectible-2 collectible-3]
expected-db {:wallet {:accounts {"0x1" {:collectibles (list collectible-2 collectible-1)}
"0x2" {:collectibles (list collectible-3)}
"0x3" {}}}}
result-db (:db (collectibles/flush-collectibles {:db db}))]
effects (collectibles/store-collectibles {:db db} [collectibles])
result-db (:db effects)]
(is (match? result-db expected-db)))))
(deftest clear-stored-collectibles
@@ -9,15 +9,12 @@
(defn view
[{:keys [selected-tab]}]
(let [collectible-list (rf/sub [:wallet/all-collectibles-list])
request-collectibles #(rf/dispatch
[:wallet/request-collectibles-for-all-accounts {}])]
(let [collectible-list (rf/sub [:wallet/all-collectibles])]
[rn/view {:style style/container}
(case selected-tab
:assets [assets/view]
:collectibles [collectibles/view
{:collectibles collectible-list
:on-end-reached request-collectibles
:on-collectible-press (fn [{:keys [id]}]
(rf/dispatch [:wallet/get-collectible-details id]))}]
[activity/view])]))
@@ -1,5 +1,6 @@
(ns status-im.contexts.wallet.send.events
(:require
[camel-snake-kebab.core :as csk]
[camel-snake-kebab.extras :as cske]
[clojure.string :as string]
[native-module.core :as native-module]
@@ -10,8 +11,7 @@
[utils.address :as address]
[utils.money :as money]
[utils.number]
[utils.re-frame :as rf]
[utils.transforms :as transforms]))
[utils.re-frame :as rf]))
(rf/reg-event-fx :wallet/clean-send-data
(fn [{:keys [db]}]
@@ -24,7 +24,7 @@
(rf/reg-event-fx :wallet/suggested-routes-success
(fn [{:keys [db]} [suggested-routes timestamp]]
(when (= (get-in db [:wallet :ui :send :suggested-routes-call-timestamp]) timestamp)
(let [suggested-routes-data (cske/transform-keys transforms/->kebab-case-keyword suggested-routes)
(let [suggested-routes-data (cske/transform-keys csk/->kebab-case suggested-routes)
chosen-route (:best suggested-routes-data)]
{:db (-> db
(assoc-in [:wallet :ui :send :suggested-routes] suggested-routes-data)
@@ -90,7 +90,7 @@
(rf/reg-event-fx :wallet/clean-selected-token
(fn [{:keys [db]}]
{:db (assoc-in db [:wallet :ui :send :token] nil)}))
{:db (update-in db [:wallet :ui :send] dissoc :token :type)}))
(rf/reg-event-fx :wallet/clean-selected-collectible
(fn [{:keys [db]}]
@@ -23,47 +23,52 @@
(defn- address-input
[input-value input-focused?]
(fn []
(let [current-screen-id (rf/sub [:navigation/current-screen-id])
scanned-address (rf/sub [:wallet/scanned-address])
send-address (rf/sub [:wallet/wallet-send-to-address])
recipient (rf/sub [:wallet/wallet-send-recipient])
recipient-plain-address? (= send-address recipient)
valid-ens-or-address? (rf/sub [:wallet/valid-ens-or-address?])
chain-id (rf/sub [:chain-id])
contacts (rf/sub [:contacts/active])]
[quo/address-input
{:on-focus #(reset! input-focused? true)
:on-blur #(reset! input-focused? false)
:on-scan (fn []
(rn/dismiss-keyboard!)
(rf/dispatch [:wallet/clean-scanned-address])
(rf/dispatch [:open-modal :scan-address]))
:ens-regex constants/regx-ens
:scanned-value (or (when recipient-plain-address? send-address) scanned-address)
:address-regex constants/regx-multichain-address
:on-detect-address #(when (or (= current-screen-id :wallet-select-address)
(= current-screen-id :scan-address))
; ^ this check is to prevent effect being triggered when screen is
; loaded but not being shown to the user (deep in the navigation
; stack) and avoid undesired behaviors
(debounce/debounce-and-dispatch
[:wallet/validate-address %]
300))
:on-detect-ens (fn [text cb]
(when (or (= current-screen-id :wallet-select-address)
(= current-screen-id :scan-address))
; ^ this check is to prevent effect being triggered when screen
; is loaded but not being shown to the user (deep in the
; navigation stack) and avoid undesired behaviors
(debounce/debounce-and-dispatch
[:wallet/find-ens text contacts chain-id cb]
300)))
:on-change-text (fn [text]
(when (empty? text)
(rf/dispatch [:wallet/clean-local-suggestions]))
(reset! input-value text))
:valid-ens-or-address? valid-ens-or-address?}])))
(let [set-value-fn (atom nil)]
(fn []
(let [current-screen-id (rf/sub [:navigation/current-screen-id])
scanned-address (rf/sub [:wallet/scanned-address])
send-address (rf/sub [:wallet/wallet-send-to-address])
recipient (rf/sub [:wallet/wallet-send-recipient])
recipient-plain-address? (= send-address recipient)
valid-ens-or-address? (rf/sub [:wallet/valid-ens-or-address?])
chain-id (rf/sub [:chain-id])
contacts (rf/sub [:contacts/active])]
(when (and @set-value-fn scanned-address)
(@set-value-fn scanned-address)
(reset! set-value-fn nil))
[quo/address-input
{:on-focus #(reset! input-focused? true)
:on-blur #(reset! input-focused? false)
:on-scan (fn [set-value]
(reset! set-value-fn set-value)
(rn/dismiss-keyboard!)
(rf/dispatch [:wallet/clean-scanned-address])
(rf/dispatch [:open-modal :scan-address]))
:ens-regex constants/regx-ens
:default-value (when recipient-plain-address? send-address)
:address-regex constants/regx-multichain-address
:on-detect-address #(when (or (= current-screen-id :wallet-select-address)
(= current-screen-id :scan-address))
; ^ this check is to prevent effect being triggered when screen
; is loaded but not being shown to the user (deep in the
; navigation stack) and avoid undesired behaviors
(debounce/debounce-and-dispatch
[:wallet/validate-address %]
300))
:on-detect-ens (fn [text cb]
(when (or (= current-screen-id :wallet-select-address)
(= current-screen-id :scan-address))
; ^ this check is to prevent effect being triggered when screen
; is loaded but not being shown to the user (deep in the
; navigation stack) and avoid undesired behaviors
(debounce/debounce-and-dispatch
[:wallet/find-ens text contacts chain-id cb]
300)))
:on-change-text (fn [text]
(when (empty? text)
(rf/dispatch [:wallet/clean-local-suggestions]))
(reset! input-value text))
:valid-ens-or-address? valid-ens-or-address?}]))))
(defn- ens-linked-address
[{:keys [address networks theme]}]
@@ -33,7 +33,6 @@
[collectibles-tab/view
{:collectibles collectibles
:filtered? search-performed?
:on-end-reached #(rf/dispatch [:wallet/request-collectibles-for-current-viewing-account])
:on-collectible-press #(rf/dispatch [:wallet/send-select-collectible
{:collectible %
:stack-id :wallet-select-asset}])}]))
@@ -48,14 +48,11 @@
(defn view
[]
(let [padding-top (:top (safe-area/get-insets))
wallet-type (reagent/atom :legacy)
;; Design team is yet to confirm the default selected networks here. Should be the current
;; selected for the account or all the networks always
selected-networks (reagent/atom [:ethereum :optimism :arbitrum])
on-settings-press #(open-preferences selected-networks)
on-legacy-press #(reset! wallet-type :legacy)
on-multichain-press #(reset! wallet-type :multichain)]
(let [padding-top (:top (safe-area/get-insets))
wallet-type (reagent/atom :legacy)
;; Design team is yet to confirm the default selected networks here.
;; Should be the current selected for the account or all the networks always
selected-networks (reagent/atom [:ethereum :optimism :arbitrum])]
(fn []
(let [{:keys [address color emoji watch-only?]
:as account} (rf/sub [:wallet/current-viewing-account])
@@ -100,6 +97,6 @@
:full-name (:name account)
:customization-color color
:emoji emoji
:on-legacy-press on-legacy-press
:on-multichain-press on-multichain-press
:on-settings-press on-settings-press}]]]]))))
:on-legacy-press #(reset! wallet-type :legacy)
:on-multichain-press #(reset! wallet-type :multichain)
:on-settings-press #(open-preferences selected-networks)}]]]]))))
@@ -52,9 +52,10 @@
:overlay-color (quo.theme/theme-value colors/white-70-blur
colors/neutral-95-opa-70-blur
theme)}])
[quo/gradient-cover
{:customization-color color
:opacity 0.4}]
[rn/view {:style style/gradient-container}
[quo/gradient-cover
{:customization-color color
:opacity 0.4}]]
[quo/drawer-bar]
[quo/drawer-top
(cond-> {:title name
+15 -11
View File
@@ -2,13 +2,12 @@
(:require
[oops.core :as oops]
[taoensso.timbre :as log]
[utils.re-frame :as rf]
[utils.transforms :as transforms]))
[utils.re-frame :as rf]))
(rf/reg-event-fx
:wallet/pending-transaction-status-changed-received
(fn [{:keys [db]} [{:keys [message]}]]
(let [details (transforms/json->clj message)
(let [details (js->clj (js/JSON.parse message) :keywordize-keys true)
tx-hash (:hash details)]
{:db (update-in db [:wallet :transactions tx-hash] assoc :status :confirmed :blocks 1)})))
@@ -26,17 +25,22 @@
"pending-transaction-status-changed" {:fx
[[:dispatch
[:wallet/pending-transaction-status-changed-received
(transforms/js->clj event-js)]]]}
(js->clj event-js
:keywordize-keys
true)]]]}
"wallet-owned-collectibles-filtering-done" {:fx [[:dispatch
[:wallet/owned-collectibles-filtering-done
(transforms/js->clj event-js)]]]}
(js->clj event-js
:keywordize-keys
true)]]]}
"wallet-get-collectibles-details-done" {:fx [[:dispatch
[:wallet/get-collectible-details-done
(transforms/js->clj event-js)]]]}
(js->clj event-js
:keywordize-keys
true)]]]}
"wallet-tick-reload" {:fx [[:dispatch [:wallet/reload]]]}
"wallet-blockchain-status-changed" {:fx [[:dispatch
[:wallet/blockchain-status-changed
(transforms/js->clj event-js)]]]}
(log/debug ::unknown-wallet-event
:type event-type
:event (transforms/js->clj event-js))))))
:type type
:event (js->clj event-js
:keywordize-keys
true))))))
+13 -29
View File
@@ -4,6 +4,7 @@
[status-im.contexts.shell.jump-to.events :as shell.events]
[status-im.contexts.shell.jump-to.state :as shell.state]
[status-im.contexts.shell.jump-to.utils :as shell.utils]
[status-im.navigation.utils :as navigation.utils]
[utils.re-frame :as rf]))
(defn- all-screens-params
@@ -24,23 +25,17 @@
:dispatch-n [[:hide-bottom-sheet]]}
(shell.events/shell-navigate-to go-to-view-id screen-params nil nil)))
(defn- add-view-to-modals
[modal-view-ids new-id]
(if (seq modal-view-ids)
(conj modal-view-ids new-id)
modal-view-ids))
(rf/defn navigate-to-within-stack
{:events [:navigate-to-within-stack]}
[{:keys [db]} comp-id]
{:db (update db :modal-view-ids add-view-to-modals (first comp-id))
{:db (update db :modal-view-ids navigation.utils/add-view-to-modal-stack (first comp-id))
:fx [[:navigate-to-within-stack comp-id]]})
(re-frame/reg-event-fx :open-modal
(fn [{:keys [db]} [component screen-params]]
{:db (-> db
(assoc :view-id component)
(assoc :modal-view-ids [component])
(update :modal-view-ids navigation.utils/add-stack-to-modal-stacks component)
(all-screens-params component screen-params))
:fx [[:dispatch [:hide-bottom-sheet]]
[:open-modal-fx component]]}))
@@ -48,7 +43,7 @@
(rf/defn dismiss-modal
{:events [:dismiss-modal]}
[{:keys [db]} comp-id]
{:db (dissoc db :modal-view-ids)
{:db (update db :modal-view-ids navigation.utils/remove-current-modal-stack)
:dismiss-modal comp-id})
(rf/defn navigate-back
@@ -56,30 +51,17 @@
[cofx]
(shell.events/shell-navigate-back cofx nil))
(defn- remove-last-view-to-modals
[modal-view-ids]
(if (seq modal-view-ids)
(pop modal-view-ids)
modal-view-ids))
(rf/defn navigate-back-within-stack
{:events [:navigate-back-within-stack]}
[{:keys [db]} comp-id]
{:db (update db :modal-view-ids remove-last-view-to-modals)
{:db (update db :modal-view-ids navigation.utils/remove-last-view-from-current-modal-stack)
:fx [[:navigate-back-within-stack comp-id]]})
(defn- remove-modal-views-until-comp-id
[modal-view-ids comp-id]
(let [comp-id-index (.indexOf (or modal-view-ids []) comp-id)
modal-view-ids (if (> comp-id-index -1)
(subvec modal-view-ids 0 (inc comp-id-index))
modal-view-ids)]
modal-view-ids))
(rf/defn navigate-back-to
{:events [:navigate-back-to]}
[{:keys [db]} comp-id]
(let [modal-view-ids (remove-modal-views-until-comp-id (:modal-view-ids db) comp-id)]
(let [modal-view-ids (navigation.utils/remove-views-from-modal-stack-until-comp-id (:modal-view-ids db)
comp-id)]
(assoc {:navigate-back-to comp-id}
:db
(if modal-view-ids
@@ -99,13 +81,15 @@
(rf/defn init-root
{:events [:init-root]}
[_ root-id]
{:set-root root-id})
[{:keys [db]} root-id]
{:set-root root-id
:db (dissoc db :modal-view-ids)})
(rf/defn set-stack-root
{:events [:set-stack-root]}
[_ stack root]
{:set-stack-root-fx [stack root]})
[{:keys [db]} stack root]
{:set-stack-root-fx [stack root]
:db (dissoc db :modal-view-ids)})
(rf/defn change-tab
{:events [:navigate-change-tab]}
+44
View File
@@ -0,0 +1,44 @@
(ns status-im.navigation.utils)
(defn add-view-to-modal-stack
[modal-view-ids new-id]
(if-let [current-stack (and (seq modal-view-ids) (last modal-view-ids))]
(let [updated-stack (conj current-stack new-id)
without-last (vec (butlast modal-view-ids))]
(conj without-last updated-stack))
modal-view-ids))
(defn remove-last-view-from-current-modal-stack
[modal-view-ids]
(if (empty? modal-view-ids)
modal-view-ids
(let [last-stack (last modal-view-ids)
updated-last-stack (vec (butlast last-stack))
without-last-stack (vec (butlast modal-view-ids))]
(if (empty? updated-last-stack)
without-last-stack
(conj without-last-stack updated-last-stack)))))
(defn add-stack-to-modal-stacks
[modal-view-ids first-view-id]
(if (seq modal-view-ids)
(conj modal-view-ids [first-view-id])
[[first-view-id]]))
(defn remove-current-modal-stack
[modal-view-ids]
(if (seq modal-view-ids)
(vec (butlast modal-view-ids))
[]))
(defn remove-views-from-modal-stack-until-comp-id
[modal-view-ids comp-id]
(let [found-index (first (keep-indexed (fn [idx stack]
(when (some #{comp-id} stack) idx))
modal-view-ids))]
(if found-index
(let [target-stack (nth modal-view-ids found-index)
comp-id-index (.lastIndexOf target-stack comp-id)
updated-stack (take (inc comp-id-index) target-stack)]
(assoc modal-view-ids found-index updated-stack))
modal-view-ids)))
+67
View File
@@ -0,0 +1,67 @@
(ns status-im.navigation.utils-test
(:require [cljs.test :refer [deftest is testing]]
[status-im.navigation.utils :as utils]))
(deftest test-add-view-to-modal-stack
(testing "Add view ID to an empty modal stack"
(is (= (utils/add-view-to-modal-stack [] 1)
[])))
(testing "Add view ID to a non-empty modal stack"
(is (= (utils/add-view-to-modal-stack [[1 2]] 3)
[[1 2 3]])))
(testing "Add view ID when multiple modal stacks exist"
(is (= (utils/add-view-to-modal-stack [[1 2] [3 4]] 5)
[[1 2] [3 4 5]]))))
(deftest test-remove-last-view-from-current-modal-stack
(testing "Remove last view ID from a single-element stack"
(is (= (utils/remove-last-view-from-current-modal-stack [[1]])
[])))
(testing "Remove last view ID from the current modal stack"
(is (= (utils/remove-last-view-from-current-modal-stack [[1 2 3] [4 5 6]])
[[1 2 3] [4 5]])))
(testing "Handle empty modal stacks"
(is (= (utils/remove-last-view-from-current-modal-stack [])
[])))
(testing "Ensure unchanged stack when nested stack is a single element"
(is (= (utils/remove-last-view-from-current-modal-stack [[1 2] [3]])
[[1 2]]))))
(deftest test-add-stack-to-modal-stacks
(testing "Add a new stack to an empty list of modal stacks"
(is (= (utils/add-stack-to-modal-stacks [] 1)
[[1]])))
(testing "Add a new stack to existing modal stacks"
(is (= (utils/add-stack-to-modal-stacks [[1 2]] 3)
[[1 2] [3]])))
(testing "Add a new stack when existing stacks are varied"
(is (= (utils/add-stack-to-modal-stacks [[1 2] [3 4]] 5)
[[1 2] [3 4] [5]]))))
(deftest test-remove-current-modal-stack
(testing "Remove the only modal stack"
(is (= (utils/remove-current-modal-stack [[1 2]])
[])))
(testing "Remove the last modal stack from multiple"
(is (= (utils/remove-current-modal-stack [[1 2] [3 4] [5 6]])
[[1 2] [3 4]])))
(testing "Handle removal when modal stacks are empty"
(is (= (utils/remove-current-modal-stack [])
[])))
(testing "Ensure unchanged when nested stacks have varied lengths"
(is (= (utils/remove-current-modal-stack [[1] [2 3] [4 5 6]])
[[1] [2 3]]))))
(deftest test-remove-views-from-modal-stack-until-comp-id
(testing "Remove views until comp-id, including multiple occurrences"
(is (= (utils/remove-views-from-modal-stack-until-comp-id [[1 2 3] [4 5 5 6]] 5)
[[1 2 3] [4 5 5]])))
(testing "comp-id at the beginning of a stack"
(is (= (utils/remove-views-from-modal-stack-until-comp-id [[1 2] [3 4] [5 6 7]] 5)
[[1 2] [3 4] [5]])))
(testing "comp-id not found, no removal"
(is (= (utils/remove-views-from-modal-stack-until-comp-id [[1 2 3] [4 5 6]] 7)
[[1 2 3] [4 5 6]])))
(testing "comp-id is the last element in a stack"
(is (= (utils/remove-views-from-modal-stack-until-comp-id [[1 2 3] [4 5 6] [7 8 9]] 9)
[[1 2 3] [4 5 6] [7 8 9]]))))
+2 -10
View File
@@ -423,19 +423,11 @@
(fn [previews]
(get previews :unfurled)))
(re-frame/reg-sub
:chats/status-link-previews-unfurled
:<- [:chat/status-link-previews]
(fn [previews]
(:unfurled previews)))
(re-frame/reg-sub
:chats/link-previews?
:<- [:chats/link-previews-unfurled]
:<- [:chats/status-link-previews-unfurled]
(fn [previews status-link-previews]
(boolean (or (seq status-link-previews)
(seq previews)))))
(fn [previews]
(boolean (seq previews))))
(re-frame/reg-sub
:chat/check-channel-muted?
+10 -28
View File
@@ -94,19 +94,6 @@
(def memo-communities-stack-items (atom nil))
(defn- merge-opened-communities
[{:keys [joined pending] :as assorted-communities}]
(update assorted-communities :opened concat joined pending))
(defn- group-communities-by-status
[requests
{:keys [id]
:as community}]
(cond
(:joined community) :joined
(boolean (get requests id)) :pending
:else :opened))
(re-frame/reg-sub
:communities/grouped-by-status
:<- [:view-id]
@@ -117,21 +104,16 @@
;; in app-db. Result map has form: {:joined [id1, id2] :pending [id3, id5] :opened [id4]}"
(fn [[view-id communities requests]]
(if (or (empty? @memo-communities-stack-items) (= view-id :communities-stack))
(let [grouped-communities (->> communities
vals
(group-by #(group-communities-by-status requests %))
merge-opened-communities
(map (fn [[k v]]
{k (sort-by (fn [{:keys [requested-to-join-at last-opened-at
joined-at]}]
(condp = k
:joined joined-at
:pending requested-to-join-at
:opened last-opened-at
last-opened-at))
#(compare %2 %1)
v)}))
(into {}))]
(let [grouped-communities (reduce (fn [acc community]
(let [joined? (:joined community)
community-id (:id community)
pending? (boolean (get requests community-id))]
(cond
joined? (update acc :joined conj community)
pending? (update acc :pending conj community)
:else (update acc :opened conj community))))
{:joined [] :pending [] :opened []}
(vals communities))]
(reset! memo-communities-stack-items grouped-communities)
grouped-communities)
@memo-communities-stack-items)))
-15
View File
@@ -102,21 +102,6 @@
(fn [messages [_ chat-id message-id]]
(get-in messages [chat-id message-id :link-previews])))
(re-frame/reg-sub
:chats/message-link-previews?
:<- [:messages/messages]
(fn [messages [_ chat-id message-id]]
(-> messages
(get-in [chat-id message-id :link-previews])
count
pos?)))
(re-frame/reg-sub
:chats/message-status-link-previews
:<- [:messages/messages]
(fn [messages [_ chat-id message-id]]
(get-in messages [chat-id message-id :status-link-previews])))
(re-frame/reg-sub
:chats/pinned
:<- [:messages/pin-messages]
-16
View File
@@ -159,19 +159,3 @@
:pinned-at nil
:pinned-by :test-user}]
(rf/sub [sub-name :0xChat])))))
(def message-with-link-previews
{:0xChat {:0x01 {:content {:response-to nil}
:quoted-message nil
:message-id "0x1"
:from :xyz
:timestamp-str "14:00"
:content-type constants/content-type-text
:chat-id :0xChat
:link-previews [{:dummy-link-preview 1}]}}})
(h/deftest-sub :chats/message-link-previews?
[sub-name]
(testing "It returns true if link previews exists in a message"
(swap! rf-db/app-db assoc :messages message-with-link-previews)
(is (rf/sub [sub-name :0xChat :0x01]))))
+4 -1
View File
@@ -7,4 +7,7 @@
:<- [:view-id]
:<- [:modal-view-ids]
(fn [[view-id modal-view-ids]]
(or (peek modal-view-ids) view-id)))
(or (some-> modal-view-ids
last
last)
view-id)))
+6 -14
View File
@@ -45,21 +45,13 @@
(-> current-account :collectibles add-collectibles-preview-url)))
(re-frame/reg-sub
:wallet/all-collectibles-list
:wallet/all-collectibles
:<- [:wallet]
(fn [{:keys [accounts]}]
(let [max-collectibles (->> accounts
(map (comp count :collectibles val))
(apply max))
all-collectibles (map (fn [[_address {:keys [collectibles]}]]
(let [amount-to-add (- max-collectibles (count collectibles))
empty-collectibles (repeat amount-to-add nil)]
(reduce conj collectibles empty-collectibles)))
accounts)]
(->> all-collectibles
(apply interleave)
(remove nil?)
(add-collectibles-preview-url)))))
(fn [wallet]
(->> wallet
:accounts
(mapcat (comp :collectibles val))
(add-collectibles-preview-url))))
(re-frame/reg-sub
:wallet/current-viewing-account-collectibles-filtered
+1 -5
View File
@@ -213,14 +213,10 @@
"Add with defaults, this version is able to receive `nil` and takes them as 0."
(fnil add* (bignumber 0) (bignumber 0)))
(defn- mul*
(defn mul
[bn1 bn2]
(.mul ^js bn1 bn2))
(def mul
"Multiply with defaults, this version is able to receive `nil` and takes them as 0."
(fnil mul* (bignumber 0) (bignumber 0)))
(defn mul-and-round
[bn1 bn2]
(.round (.mul ^js bn1 bn2) 0))
-4
View File
@@ -1,7 +1,6 @@
(ns utils.transforms
(:refer-clojure :exclude [js->clj])
(:require
[camel-snake-kebab.core :as csk]
[cljs-bean.core :as clj-bean]
[oops.core :as oops]
[reagent.impl.template :as reagent.template]
@@ -21,9 +20,6 @@
(try (js->clj (.parse js/JSON json))
(catch js/Error _ (when (string? json) json)))))
(def ->kebab-case-keyword (memoize csk/->kebab-case-keyword))
(def ->PascalCaseKeyword (memoize csk/->PascalCaseKeyword))
(defn json->js
[json]
(when-not (= json "undefined")
+3 -3
View File
@@ -3,7 +3,7 @@
"_comment": "Instead use: scripts/update-status-go.sh <rev>",
"owner": "status-im",
"repo": "status-go",
"version": "add-favicons-to-link-previews",
"commit-sha1": "ab85a9591b5ee08ebed16fed83bd53ffc1b43de3",
"src-sha256": "1ldgzvsn8hv3k4r35l1x7zhzl8xmi72bbz1wsr7jmj5x8mmlfbwm"
"version": "v0.176.2",
"commit-sha1": "00f50a4112ef80cc29737f8edf34cdb1cf181709",
"src-sha256": "12z9qf0hhb2pc7ask6f86pah0wjp3kyxyv32rpfnkjl42maw0bw6"
}
+2
View File
@@ -309,6 +309,8 @@ class ProfileView(BaseView):
xpath="//android.widget.TextView[contains(@text,'with upstream RPC')]")
self.plus_button = Button(self.driver, xpath="(//android.widget.ImageView[@content-desc='icon'])[2]")
self.custom_chain_button = Button(self.driver, translation_id="custom")
self.custom_network_url_input = EditBox(self.driver, translation_id="rpc-url",
suffix="/following-sibling::*[1]/android.widget.EditText")
self.custom_network_symbol_input = EditBox(self.driver, translation_id="specify-symbol")
self.specify_name_input = EditBox(self.driver, translation_id="name",
suffix="/following-sibling::*[1]/android.widget.EditText")

Some files were not shown because too many files have changed in this diff Show More