diff --git a/src/status_im/translations/en.cljs b/src/status_im/translations/en.cljs index e6ba9e8236..c57e3c22ac 100644 --- a/src/status_im/translations/en.cljs +++ b/src/status_im/translations/en.cljs @@ -603,7 +603,11 @@ :reset-default "Reset to default" :view-cryptokitties "View in CryptoKitties" - :cryptokitty-name "CryptoKitty #" + :cryptokitty-name "CryptoKitty #{{id}}" + + :view-etheremon "View in Etheremon" + + :view-cryptostrikers "View in CryptoStrikers" ;; network settings :new-network "New network" diff --git a/src/status_im/ui/screens/wallet/collectibles/cryptokitties.cljs b/src/status_im/ui/screens/wallet/collectibles/cryptokitties.cljs index bb0025dd7f..876635de25 100644 --- a/src/status_im/ui/screens/wallet/collectibles/cryptokitties.cljs +++ b/src/status_im/ui/screens/wallet/collectibles/cryptokitties.cljs @@ -1,31 +1,15 @@ (ns status-im.ui.screens.wallet.collectibles.cryptokitties - (:require-macros [status-im.utils.views :refer [defview letsubs]]) (: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.utils.handlers :as handlers]) - (:refer-clojure :exclude [symbol])) + [status-im.ui.screens.wallet.collectibles.styles :as styles] + [status-im.utils.handlers :as handlers] + [status-im.utils.http :as http])) -(def symbol :CK) - -(handlers/register-handler-fx - :load-kitty-success - [re-frame/trim-v] - (fn [{db :db} [[id collectible]]] - {:db (update-in db [:collectibles symbol] assoc id collectible)})) - -(handlers/register-handler-fx - :load-kitty-failure - [re-frame/trim-v] - (fn [{db :db} [_]] - {:db db})) - -(defn parse-payload [o] - (js->clj (js/JSON.parse o) - :keywordize-keys true)) +(def ck :CK) (handlers/register-handler-fx :load-kitties @@ -34,13 +18,13 @@ :http-get-n (mapv (fn [id] {:url (str "https://api.cryptokitties.co/kitties/" id) :success-event-creator (fn [o] - [:load-kitty-success [id (parse-payload o)]]) + [:load-collectible-success ck {id (http/parse-payload o)}]) :failure-event-creator (fn [o] - [:load-kitty-failure [id (parse-payload o)]])}) + [:load-collectible-failure ck {id (http/parse-payload o)}])}) ids)})) (defn kitties-url [address] - (str "https://api.cryptokitties.co/kitties?offset=0&limit=100&owner_wallet_address=" address "&parents=false")) + (str "https://api.cryptokitties.co/kitties?offset=0&limit=20&owner_wallet_address=" address "&parents=false")) (handlers/register-handler-fx :load-kitties-success @@ -48,38 +32,25 @@ {:db db :dispatch [:load-kitties ids]})) -(defmethod collectibles/load-collectibles-fx symbol [_ address] +;; 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 (parse-payload o)))]) + [:load-kitties-success (map :id (:kitties (http/parse-payload o)))]) :failure-event-creator (fn [o] - [:load-collectibles-failure (parse-payload o)]) + [:load-collectibles-failure (http/parse-payload o)]) :timeout-ms 10000}}) -(defn- kitty-name [{:keys [id name]}] - (or name (str (i18n/label :t/cryptokitty-name) id))) +(def base-url "https://www.cryptokitties.co/kitty/") -(def view-style - {:padding-vertical 10}) - -(def text-style - {:flex 1 - :flex-direction :row - :align-items :center - :padding-horizontal 16}) - -(def name-style - {:color colors/black - :margin-bottom 10}) - -(defmethod collectibles/render-collectible symbol [_ {:keys [id bio image_url] :as m}] - [react/view {:style view-style} - [react/view {:style text-style} +(defmethod collectibles/render-collectible ck [_ {:keys [id name bio image_url]}] + [react/view {:style styles/details} + [react/view {:style styles/details-text} ;; TODO reenable image once SVG is supported #_[react/image {:style {:width 80 :height 80 :margin 10 :background-color "red"} :source {:uri image_url}}] [react/view {} - [react/text {:style name-style} - (kitty-name m)] + [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]]] @@ -87,4 +58,4 @@ :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)}])}]]) + :on-press #(re-frame/dispatch [:open-browser {:url (str base-url id)}])}]]) diff --git a/src/status_im/ui/screens/wallet/collectibles/cryptostrikers.cljs b/src/status_im/ui/screens/wallet/collectibles/cryptostrikers.cljs new file mode 100644 index 0000000000..9b23a0d380 --- /dev/null +++ b/src/status_im/ui/screens/wallet/collectibles/cryptostrikers.cljs @@ -0,0 +1,34 @@ +(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])) + +(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} + [react/image {:style styles/details-image + :source {:uri image}}] + [react/view {: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 new file mode 100644 index 0000000000..2c2617420f --- /dev/null +++ b/src/status_im/ui/screens/wallet/collectibles/etheremon.cljs @@ -0,0 +1,34 @@ +(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])) + +(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/image {:style styles/details-image + :source {:uri image}}] + [react/view {: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/events.cljs b/src/status_im/ui/screens/wallet/collectibles/events.cljs index dc2eb25cbd..c957f8e47a 100644 --- a/src/status_im/ui/screens/wallet/collectibles/events.cljs +++ b/src/status_im/ui/screens/wallet/collectibles/events.cljs @@ -2,8 +2,20 @@ (:require [re-frame.core :as re-frame] [status-im.utils.handlers :as handlers])) +(handlers/register-handler-fx + :load-collectible-success + [re-frame/trim-v] + (fn [{db :db} [symbol collectibles]] + {:db (update-in db [:collectibles symbol] merge collectibles)})) + (handlers/register-handler-fx :load-collectibles-failure [re-frame/trim-v] (fn [{db :db} [{:keys [message]}]] {:db (assoc db :collectibles-failure message)})) + +(handlers/register-handler-fx + :load-collectible-failure + [re-frame/trim-v] + (fn [{db :db} [_]] + {:db db})) diff --git a/src/status_im/ui/screens/wallet/collectibles/styles.cljs b/src/status_im/ui/screens/wallet/collectibles/styles.cljs index 17508ecc4f..c34d49b6c8 100644 --- a/src/status_im/ui/screens/wallet/collectibles/styles.cljs +++ b/src/status_im/ui/screens/wallet/collectibles/styles.cljs @@ -1,4 +1,5 @@ -(ns status-im.ui.screens.wallet.collectibles.styles) +(ns status-im.ui.screens.wallet.collectibles.styles + (:require [status-im.ui.components.colors :as colors])) (def default-collectible {:padding-left 10 @@ -8,3 +9,22 @@ {:flex 1 :align-items :center :justify-content :center}) + +(def details + {:padding-vertical 10}) + +(def details-text + {:flex 1 + :flex-direction :row + :align-items :center + :padding-horizontal 16}) + +(def details-name + {:color colors/black + :text-align-vertical :center + :margin-bottom 10}) + +(def details-image + {:width 80 + :height 80 + :margin 10}) diff --git a/src/status_im/ui/screens/wallet/collectibles/views.cljs b/src/status_im/ui/screens/wallet/collectibles/views.cljs index a23f314b52..0c60d6b1c1 100644 --- a/src/status_im/ui/screens/wallet/collectibles/views.cljs +++ b/src/status_im/ui/screens/wallet/collectibles/views.cljs @@ -9,9 +9,14 @@ [status-im.ui.components.toolbar.view :as toolbar] [status-im.ui.screens.wallet.collectibles.styles :as styles])) -(defmulti load-collectibles-fx (fn [symbol _] symbol)) +(defmulti load-collectible-fx (fn [symbol _] symbol)) -(defmethod load-collectibles-fx :default [_ _] nil) +(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)) diff --git a/src/status_im/ui/screens/wallet/events.cljs b/src/status_im/ui/screens/wallet/events.cljs index c6446f6b9b..7222b9537c 100644 --- a/src/status_im/ui/screens/wallet/events.cljs +++ b/src/status_im/ui/screens/wallet/events.cljs @@ -1,5 +1,6 @@ (ns status-im.ui.screens.wallet.events - (:require [re-frame.core :as re-frame :refer [dispatch reg-fx]] + (:require [clojure.set :as set] + [re-frame.core :as re-frame :refer [dispatch reg-fx]] [status-im.i18n :as i18n] [status-im.ui.screens.wallet.navigation] [status-im.utils.ethereum.core :as ethereum] @@ -11,11 +12,11 @@ [status-im.models.wallet :as models.wallet] [taoensso.timbre :as log] status-im.ui.screens.wallet.request.events - [status-im.utils.money :as money] [status-im.constants :as constants] [status-im.ui.screens.navigation :as navigation] [status-im.ui.screens.wallet.collectibles.views :as collectibles] - [clojure.set :as set])) + [status-im.utils.ethereum.erc721 :as erc721] + [status-im.utils.money :as money])) (defn get-balance [{:keys [web3 account-id on-success on-error]}] (if (and web3 account-id) @@ -282,7 +283,19 @@ (handlers/register-handler-fx :wallet/show-collectibles - (fn [_ [_ address {:keys [symbol] :as m}]] - (if-let [fx (collectibles/load-collectibles-fx symbol address)] - (assoc fx :dispatch [:navigate-to :display-collectible m]) - {:show-error (str "Missing implementation for " (name symbol))}))) + (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))) diff --git a/src/status_im/ui/screens/wallet/views.cljs b/src/status_im/ui/screens/wallet/views.cljs index fc80e14df1..74bdfc1ebf 100644 --- a/src/status_im/ui/screens/wallet/views.cljs +++ b/src/status_im/ui/screens/wallet/views.cljs @@ -11,7 +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.cryptokitties + status-im.ui.screens.wallet.collectibles.cryptostrikers + status-im.ui.screens.wallet.collectibles.etheremon)) (defn toolbar-view [] [toolbar/toolbar {:style styles/toolbar :flat? true} @@ -86,11 +88,11 @@ [list/item-icon {:icon :icons/forward :icon-opts {:color :gray}}]) -(defn- render-collectible [address-hex {:keys [symbol icon amount] :as m}] +(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 address-hex m])}) + {:on-press #(re-frame/dispatch [:wallet/show-collectibles i address-hex m])}) [react/view {:style styles/asset-item-container} [list/item [list/item-image icon] @@ -98,12 +100,11 @@ [react/text {:style styles/asset-item-value :number-of-lines 1 :ellipsize-mode :tail - :accessibility-label (str (-> symbol name clojure.string/lower-case) "-collectible-value-text")} + :accessibility-label (str (-> symbol clojure.core/name clojure.string/lower-case) "-collectible-value-text")} (or i 0)] [react/text {:style styles/asset-item-currency - :uppercase? true :number-of-lines 1} - (name symbol)]] + name]] (when details? item-icon-forward)]]])) diff --git a/src/status_im/utils/ethereum/core.cljs b/src/status_im/utils/ethereum/core.cljs index 68b2897595..e39fc0da3b 100644 --- a/src/status_im/utils/ethereum/core.cljs +++ b/src/status_im/utils/ethereum/core.cljs @@ -95,7 +95,7 @@ (defn format-param [param] (if (number? param) - (zero-pad-64 (hex->int param)) + (zero-pad-64 (str (hex->int param))) (zero-pad-64 (subs param 2)))) (defn format-call-params [method-id & params] diff --git a/src/status_im/utils/ethereum/erc721.cljs b/src/status_im/utils/ethereum/erc721.cljs index 1479aca08a..40ce68f174 100644 --- a/src/status_im/utils/ethereum/erc721.cljs +++ b/src/status_im/utils/ethereum/erc721.cljs @@ -2,8 +2,7 @@ " Helper functions to interact with [ERC721](https://eips.ethereum.org/EIPS/eip-721) smart contract " - (:require [status-im.utils.ethereum.core :as ethereum] - [status-im.utils.ethereum.erc20 :as erc20])) + (:require [status-im.utils.ethereum.core :as ethereum])) (defn token-of-owner-by-index [web3 contract address index cb] (ethereum/call web3 diff --git a/src/status_im/utils/ethereum/tokens.cljs b/src/status_im/utils/ethereum/tokens.cljs index eaeab6d117..2d6f976523 100644 --- a/src/status_im/utils/ethereum/tokens.cljs +++ b/src/status_im/utils/ethereum/tokens.cljs @@ -392,7 +392,7 @@ :address "0x06012c8cf97bead5deae237070f9587f8e7a266d"} {:symbol :EMONA :nft? true - :name "EtheremonAsset" + :name "Etheremon" :address "0xB2c0782ae4A299f7358758B2D15dA9bF29E1DD99"} {:symbol :STRK :nft? true diff --git a/src/status_im/utils/http.cljs b/src/status_im/utils/http.cljs index a5a5b637b6..c5437894c4 100644 --- a/src/status_im/utils/http.cljs +++ b/src/status_im/utils/http.cljs @@ -1,6 +1,7 @@ (ns status-im.utils.http (:require [status-im.utils.utils :as utils] - [status-im.react-native.js-dependencies :as rn-dependencies]) + [status-im.react-native.js-dependencies :as rn-dependencies] + [taoensso.timbre :as log]) (:refer-clojure :exclude [get])) ;; Default HTTP request timeout ms @@ -61,3 +62,11 @@ (defn normalize-url [url] (str (when (and url (not (re-find #"^[a-zA-Z-_]+:/" url))) "http://") url)) + +(defn parse-payload [o] + (when o + (try + (js->clj (js/JSON.parse o) + :keywordize-keys true) + (catch :default _ + (log/debug (str "Failed to parse " o))))))