diff --git a/.env b/.env index 0f41fc4bb0..3ed6d3c318 100644 --- a/.env +++ b/.env @@ -17,4 +17,4 @@ DEBUG_WEBVIEW=1 INSTABUG_SURVEYS=1 GROUP_CHATS_ENABLED=0 USE_SYM_KEY=0 -SPAM_BUTTON_DETECTION_ENABLED=1 +SPAM_BUTTON_DETECTION_ENABLED=1 \ No newline at end of file diff --git a/.env.e2e b/.env.e2e index 65433625fd..b0a4481eac 100644 --- a/.env.e2e +++ b/.env.e2e @@ -16,4 +16,4 @@ INSTABUG_TOKEN=758630ed52864cbad9c5eeeac596c60c DEBUG_WEBVIEW=1 INSTABUG_SURVEYS=0 GROUP_CHATS_ENABLED=1 -USE_SYM_KEY=0 +USE_SYM_KEY=0 \ No newline at end of file diff --git a/.env.jenkins b/.env.jenkins index 339f042fe7..95c63759a9 100644 --- a/.env.jenkins +++ b/.env.jenkins @@ -18,4 +18,4 @@ INSTABUG_SURVEYS=1 GROUP_CHATS_ENABLED=0 USE_SYM_KEY=0 SPAM_BUTTON_DETECTION_ENABLED=1 -MAINNET_WARNING_ENABLED=1 +MAINNET_WARNING_ENABLED=1 \ No newline at end of file diff --git a/.env.nightly b/.env.nightly index 74243ae611..c9be838cf8 100644 --- a/.env.nightly +++ b/.env.nightly @@ -17,4 +17,4 @@ DEBUG_WEBVIEW=1 INSTABUG_SURVEYS=1 GROUP_CHATS_ENABLED=0 SPAM_BUTTON_DETECTION_ENABLED=1 -MAINNET_WARNING_ENABLED=1 +MAINNET_WARNING_ENABLED=1 \ No newline at end of file diff --git a/.env.prod b/.env.prod index 5800678c76..03acd57be3 100644 --- a/.env.prod +++ b/.env.prod @@ -20,4 +20,4 @@ GROUP_CHATS_ENABLED=0 USE_SYM_KEY=0 MAINNET_WARNING_ENABLED=1 SPAM_BUTTON_DETECTION_ENABLED=1 -UNIVERSAL_LINKS_ENABLED=0 +UNIVERSAL_LINKS_ENABLED=0 \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 2211aa1090..be636eaaf3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ### Added - Added Farsi public #status channel - Spam moderation +- Collectibles support (CryptoKitties, CryptoStrikers and Etheremon) ## [0.9.21] - 2018-06-25 ### Added diff --git a/src/status_im/ui/components/svgimage.cljs b/src/status_im/ui/components/svgimage.cljs index 82b44ff62d..4f1eb0e600 100644 --- a/src/status_im/ui/components/svgimage.cljs +++ b/src/status_im/ui/components/svgimage.cljs @@ -1,7 +1,8 @@ (ns status-im.ui.components.svgimage (:require [status-im.ui.components.react :as react] [reagent.core :as reagent] - [status-im.utils.platform :as platform])) + [status-im.utils.platform :as platform] + [status-im.utils.http :as http])) (defn html [uri width height] (str @@ -38,7 +39,7 @@ (defn svgimage [{:keys [style source]}] (let [width (reagent/atom nil) {:keys [uri k] :or {k 1}} source] - (when (re-find #"^(https:)([/|.|\w|\s|-])*\.(?:jpg|svg|png)$" uri) + (when (http/url-sanitized? uri) (fn [] [react/view {:style style :on-layout #(reset! width (-> % .-nativeEvent .-layout .-width))} diff --git a/src/status_im/ui/screens/db.cljs b/src/status_im/ui/screens/db.cljs index 90f2a7f55e..5fed9f4f52 100644 --- a/src/status_im/ui/screens/db.cljs +++ b/src/status_im/ui/screens/db.cljs @@ -114,7 +114,7 @@ (spec/def :navigation.screen-params/usage-data vector?) -(spec/def :navigation.screen-params/display-collectible map?) +(spec/def :navigation.screen-params/collectibles-list map?) (spec/def :navigation/screen-params (spec/nilable (allowed-keys :opt-un [:navigation.screen-params/network-details :navigation.screen-params/browser @@ -124,7 +124,7 @@ :navigation.screen-params/edit-contact-group :navigation.screen-params/dapp-description :navigation.screen-params/usage-data - :navigation.screen-params/display-collectible]))) + :navigation.screen-params/collectibles-list]))) (spec/def :desktop/desktop (spec/nilable any?)) diff --git a/src/status_im/ui/screens/events.cljs b/src/status_im/ui/screens/events.cljs index 6c20e32cbd..9d3fa32f1a 100644 --- a/src/status_im/ui/screens/events.cljs +++ b/src/status_im/ui/screens/events.cljs @@ -26,6 +26,9 @@ status-im.ui.screens.wallet.settings.events status-im.ui.screens.wallet.transactions.events status-im.ui.screens.wallet.choose-recipient.events + status-im.ui.screens.wallet.collectibles.cryptokitties.events + status-im.ui.screens.wallet.collectibles.cryptostrikers.events + status-im.ui.screens.wallet.collectibles.etheremon.events status-im.ui.screens.browser.events status-im.ui.screens.offline-messaging-settings.events status-im.ui.screens.bootnodes-settings.events diff --git a/src/status_im/ui/screens/views.cljs b/src/status_im/ui/screens/views.cljs index e353037589..5e293d9b09 100644 --- a/src/status_im/ui/screens/views.cljs +++ b/src/status_im/ui/screens/views.cljs @@ -25,7 +25,7 @@ [status-im.ui.screens.profile.contact.views :as profile.contact] [status-im.ui.screens.profile.group-chat.views :as profile.group-chat] [status-im.ui.screens.profile.photo-capture.views :refer [profile-photo-capture]] - [status-im.ui.screens.wallet.collectibles.views :as collectibles] + [status-im.ui.screens.wallet.collectibles.views :refer [collectibles-list]] [status-im.ui.screens.wallet.send.views :refer [send-transaction send-transaction-modal sign-message-modal]] [status-im.ui.screens.wallet.choose-recipient.views :refer [choose-recipient]] [status-im.ui.screens.wallet.request.views :refer [request-transaction send-transaction-request]] @@ -54,7 +54,7 @@ (defn get-main-component [view-id] (case view-id - :display-collectible collectibles/display-collectible + :collectibles-list collectibles-list :intro intro :create-account create-account :usage-data usage-data diff --git a/src/status_im/ui/screens/wallet/collectibles/cryptokitties.cljs b/src/status_im/ui/screens/wallet/collectibles/cryptokitties.cljs deleted file mode 100644 index b57a7e9738..0000000000 --- a/src/status_im/ui/screens/wallet/collectibles/cryptokitties.cljs +++ /dev/null @@ -1,62 +0,0 @@ -(ns status-im.ui.screens.wallet.collectibles.cryptokitties - (:require [re-frame.core :as re-frame] - [status-im.i18n :as i18n] - [status-im.ui.components.action-button.action-button :as action-button] - [status-im.ui.components.colors :as colors] - [status-im.ui.components.react :as react] - [status-im.ui.screens.wallet.collectibles.views :as collectibles] - [status-im.ui.screens.wallet.collectibles.styles :as styles] - [status-im.utils.handlers :as handlers] - [status-im.utils.http :as http] - [status-im.ui.components.svgimage :as svgimage])) - -(def ck :CK) - -(handlers/register-handler-fx - :load-kitties - (fn [{db :db} [_ ids]] - {:db db - :http-get-n (mapv (fn [id] - {:url (str "https://api.cryptokitties.co/kitties/" id) - :success-event-creator (fn [o] - [:load-collectible-success ck {id (http/parse-payload o)}]) - :failure-event-creator (fn [o] - [:load-collectible-failure ck {id (http/parse-payload o)}])}) - ids)})) - -(defn kitties-url [address] - (str "https://api.cryptokitties.co/kitties?offset=0&limit=20&owner_wallet_address=" address "&parents=false")) - -(handlers/register-handler-fx - :load-kitties-success - (fn [{db :db} [_ ids]] - {:db db - :dispatch [:load-kitties ids]})) - -;; TODO(julien) Each HTTP call will return up to 20 kitties. Make sure all extra kitties are fetched -(defmethod collectibles/load-collectibles-fx ck [_ _ _ address] - {:http-get {:url (kitties-url address) - :success-event-creator (fn [o] - [:load-kitties-success (map :id (:kitties (http/parse-payload o)))]) - :failure-event-creator (fn [o] - [:load-collectibles-failure (http/parse-payload o)]) - :timeout-ms 10000}}) - -(def base-url "https://www.cryptokitties.co/kitty/") - -(defmethod collectibles/render-collectible ck [_ {:keys [id name bio image_url]}] - [react/view {:style styles/details} - [react/view {:style styles/details-text} - [svgimage/svgimage {:style styles/details-image - :source {:uri image_url}}] - [react/view {:flex 1} - [react/text {:style styles/details-name} - (or name (i18n/label :t/cryptokitty-name {:id id}))] - [react/text {:number-of-lines 3 - :ellipsize-mode :tail} - bio]]] - [action-button/action-button {:label (i18n/label :t/view-cryptokitties) - :icon :icons/address - :icon-opts {:color colors/blue} - :accessibility-label :open-collectible-button - :on-press #(re-frame/dispatch [:open-browser {:url (str base-url id)}])}]]) diff --git a/src/status_im/ui/screens/wallet/collectibles/cryptokitties/events.cljs b/src/status_im/ui/screens/wallet/collectibles/cryptokitties/events.cljs new file mode 100644 index 0000000000..6325389839 --- /dev/null +++ b/src/status_im/ui/screens/wallet/collectibles/cryptokitties/events.cljs @@ -0,0 +1,31 @@ +(ns status-im.ui.screens.wallet.collectibles.cryptokitties.events + (:require [status-im.utils.handlers :as handlers] + [status-im.ui.screens.wallet.collectibles.events :as collectibles] + [status-im.utils.http :as http])) + +(def ck :CK) + +(handlers/register-handler-fx + :load-kitties + (fn [{db :db} [_ ids]] + {:db db + :http-get-n (mapv (fn [id] + {:url (str "https://api.cryptokitties.co/kitties/" id) + :success-event-creator (fn [o] + [:load-collectible-success ck {id (http/parse-payload o)}]) + :failure-event-creator (fn [o] + [:load-collectible-failure ck {id (http/parse-payload o)}])}) + ids)})) + +;; TODO(andrey) Each HTTP call will return up to 100 kitties. Maybe we need to implement some kind of paging later +(defmethod collectibles/load-collectibles-fx ck [_ _ items-number address] + {:http-get {:url (str "https://api.cryptokitties.co/kitties?offset=0&limit=" + items-number + "&owner_wallet_address=" + address + "&parents=false") + :success-event-creator (fn [o] + [:load-kitties (map :id (:kitties (http/parse-payload o)))]) + :failure-event-creator (fn [o] + [:load-collectibles-failure (http/parse-payload o)]) + :timeout-ms 10000}}) \ No newline at end of file diff --git a/src/status_im/ui/screens/wallet/collectibles/cryptokitties/views.cljs b/src/status_im/ui/screens/wallet/collectibles/cryptokitties/views.cljs new file mode 100644 index 0000000000..3b71f2995b --- /dev/null +++ b/src/status_im/ui/screens/wallet/collectibles/cryptokitties/views.cljs @@ -0,0 +1,28 @@ +(ns status-im.ui.screens.wallet.collectibles.cryptokitties.views + (:require [re-frame.core :as re-frame] + [status-im.i18n :as i18n] + [status-im.ui.components.action-button.action-button :as action-button] + [status-im.ui.components.colors :as colors] + [status-im.ui.components.react :as react] + [status-im.ui.screens.wallet.collectibles.styles :as styles] + [status-im.ui.screens.wallet.collectibles.views :as collectibles] + [status-im.ui.components.svgimage :as svgimage])) + +(defmethod collectibles/render-collectible :CK [_ {:keys [id name bio image_url]}] + [react/view {:style styles/details} + [react/view {:style styles/details-text} + [svgimage/svgimage {:style styles/details-image + :source {:uri image_url}}] + [react/view {:flex 1} + [react/text {:style styles/details-name} + (or name (i18n/label :t/cryptokitty-name {:id id}))] + [react/text {:number-of-lines 3 + :ellipsize-mode :tail} + bio]]] + [action-button/action-button + {:label (i18n/label :t/view-cryptokitties) + :icon :icons/address + :icon-opts {:color colors/blue} + :accessibility-label :open-collectible-button + :on-press #(re-frame/dispatch [:open-browser + {:url (str "https://www.cryptokitties.co/kitty/" id)}])}]]) \ No newline at end of file diff --git a/src/status_im/ui/screens/wallet/collectibles/cryptostrikers.cljs b/src/status_im/ui/screens/wallet/collectibles/cryptostrikers.cljs deleted file mode 100644 index cb731fe5f1..0000000000 --- a/src/status_im/ui/screens/wallet/collectibles/cryptostrikers.cljs +++ /dev/null @@ -1,36 +0,0 @@ -(ns status-im.ui.screens.wallet.collectibles.cryptostrikers - (:require [re-frame.core :as re-frame] - [status-im.i18n :as i18n] - [status-im.ui.components.action-button.action-button :as action-button] - [status-im.ui.components.colors :as colors] - [status-im.ui.components.react :as react] - [status-im.ui.screens.wallet.collectibles.styles :as styles] - [status-im.ui.screens.wallet.collectibles.views :as collectibles] - [status-im.utils.http :as http] - [status-im.ui.components.svgimage :as svgimage])) - -(def strikers :STRK) - -(defmethod collectibles/load-collectible-fx strikers [_ id] - {:http-get {:url (str "https://us-central1-cryptostrikers-prod.cloudfunctions.net/cards/" id) - :success-event-creator (fn [o] - [:load-collectible-success strikers {id (http/parse-payload o)}]) - :failure-event-creator (fn [o] - [:load-collectible-failure strikers {id (http/parse-payload o)}])}}) - -(defmethod collectibles/render-collectible strikers [_ {:keys [external_url description name image]}] - [react/view {:style styles/details} - [react/view {:style styles/details-text} - [svgimage/svgimage {:style styles/details-image - :source {:uri image - :k 1.4}}] - [react/view {:flex 1 :justify-content :center} - [react/text {:style styles/details-name} - name] - [react/text - description]]] - [action-button/action-button {:label (i18n/label :t/view-cryptostrikers) - :icon :icons/address - :icon-opts {:color colors/blue} - :accessibility-label :open-collectible-button - :on-press #(re-frame/dispatch [:open-browser {:url external_url}])}]]) diff --git a/src/status_im/ui/screens/wallet/collectibles/cryptostrikers/events.cljs b/src/status_im/ui/screens/wallet/collectibles/cryptostrikers/events.cljs new file mode 100644 index 0000000000..90f910544b --- /dev/null +++ b/src/status_im/ui/screens/wallet/collectibles/cryptostrikers/events.cljs @@ -0,0 +1,12 @@ +(ns status-im.ui.screens.wallet.collectibles.cryptostrikers.events + (:require [status-im.ui.screens.wallet.collectibles.events :as collectibles] + [status-im.utils.http :as http])) + +(def strikers :STRK) + +(defmethod collectibles/load-collectible-fx strikers [_ id] + {:http-get {:url (str "https://us-central1-cryptostrikers-prod.cloudfunctions.net/cards/" id) + :success-event-creator (fn [o] + [:load-collectible-success strikers {id (http/parse-payload o)}]) + :failure-event-creator (fn [o] + [:load-collectible-failure strikers {id (http/parse-payload o)}])}}) \ No newline at end of file diff --git a/src/status_im/ui/screens/wallet/collectibles/cryptostrikers/views.cljs b/src/status_im/ui/screens/wallet/collectibles/cryptostrikers/views.cljs new file mode 100644 index 0000000000..7552e6c9e2 --- /dev/null +++ b/src/status_im/ui/screens/wallet/collectibles/cryptostrikers/views.cljs @@ -0,0 +1,27 @@ +(ns status-im.ui.screens.wallet.collectibles.cryptostrikers.views + (:require [re-frame.core :as re-frame] + [status-im.i18n :as i18n] + [status-im.ui.components.action-button.action-button :as action-button] + [status-im.ui.components.colors :as colors] + [status-im.ui.components.react :as react] + [status-im.ui.screens.wallet.collectibles.styles :as styles] + [status-im.ui.components.svgimage :as svgimage] + [status-im.ui.screens.wallet.collectibles.views :as collectibles])) + +(defmethod collectibles/render-collectible :STRK [_ {:keys [external_url description name image]}] + [react/view {:style styles/details} + [react/view {:style styles/details-text} + [svgimage/svgimage {:style styles/details-image + :source {:uri image + :k 1.4}}] + [react/view {:flex 1 :justify-content :center} + [react/text {:style styles/details-name} + name] + [react/text + description]]] + [action-button/action-button + {:label (i18n/label :t/view-cryptostrikers) + :icon :icons/address + :icon-opts {:color colors/blue} + :accessibility-label :open-collectible-button + :on-press #(re-frame/dispatch [:open-browser {:url external_url}])}]]) diff --git a/src/status_im/ui/screens/wallet/collectibles/etheremon.cljs b/src/status_im/ui/screens/wallet/collectibles/etheremon.cljs deleted file mode 100644 index 312e4ef278..0000000000 --- a/src/status_im/ui/screens/wallet/collectibles/etheremon.cljs +++ /dev/null @@ -1,37 +0,0 @@ -(ns status-im.ui.screens.wallet.collectibles.etheremon - (:require [re-frame.core :as re-frame] - [status-im.i18n :as i18n] - [status-im.ui.components.action-button.action-button :as action-button] - [status-im.ui.components.colors :as colors] - [status-im.ui.components.react :as react] - [status-im.ui.screens.wallet.collectibles.styles :as styles] - [status-im.ui.screens.wallet.collectibles.views :as collectibles] - [status-im.utils.http :as http] - [status-im.ui.components.svgimage :as svgimage])) - -(def emona :EMONA) - -(defmethod collectibles/load-collectible-fx emona [_ id] - {:http-get {:url (str "https://www.etheremon.com/api/monster/get_data?monster_ids=" id) - :success-event-creator (fn [o] - [:load-collectible-success emona (:data (http/parse-payload o))]) - :failure-event-creator (fn [o] - [:load-collectible-failure emona {id (http/parse-payload o)}])}}) - -(def base-url "https://www.etheremon.com/#/mons/") - -(defmethod collectibles/render-collectible emona [_ {:keys [monster_id user_defined_name image]}] - [react/view {:style styles/details} - [react/view {:style styles/details-text} - [react/view {:flex 1} - [svgimage/svgimage {:style styles/details-image - :source {:uri image - :k 2}}]] - [react/view {:flex 1 :justify-content :center} - [react/text {:style styles/details-name} - user_defined_name]]] - [action-button/action-button {:label (i18n/label :t/view-etheremon) - :icon :icons/address - :icon-opts {:color colors/blue} - :accessibility-label :open-collectible-button - :on-press #(re-frame/dispatch [:open-browser {:url (str base-url monster_id)}])}]]) diff --git a/src/status_im/ui/screens/wallet/collectibles/etheremon/events.cljs b/src/status_im/ui/screens/wallet/collectibles/etheremon/events.cljs new file mode 100644 index 0000000000..396bd56ba9 --- /dev/null +++ b/src/status_im/ui/screens/wallet/collectibles/etheremon/events.cljs @@ -0,0 +1,12 @@ +(ns status-im.ui.screens.wallet.collectibles.etheremon.events + (:require [status-im.ui.screens.wallet.collectibles.events :as collectibles] + [status-im.utils.http :as http])) + +(def emona :EMONA) + +(defmethod collectibles/load-collectible-fx emona [_ id] + {:http-get {:url (str "https://www.etheremon.com/api/monster/get_data?monster_ids=" id) + :success-event-creator (fn [o] + [:load-collectible-success emona (:data (http/parse-payload o))]) + :failure-event-creator (fn [o] + [:load-collectible-failure emona {id (http/parse-payload o)}])}}) \ No newline at end of file diff --git a/src/status_im/ui/screens/wallet/collectibles/etheremon/views.cljs b/src/status_im/ui/screens/wallet/collectibles/etheremon/views.cljs new file mode 100644 index 0000000000..c1a09a034b --- /dev/null +++ b/src/status_im/ui/screens/wallet/collectibles/etheremon/views.cljs @@ -0,0 +1,27 @@ +(ns status-im.ui.screens.wallet.collectibles.etheremon.views + (:require [re-frame.core :as re-frame] + [status-im.i18n :as i18n] + [status-im.ui.components.action-button.action-button :as action-button] + [status-im.ui.components.colors :as colors] + [status-im.ui.components.react :as react] + [status-im.ui.screens.wallet.collectibles.styles :as styles] + [status-im.ui.components.svgimage :as svgimage] + [status-im.ui.screens.wallet.collectibles.views :as collectibles])) + +(defmethod collectibles/render-collectible :EMONA [_ {:keys [user_defined_name image class_id]}] + [react/view {:style styles/details} + [react/view {:style styles/details-text} + [react/view {:flex 1} + [svgimage/svgimage {:style styles/details-image + :source {:uri image + :k 2}}]] + [react/view {:flex 1 :justify-content :center} + [react/text {:style styles/details-name} + user_defined_name]]] + [action-button/action-button + {:label (i18n/label :t/view-etheremon) + :icon :icons/address + :icon-opts {:color colors/blue} + :accessibility-label :open-collectible-button + :on-press #(re-frame/dispatch [:open-browser + {:url (str "https://www.etheremon.com/#/mons/" class_id)}])}]]) diff --git a/src/status_im/ui/screens/wallet/collectibles/events.cljs b/src/status_im/ui/screens/wallet/collectibles/events.cljs index c957f8e47a..e7d1d797e4 100644 --- a/src/status_im/ui/screens/wallet/collectibles/events.cljs +++ b/src/status_im/ui/screens/wallet/collectibles/events.cljs @@ -1,6 +1,45 @@ (ns status-im.ui.screens.wallet.collectibles.events (:require [re-frame.core :as re-frame] - [status-im.utils.handlers :as handlers])) + [status-im.utils.handlers :as handlers] + [status-im.utils.ethereum.erc721 :as erc721] + [status-im.utils.ethereum.tokens :as tokens] + [status-im.utils.money :as money])) + +(defmulti load-collectible-fx (fn [symbol _] symbol)) + +(defmethod load-collectible-fx :default [_ _] nil) + +(defmulti load-collectibles-fx (fn [_ symbol _ _] symbol)) + +(defmethod load-collectibles-fx :default [web3 symbol items-number address] + {:load-collectibles-fx [web3 symbol items-number address]}) + +(handlers/register-handler-fx + :show-collectibles-list + (fn [{:keys [db]} [_ address {:keys [symbol amount] :as collectible}]] + (let [items-number (money/to-number amount) + loaded-items-number (count (get-in db [:collectibles symbol]))] + (merge (when (not= items-number loaded-items-number) + (load-collectibles-fx (:web3 db) symbol items-number address)) + {:dispatch [:navigate-to :collectibles-list collectible]})))) + +(defn load-token [web3 i items-number contract address symbol] + (when (< i items-number) + (erc721/token-of-owner-by-index web3 contract address i + (fn [v1 v2] + (load-token web3 (inc i) items-number contract address symbol) + (re-frame/dispatch [:load-collectible symbol (.toNumber v2)]))))) + +(re-frame/reg-fx + :load-collectibles-fx + (fn [[web3 symbol items-number address]] + (let [contract (:address (tokens/symbol->token :mainnet symbol))] + (load-token web3 0 items-number contract address symbol)))) + +(handlers/register-handler-fx + :load-collectible + (fn [_ [_ symbol token-id]] + (load-collectible-fx symbol token-id))) (handlers/register-handler-fx :load-collectible-success diff --git a/src/status_im/ui/screens/wallet/collectibles/subs.cljs b/src/status_im/ui/screens/wallet/collectibles/subs.cljs index b0c88f4bcf..d9a7428946 100644 --- a/src/status_im/ui/screens/wallet/collectibles/subs.cljs +++ b/src/status_im/ui/screens/wallet/collectibles/subs.cljs @@ -1,6 +1,11 @@ (ns status-im.ui.screens.wallet.collectibles.subs (:require [re-frame.core :as re-frame])) -(re-frame/reg-sub :collectibles - (fn [db [_ s]] - (vals (get-in db [:collectibles s])))) +(re-frame/reg-sub :collectibles :collectibles) + +(re-frame/reg-sub + :screen-collectibles + :<- [:collectibles] + :<- [:get-screen-params] + (fn [[collectibles {:keys [symbol]}]] + (mapv #(assoc (second %) :id (first %)) (get collectibles symbol)))) \ No newline at end of file diff --git a/src/status_im/ui/screens/wallet/collectibles/views.cljs b/src/status_im/ui/screens/wallet/collectibles/views.cljs index 0c60d6b1c1..f978ff7c3e 100644 --- a/src/status_im/ui/screens/wallet/collectibles/views.cljs +++ b/src/status_im/ui/screens/wallet/collectibles/views.cljs @@ -1,7 +1,6 @@ (ns status-im.ui.screens.wallet.collectibles.views (:require-macros [status-im.utils.views :refer [defview letsubs]]) - (:require [re-frame.core :as re-frame] - [status-im.ui.components.colors :as colors] + (:require [status-im.ui.components.colors :as colors] [status-im.ui.components.list.views :as list] [status-im.ui.components.react :as react] [status-im.ui.components.status-bar.view :as status-bar] @@ -9,33 +8,24 @@ [status-im.ui.components.toolbar.view :as toolbar] [status-im.ui.screens.wallet.collectibles.styles :as styles])) -(defmulti load-collectible-fx (fn [symbol _] symbol)) - -(defmethod load-collectible-fx :default [_ _] nil) - -(defmulti load-collectibles-fx (fn [_ symbol _ _] symbol)) - -(defmethod load-collectibles-fx :default [web3 symbol i address] - {:load-collectibles [web3 symbol i address]}) - (defmulti render-collectible (fn [symbol _] symbol)) (defmethod render-collectible :default [symbol {:keys [id name]}] [react/view {:style styles/default-collectible} [react/text (str (clojure.core/name symbol) " #" (or id name))]]) -(defview display-collectible [] - (letsubs [{:keys [name symbol]} [:get-screen-params]] - (let [collectibles @(re-frame/subscribe [:collectibles symbol])] - [react/view {:style component.styles/flex} - (if (seq collectibles) - [react/view {:style component.styles/flex} - [status-bar/status-bar] - [toolbar/toolbar {} - toolbar/default-nav-back - [toolbar/content-title name]] - [list/flat-list {:data collectibles - :key-fn (comp str :id) - :render-fn #(render-collectible symbol %)}]] - [react/view {:style styles/loading-indicator} - [react/activity-indicator {:animating true :size :large :color colors/blue}]])]))) +(defview collectibles-list [] + (letsubs [{:keys [name symbol]} [:get-screen-params] + collectibles [:screen-collectibles]] + [react/view {:style component.styles/flex} + [react/view {:style component.styles/flex} + [status-bar/status-bar] + [toolbar/toolbar {} + toolbar/default-nav-back + [toolbar/content-title name]] + (if (seq collectibles) + [list/flat-list {:data collectibles + :key-fn (comp str :id) + :render-fn #(render-collectible symbol %)}] + [react/view {:style styles/loading-indicator} + [react/activity-indicator {:animating true :size :large :color colors/blue}]])]])) \ No newline at end of file diff --git a/src/status_im/ui/screens/wallet/events.cljs b/src/status_im/ui/screens/wallet/events.cljs index 536c2979e4..8814b45a3b 100644 --- a/src/status_im/ui/screens/wallet/events.cljs +++ b/src/status_im/ui/screens/wallet/events.cljs @@ -14,8 +14,6 @@ status-im.ui.screens.wallet.request.events [status-im.constants :as constants] [status-im.ui.screens.navigation :as navigation] - [status-im.ui.screens.wallet.collectibles.views :as collectibles] - [status-im.utils.ethereum.erc721 :as erc721] [status-im.utils.money :as money])) (defn get-balance [{:keys [web3 account-id on-success on-error]}] @@ -281,23 +279,4 @@ (fn [{:keys [db]}] {:db (-> db (assoc-in [:wallet :send-transaction] {}) - (navigation/navigate-back))})) - -(handlers/register-handler-fx - :wallet/show-collectibles - (fn [{:keys [db]} [_ i address {:keys [symbol] :as m}]] - (assoc (collectibles/load-collectibles-fx (:web3 db) symbol i address) - :dispatch [:navigate-to :display-collectible m]))) - -(re-frame/reg-fx - :load-collectibles - (fn [[web3 symbol i address]] - (dotimes [n i] - (erc721/token-of-owner-by-index web3 (:address (tokens/symbol->token :mainnet symbol)) address n - #(re-frame/dispatch [:load-collectible symbol (.toNumber %2)]))))) - -(handlers/register-handler-fx - :load-collectible - (fn [{db :db} [_ symbol id]] - (assoc (collectibles/load-collectible-fx symbol id) - :db db))) + (navigation/navigate-back))})) \ No newline at end of file diff --git a/src/status_im/ui/screens/wallet/views.cljs b/src/status_im/ui/screens/wallet/views.cljs index 74bdfc1ebf..25c5d9615d 100644 --- a/src/status_im/ui/screens/wallet/views.cljs +++ b/src/status_im/ui/screens/wallet/views.cljs @@ -11,9 +11,9 @@ [status-im.ui.screens.wallet.styles :as styles] [status-im.ui.screens.wallet.utils :as wallet.utils] [status-im.utils.money :as money] - status-im.ui.screens.wallet.collectibles.cryptokitties - status-im.ui.screens.wallet.collectibles.cryptostrikers - status-im.ui.screens.wallet.collectibles.etheremon)) + status-im.ui.screens.wallet.collectibles.etheremon.views + status-im.ui.screens.wallet.collectibles.cryptostrikers.views + status-im.ui.screens.wallet.collectibles.cryptokitties.views)) (defn toolbar-view [] [toolbar/toolbar {:style styles/toolbar :flat? true} @@ -88,11 +88,12 @@ [list/item-icon {:icon :icons/forward :icon-opts {:color :gray}}]) -(defn- render-collectible [address-hex {:keys [symbol name icon amount] :as m}] - (let [i (money/to-fixed amount) - details? (pos? i)] - [react/touchable-highlight (when details? - {:on-press #(re-frame/dispatch [:wallet/show-collectibles i address-hex m])}) +(defn- render-collectible [address-hex {:keys [symbol name icon amount] :as collectible}] + (let [items-number (money/to-fixed amount) + details? (pos? items-number)] + [react/touchable-highlight + (when details? + {:on-press #(re-frame/dispatch [:show-collectibles-list address-hex collectible])}) [react/view {:style styles/asset-item-container} [list/item [list/item-image icon] @@ -100,8 +101,9 @@ [react/text {:style styles/asset-item-value :number-of-lines 1 :ellipsize-mode :tail - :accessibility-label (str (-> symbol clojure.core/name clojure.string/lower-case) "-collectible-value-text")} - (or i 0)] + :accessibility-label (str (-> symbol clojure.core/name clojure.string/lower-case) + "-collectible-value-text")} + (or items-number "...")] [react/text {:style styles/asset-item-currency :number-of-lines 1} name]] diff --git a/src/status_im/utils/ethereum/erc721.cljs b/src/status_im/utils/ethereum/erc721.cljs index 40ce68f174..bd8a1ef1e8 100644 --- a/src/status_im/utils/ethereum/erc721.cljs +++ b/src/status_im/utils/ethereum/erc721.cljs @@ -6,5 +6,9 @@ (defn token-of-owner-by-index [web3 contract address index cb] (ethereum/call web3 - (ethereum/call-params contract "tokenOfOwnerByIndex(address,uint256)" (ethereum/normalized-address address) index) + (ethereum/call-params + contract + "tokenOfOwnerByIndex(address,uint256)" + (ethereum/normalized-address address) + (ethereum/int->hex index)) #(cb %1 (ethereum/hex->bignumber %2)))) diff --git a/src/status_im/utils/ethereum/tokens.cljs b/src/status_im/utils/ethereum/tokens.cljs index 55d0e2246b..adb75ca53a 100644 --- a/src/status_im/utils/ethereum/tokens.cljs +++ b/src/status_im/utils/ethereum/tokens.cljs @@ -1,6 +1,7 @@ (ns status-im.utils.ethereum.tokens (:require-macros [status-im.utils.ethereum.macros :refer [resolve-icons]]) - (:require [clojure.string :as string])) + (:require [clojure.string :as string] + [status-im.utils.config :as config])) (defn- asset-border [color] {:border-color color :border-width 1 :border-radius 32}) @@ -386,21 +387,18 @@ :address "0x9B11EFcAAA1890f6eE52C6bB7CF8153aC5d74139" :decimals 8 :hidden? true} - {:symbol :CK - :nft? true - :name "CryptoKitties" - :address "0x06012c8cf97bead5deae237070f9587f8e7a266d" - :hidden? true} - {:symbol :EMONA - :nft? true - :name "Etheremon" - :address "0xB2c0782ae4A299f7358758B2D15dA9bF29E1DD99" - :hidden? true} - {:symbol :STRK - :nft? true - :name "CryptoStrikers" - :address "0xdcaad9fd9a74144d226dbf94ce6162ca9f09ed7e" - :hidden? true}]) + {:symbol :CK + :nft? true + :name "CryptoKitties" + :address "0x06012c8cf97bead5deae237070f9587f8e7a266d"} + {:symbol :EMONA + :nft? true + :name "Etheremon" + :address "0xB2c0782ae4A299f7358758B2D15dA9bF29E1DD99"} + {:symbol :STRK + :nft? true + :name "CryptoStrikers" + :address "0xdcaad9fd9a74144d226dbf94ce6162ca9f09ed7e"}]) :testnet (resolve-icons :testnet [{:name "Status Test Token" diff --git a/src/status_im/utils/http.cljs b/src/status_im/utils/http.cljs index c5437894c4..1482a92677 100644 --- a/src/status_im/utils/http.cljs +++ b/src/status_im/utils/http.cljs @@ -70,3 +70,6 @@ :keywordize-keys true) (catch :default _ (log/debug (str "Failed to parse " o)))))) + +(defn url-sanitized? [uri] + (not (nil? (re-find #"^(https:)([/|.|\w|\s|-])*\.(?:jpg|svg|png)$" uri)))) \ No newline at end of file diff --git a/src/status_im/utils/money.cljs b/src/status_im/utils/money.cljs index 43b118dc09..c625cd100e 100644 --- a/src/status_im/utils/money.cljs +++ b/src/status_im/utils/money.cljs @@ -76,6 +76,10 @@ (when bn (.toFixed bn))) +(defn to-number [bn] + (when bn + (.toNumber bn))) + (defn wei->str [unit n] (str (to-fixed (wei-> unit n)) " " (string/upper-case (name unit)))) diff --git a/test/cljs/status_im/test/runner.cljs b/test/cljs/status_im/test/runner.cljs index a197cf4bb7..afd3f66969 100644 --- a/test/cljs/status_im/test/runner.cljs +++ b/test/cljs/status_im/test/runner.cljs @@ -43,6 +43,7 @@ [status-im.test.utils.prices] [status-im.test.utils.keychain.core] [status-im.test.utils.universal-links.core] + [status-im.test.utils.http] [status-im.test.ui.screens.events] [status-im.test.ui.screens.accounts.login.events])) @@ -98,5 +99,6 @@ 'status-im.test.utils.prices 'status-im.test.utils.keychain.core 'status-im.test.utils.universal-links.core + 'status-im.test.utils.http 'status-im.test.ui.screens.events 'status-im.test.ui.screens.accounts.login.events) diff --git a/test/cljs/status_im/test/utils/http.cljs b/test/cljs/status_im/test/utils/http.cljs new file mode 100644 index 0000000000..003f747736 --- /dev/null +++ b/test/cljs/status_im/test/utils/http.cljs @@ -0,0 +1,40 @@ +(ns status-im.test.utils.http + (:require [cljs.test :refer-macros [deftest is testing]] + [status-im.utils.http :as http])) + +(deftest url-sanitize-check + (testing "https://storage.googleapis.com/ck-kitty-image/0x06012c8cf97bead5deae237070f9587f8e7a266d/818934.svg" + (testing "it returns true" + (is (http/url-sanitized? "https://storage.googleapis.com/ck-kitty-image/0x06012c8cf97bead5deae237070f9587f8e7a266d/818934.svg")))) + + (testing "https://www.cryptostrikers.com/assets/images/cards/017.svg" + (testing "it returns true" + (is (http/url-sanitized? "https://www.cryptostrikers.com/assets/images/cards/017.svg")))) + + (testing "https://www.etheremon.com/assets/images/mons_origin/025.png" + (testing "it returns true" + (is (http/url-sanitized? "https://www.etheremon.com/assets/images/mons_origin/025.png")))) + + (testing "http://www.etheremon.com/assets/images/mons_origin/025.png" + (testing "it returns false" + (is (not (http/url-sanitized? "http://www.etheremon.com/assets/images/mons_origin/025.png"))))) + + (testing "xxx:x \\\\x0Aonerror=javascript:alert(1)" + (testing "it returns false" + (is (not (http/url-sanitized? "xxx:x \\\\x0Aonerror=javascript:alert(1)"))))) + + (testing "https://www.etheremon.com/assets/images/mons_origin/025.png'<script>alert('123');</script>" + (testing "it returns false" + (is (not (http/url-sanitized? "https://www.etheremon.com/assets/images/mons_origin/025.png'<script>alert('123');</script>"))))) + + (testing "https://www.etheremon.com/assets/images/mons'<script>alert('123');</script>origin/025.png" + (testing "it returns false" + (is (not (http/url-sanitized? "https://www.etheremon.com/assets/images/mons'<script>alert('123');</script>origin/025.png"))))) + + (testing "https://www.etheremon.com/assets/images/mons_origin/025.png'>" + (testing "it returns false" + (is (not (http/url-sanitized? "https://www.etheremon.com/assets/images/mons_origin/025.png'>"))))) + + (testing "https://www.etheremon.com/assets/images/mons'>origin/025.png" + (testing "it returns false" + (is (not (http/url-sanitized? "https://www.etheremon.com/assets/images/mons'>origin/025.png")))))) \ No newline at end of file