[#4663] Added collectibles and CK support

Signed-off-by: Pedro Pombeiro <pombeirp@users.noreply.github.com>
This commit is contained in:
Julien Eluard 2018-01-02 12:02:55 +01:00 committed by Pedro Pombeiro
parent 49bc773037
commit e449e12a2a
No known key found for this signature in database
GPG Key ID: A65DEB11E4BBC647
24 changed files with 264 additions and 27 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

View File

@ -427,6 +427,7 @@
:wallet-exchange "Exchange"
:wallet-asset "Asset"
:wallet-assets "Assets"
:wallet-collectibles "Collectibles"
:wallet-add-asset "Add asset"
:wallet-total-value "Total value"
:wallet-settings "Wallet settings"
@ -589,6 +590,9 @@
:scan-qr-code "Scan a QR code with a wallet address"
:reset-default "Reset to default"
:view-cryptokitties "View in CryptoKitties"
:cryptokitty-name "CryptoKitty #"
;; network settings
:new-network "New network"
:add-network "Add network"

View File

@ -110,6 +110,8 @@
(spec/def :navigation.screen-params/usage-data vector?)
(spec/def :navigation.screen-params/display-collectible map?)
(spec/def :navigation/screen-params (spec/nilable (allowed-keys :opt-un [:navigation.screen-params/network-details
:navigation.screen-params/browser
:navigation.screen-params/profile-qr-viewer
@ -117,7 +119,8 @@
:navigation.screen-params/group-contacts
:navigation.screen-params/edit-contact-group
:navigation.screen-params/dapp-description
:navigation.screen-params/usage-data])))
:navigation.screen-params/usage-data
:navigation.screen-params/display-collectible])))
(spec/def :desktop/desktop (spec/nilable any?))
@ -129,6 +132,9 @@
(spec/def :inbox/fetching? (spec/nilable boolean?))
(spec/def :inbox/current-id (spec/nilable string?))
(spec/def ::collectible (spec/nilable map?))
(spec/def ::collectibles (spec/nilable map?))
;;;;NODE
(spec/def :node/after-start (spec/nilable vector?))
@ -249,4 +255,6 @@
:prices/prices
:prices/prices-loading?
:notifications/notifications
::device-UUID]))
::device-UUID
::collectible
::collectibles]))

View File

@ -18,6 +18,7 @@
status-im.ui.screens.profile.events
status-im.ui.screens.qr-scanner.events
status-im.ui.screens.wallet.events
status-im.ui.screens.wallet.collectibles.events
status-im.ui.screens.wallet.send.events
status-im.ui.screens.wallet.settings.events
status-im.ui.screens.wallet.transactions.events

View File

@ -8,6 +8,7 @@
status-im.ui.screens.contacts.subs
status-im.ui.screens.group.subs
status-im.ui.screens.wallet.subs
status-im.ui.screens.wallet.collectibles.subs
status-im.ui.screens.wallet.request.subs
status-im.ui.screens.wallet.send.subs
status-im.ui.screens.wallet.transactions.subs

View File

@ -24,6 +24,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.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]]
@ -51,6 +52,7 @@
(defn get-main-component [view-id]
(case view-id
:display-collectible collectibles/display-collectible
:intro intro
:create-account create-account
:usage-data usage-data

View File

@ -0,0 +1,90 @@
(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]))
(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))
(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-kitty-success [id (parse-payload o)]])
:failure-event-creator (fn [o]
[:load-kitty-failure [id (parse-payload o)]])})
ids)}))
(defn kitties-url [address]
(str "https://api.cryptokitties.co/kitties?offset=0&limit=100&owner_wallet_address=" address "&parents=false"))
(handlers/register-handler-fx
:load-kitties-success
(fn [{db :db} [_ ids]]
{:db db
:dispatch [:load-kitties ids]}))
(defmethod collectibles/load-collectibles-fx symbol [_ address]
{:http-get {:url (kitties-url address)
:success-event-creator (fn [o]
[:load-kitties-success (map :id (:kitties (parse-payload o)))])
:failure-event-creator (fn [o]
[:load-collectibles-failure (parse-payload o)])
:timeout-ms 10000}})
(defn- kitty-name [{:keys [id name]}]
(or name (str (i18n/label :t/cryptokitty-name) id)))
(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}
;; 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 {: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)}])}]])

View File

@ -0,0 +1,9 @@
(ns status-im.ui.screens.wallet.collectibles.events
(:require [re-frame.core :as re-frame]
[status-im.utils.handlers :as handlers]))
(handlers/register-handler-fx
:load-collectibles-failure
[re-frame/trim-v]
(fn [{db :db} [{:keys [message]}]]
{:db (assoc db :collectibles-failure message)}))

View File

@ -0,0 +1,10 @@
(ns status-im.ui.screens.wallet.collectibles.styles)
(def default-collectible
{:padding-left 10
:padding-vertical 20})
(def loading-indicator
{:flex 1
:align-items :center
:justify-content :center})

View File

@ -0,0 +1,6 @@
(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]))))

View File

@ -0,0 +1,36 @@
(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]
[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]
[status-im.ui.components.styles :as component.styles]
[status-im.ui.components.toolbar.view :as toolbar]
[status-im.ui.screens.wallet.collectibles.styles :as styles]))
(defmulti load-collectibles-fx (fn [symbol _] symbol))
(defmethod load-collectibles-fx :default [_ _] nil)
(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}]])])))

View File

@ -12,7 +12,9 @@
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.navigation :as navigation]
[status-im.ui.screens.wallet.collectibles.views :as collectibles]
[clojure.set :as set]))
(defn get-balance [{:keys [web3 account-id on-success on-error]}]
(if (and web3 account-id)
@ -99,14 +101,18 @@
(fn [{:keys [web3 obj success-event]}]
(ethereum/estimate-gas-web3 web3 (clj->js obj) #(re-frame/dispatch [success-event %2]))))
(defn tokens-symbols [v chain]
(set/difference (set v) (set (map :symbol (tokens/nfts-for chain)))))
;; Handlers
(handlers/register-handler-fx
:update-wallet
(fn [{{:keys [web3 account/account network network-status] {:keys [address settings]} :account/account :as db} :db} _]
(fn [{{:keys [web3 network network-status] {:keys [address settings]} :account/account :as db} :db} _]
(let [network (get-in db [:account/account :networks network])
chain (ethereum/network->chain-keyword network)
mainnet? (= :mainnet chain)
symbols (get-in settings [:wallet :visible-tokens chain])
assets (get-in settings [:wallet :visible-tokens chain])
tokens (tokens-symbols (get-in settings [:wallet :visible-tokens chain]) chain)
currency-id (or (get-in settings [:wallet :currency]) :usd)
currency (get constants/currencies currency-id)]
(when (not= network-status :offline)
@ -116,11 +122,11 @@
:error-event :update-balance-fail}
:get-tokens-balance {:web3 web3
:account-id address
:symbols symbols
:symbols assets
:chain chain
:success-event :update-token-balance-success
:error-event :update-token-balance-fail}
:get-prices {:from (if mainnet? (conj symbols "ETH") ["ETH"])
:get-prices {:from (if mainnet? (conj tokens "ETH") ["ETH"])
:to [(:code currency)]
:success-event :update-prices-success
:error-event :update-prices-fail}
@ -267,4 +273,11 @@
(fn [{:keys [db]}]
{:db (-> db
(assoc-in [:wallet :send-transaction] {})
(navigation/navigate-back))}))
(navigation/navigate-back))}))
(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))})))

View File

@ -1,7 +1,6 @@
(ns status-im.ui.screens.wallet.styles
(:require-macros [status-im.utils.styles :refer [defstyle]])
(:require [status-im.ui.components.colors :as colors]
[status-im.ui.components.react :as react]
[status-im.ui.components.styles :as styles]))
;; wallet
@ -153,11 +152,6 @@
:padding-top 16
:background-color colors/white})
(def asset-section-title
{:font-size 14
:margin-left 16
:color colors/gray})
(def asset-item-container
{:flex 1
:flex-direction :row

View File

@ -9,7 +9,9 @@
[status-im.ui.components.icons.vector-icons :as vector-icons]
[status-im.ui.screens.wallet.onboarding.views :as onboarding.views]
[status-im.ui.screens.wallet.styles :as styles]
[status-im.ui.screens.wallet.utils :as wallet.utils]))
[status-im.ui.screens.wallet.utils :as wallet.utils]
[status-im.utils.money :as money]
status-im.ui.screens.wallet.collectibles.cryptokitties))
(defn toolbar-view []
[toolbar/toolbar {:style styles/toolbar :flat? true}
@ -80,21 +82,56 @@
:number-of-lines 1}
(if @asset-value @asset-value "...")]]])))
(defn- asset-section [assets currency]
[react/view styles/asset-section
[react/text {:style styles/asset-section-title} (i18n/label :t/wallet-assets)]
[list/flat-list
{:default-separator? true
:scroll-enabled false
:key-fn (comp str :symbol)
:data assets
:render-fn (render-asset currency)}]])
(def item-icon-forward
[list/item-icon {:icon :icons/forward
:icon-opts {:color :gray}}])
(defn- render-collectible [address-hex {:keys [symbol 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])})
[react/view {:style styles/asset-item-container}
[list/item
[list/item-image icon]
[react/view {:style styles/asset-item-value-container}
[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")}
(or i 0)]
[react/text {:style styles/asset-item-currency
:uppercase? true
:number-of-lines 1}
(name symbol)]]
(when details?
item-icon-forward)]]]))
(defn group-assets [v]
(group-by #(if (:nft? %) :nfts :tokens) v))
(defn- asset-section [assets currency address-hex]
(let [{:keys [tokens nfts]} (group-assets assets)]
[react/view styles/asset-section
[list/section-list
{:default-separator? true
:scroll-enabled false
:key-fn (comp str :symbol)
:sections [{:title (i18n/label :t/wallet-assets)
:key :assets
:data tokens
:render-fn (render-asset currency)}
{:title (i18n/label :t/wallet-collectibles)
:key :collectibles
:data nfts
:render-fn #(render-collectible address-hex %)}]}]]))
(views/defview wallet-root []
(views/letsubs [assets [:wallet/visible-assets-with-amount]
currency [:wallet/currency]
portfolio-value [:portfolio-value]
{:keys [seed-backed-up?]} [:get-current-account]]
{:keys [seed-backed-up?]} [:get-current-account]
address-hex [:get-current-account-hex]]
[react/view styles/main-section
[toolbar-view]
[react/scroll-view {:refresh-control
@ -110,7 +147,7 @@
[backup-seed-phrase])
[list/action-list actions
{:container-style styles/action-section}]
[asset-section assets currency]
[asset-section assets currency address-hex]
;; Hack to allow different colors for bottom scroll view (iOS only)
[react/view {:style styles/scroll-bottom}]]]))

View File

@ -0,0 +1,11 @@
(ns status-im.utils.ethereum.erc721
"
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]))
(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)
#(cb %1 (ethereum/hex->bignumber %2))))

View File

@ -385,7 +385,19 @@
:name "Attention Token of Media"
:address "0x9B11EFcAAA1890f6eE52C6bB7CF8153aC5d74139"
:decimals 8
:hidden? true}])
:hidden? true}
{:symbol :CK
:nft? true
:name "CryptoKitties"
:address "0x06012c8cf97bead5deae237070f9587f8e7a266d"}
{:symbol :EMONA
:nft? true
:name "EtheremonAsset"
:address "0xB2c0782ae4A299f7358758B2D15dA9bF29E1DD99"}
{:symbol :STRK
:nft? true
:name "CryptoStrikers"
:address "0xdcaad9fd9a74144d226dbf94ce6162ca9f09ed7e"}])
:testnet
(resolve-icons :testnet
[{:name "Status Test Token"
@ -416,6 +428,9 @@
(defn tokens-for [chain]
(get all chain))
(defn nfts-for [chain]
(filter :nft? (tokens-for chain)))
(defn sorted-tokens-for [chain]
(->> (tokens-for chain)
(filter #(not (:hidden? %)))