[#13173] move stickers business logic to status-go

Signed-off-by: andrey <motor4ik@gmail.com>
This commit is contained in:
andrey 2022-03-25 12:09:47 +01:00
parent d765dd1f60
commit 54c3030971
No known key found for this signature in database
GPG Key ID: 89B67245FD2F0272
24 changed files with 207 additions and 410 deletions

1
.env
View File

@ -34,3 +34,4 @@ COLLECTIBLES_ENABLED=1
COMMANDS_ENABLED=1
TWO_MINUTES_SYNCING=1
SWAP_ENABLED=1
STICKERS_TEST_ENABLED=1

View File

@ -34,3 +34,4 @@ COMMUNITIES_MANAGEMENT_ENABLED=1
METRICS_ENABLED=0
DELETE_MESSAGE_ENABLED=1
TWO_MINUTES_SYNCING=1
STICKERS_TEST_ENABLED=1

View File

@ -37,3 +37,4 @@ METRICS_ENABLED=0
DELETE_MESSAGE_ENABLED=1
TWO_MINUTES_SYNCING=1
ENABLE_QUO_PREVIEW=1
STICKERS_TEST_ENABLED=1

View File

@ -10,7 +10,6 @@
[status-im.native-module.core :as status]
[status-im.ui.components.list-selection :as list-selection]
[status-im.navigation :as navigation]
[status-im.utils.contenthash :as contenthash]
[status-im.utils.fx :as fx]
[status-im.utils.http :as http]
[status-im.utils.platform :as platform]
@ -182,20 +181,6 @@
:history new-history
:history-index new-index))))))
(defmulti storage-gateway :namespace)
(defmethod storage-gateway :ipns
[{:keys [hash]}]
(str "https://" hash))
(defmethod storage-gateway :ipfs
[{:keys [hash]}]
(contenthash/ipfs-url hash))
(defmethod storage-gateway :swarm
[{:keys [hash]}]
(str "https://gateway.ethswarm.org/bzz/" hash))
(fx/defn resolve-ens-multihash-success
{:events [:browser.callback/resolve-ens-multihash-success]}
[{:keys [db] :as cofx} url]

View File

@ -14,8 +14,7 @@
[status-im.utils.fx :as fx]
["emojilib" :as emojis]
[status-im.chat.models.mentions :as mentions]
[status-im.utils.utils :as utils]
[status-im.multiaccounts.update.core :as multiaccounts.update]))
[status-im.utils.utils :as utils]))
(defn text->emoji
"Replaces emojis in a specified `text`"
@ -230,12 +229,12 @@
:text (i18n/label :t/update-to-listen-audio {"locale" "en"})})))
(fx/defn send-sticker-message
[cofx {:keys [hash pack]} current-chat-id]
(when-not (string/blank? hash)
[cofx {:keys [hash packID]} current-chat-id]
(when-not (or (string/blank? hash) (string/blank? packID))
(chat.message/send-message cofx {:chat-id current-chat-id
:content-type constants/content-type-sticker
:sticker {:hash hash
:pack pack}
:pack (int packID)}
:text (i18n/label :t/update-to-see-sticker {"locale" "en"})})))
(fx/defn send-edited-message [{:keys [db] :as cofx} text {:keys [message-id]}]
@ -265,13 +264,16 @@
(fx/defn chat-send-sticker
{:events [:chat/send-sticker]}
[{{:keys [current-chat-id multiaccount]} :db :as cofx} {:keys [hash] :as sticker}]
[{{:keys [current-chat-id] :as db} :db :as cofx} {:keys [hash packID] :as sticker}]
(fx/merge
cofx
(multiaccounts.update/multiaccount-update
:stickers/recent-stickers
(conj (remove #(= hash %) (:stickers/recent-stickers multiaccount)) hash)
{})
{:db (update db
:stickers/recent-stickers
(fn [recent]
(conj (remove #(= hash (:hash %)) recent) sticker)))
::json-rpc/call [{:method "stickers_addRecent"
:params [(int packID) hash]
:on-success #()}]}
(send-sticker-message sticker current-chat-id)))
(fx/defn chat-send-audio

View File

@ -171,4 +171,8 @@
(def ^:const visibility-status-inactive 4)
(def ^:const wallet-connect-version-1 1)
(def ^:const wallet-connect-version-2 2)
(def ^:const wallet-connect-version-2 2)
(def ^:const sticker-pack-status-installed 1)
(def ^:const sticker-pack-status-pending 2)
(def ^:const sticker-pack-status-owned 3)

View File

@ -46,8 +46,6 @@
config/default-network))
(update :wallet/visible-tokens rpc->visible-tokens)
(update :pinned-mailservers rpc->pinned-mailservers)
(update :stickers/packs-installed rpc->stickers-packs)
(update :stickers/packs-pending set)
(update :link-previews-enabled-sites set)
(update :custom-bootnodes rpc->custom-bootnodes)
(update :custom-bootnodes-enabled? rpc->custom-bootnodes)

View File

@ -33,7 +33,6 @@
:dimensions/window (dimensions/window)
:registry {}
:visibility-status-updates {}
:stickers/packs-owned #{}
:stickers/packs-pending #{}
:keycard {:nfc-enabled? false
:pin {:original []

View File

@ -242,7 +242,16 @@
"mailservers_getChatRequestRanges" {}
"mailservers_deleteChatRequestRange" {}
"appmetrics_saveAppMetrics" {}
"appmetrics_getAppMetrics" {}})
"appmetrics_getAppMetrics" {}
"stickers_market" {}
"stickers_installed" {}
"stickers_install" {}
"stickers_addRecent" {}
"stickers_recent" {}
"stickers_buyPrepareTx" {}
"stickers_processPending" {}
"stickers_addPending" {}
"stickers_pending" {}})
(defn on-error-retry
[call-method {:keys [method number-of-retries delay on-error] :as arg}]

View File

@ -18,7 +18,6 @@
[status-im.popover.core :as popover]
[status-im.communities.core :as communities]
[status-im.transport.core :as transport]
[status-im.stickers.core :as stickers]
[status-im.mobile-sync-settings.core :as mobile-network]
[status-im.utils.fx :as fx]
[status-im.utils.keychain.core :as keychain]
@ -445,7 +444,6 @@
(initialize-transactions-management-enabled)
(check-network-version network-id)
(contact/initialize-contacts)
(stickers/init-stickers-packs)
(initialize-browser)
(mobile-network/on-network-status-change)
(get-group-chat-invitations)

View File

@ -1,24 +1,13 @@
(ns status-im.stickers.core
(:require [cljs.reader :as edn]
[clojure.set :as clojure.set]
[clojure.string :as string]
(:require [clojure.string :as string]
[re-frame.core :as re-frame]
[status-im.ethereum.abi-spec :as abi-spec]
[status-im.ethereum.contracts :as contracts]
[status-im.ethereum.core :as ethereum]
[status-im.ethereum.json-rpc :as json-rpc]
[status-im.navigation :as navigation]
[status-im.multiaccounts.update.core :as multiaccounts.update]
[status-im.signing.core :as signing]
[status-im.utils.contenthash :as contenthash]
[status-im.utils.fx :as fx]
[status-im.utils.utils :as utils]))
(defn pack-data-callback
[id]
(fn [[_ _ _ _ price contenthash]]
(when-let [url (contenthash/url contenthash)]
(re-frame/dispatch [:stickers/load-pack url id price]))))
[status-im.utils.utils :as utils]
[status-im.utils.config :as config]
[status-im.constants :as constants]))
(re-frame/reg-fx
:stickers/set-pending-timeout-fx
@ -26,193 +15,109 @@
(utils/set-timeout #(re-frame/dispatch [:stickers/pending-timeout])
10000)))
(defn eth-call-pack-data
[contract id]
(json-rpc/eth-call
{:contract contract
;; Returns vector of pack data parameters by pack id:
;; [category owner mintable timestamp price contenthash]
:method "getPackData(uint256)"
:params [id]
:outputs ["bytes4[]" "address" "bool" "uint256" "uint256" "bytes"]
:on-success (pack-data-callback id)}))
(re-frame/reg-fx
:stickers/pack-data-fx
(fn [[contract id]]
(eth-call-pack-data contract id)))
(re-frame/reg-fx
:stickers/load-packs-fx
(fn [[contract]]
(json-rpc/eth-call
{:contract contract
;; Returns number of packs registered in the contract
:method "packCount()"
:outputs ["uint256"]
:on-success
(fn [[count]]
(dotimes [id count]
(eth-call-pack-data contract id)))})))
(re-frame/reg-fx
:stickers/owned-packs-fx
(fn [[contract address]]
(json-rpc/eth-call
{:contract contract
;; Returns vector of owned tokens ids in the contract by address
:method "balanceOf(address)"
:params [address]
:outputs ["uint256"]
:number-of-retries 3
:on-success
(fn [[count]]
(dotimes [id count]
(json-rpc/eth-call
{:contract contract
;; Returns pack id in the contract by token id
:method "tokenOfOwnerByIndex(address,uint256)"
:params [address id]
:outputs ["uint256"]
:number-of-retries 3
:on-success
(fn [[token-id]]
(json-rpc/eth-call
{:contract contract
;; Returns pack id in the contract by token id
:method "tokenPackId(uint256)"
:params [token-id]
:outputs ["uint256"]
:number-of-retries 3
:on-success
(fn [[pack-id]]
(re-frame/dispatch [:stickers/pack-owned pack-id]))}))})))})))
(fx/defn init-stickers-packs
[{:keys [db] :as cofx}]
(let [sticker-packs (get-in db [:multiaccount :stickers/packs-installed])
pending-packs (get-in db [:multiaccount :stickers/packs-pending] #{})]
(cond-> {:db (assoc db
:stickers/packs-installed sticker-packs
:stickers/packs sticker-packs
:stickers/packs-pending pending-packs)}
(not-empty pending-packs)
(assoc :stickers/set-pending-timeout-fx nil))))
(fx/defn install-stickers-pack
{:events [:stickers/install-pack]}
[{{:keys [multiaccount] :as db} :db :as cofx} id]
(let [pack (get-in db [:stickers/packs id])]
(fx/merge
cofx
{:db (-> db
(assoc-in [:stickers/packs-installed id] pack))}
;;(assoc :stickers/selected-pack id))} TODO it doesn't scroll to selected pack on Android
(multiaccounts.update/multiaccount-update
:stickers/packs-installed
(assoc (:stickers/packs-installed multiaccount) id pack)
{}))))
(defn valid-sticker? [sticker]
(contains? sticker :hash))
(fx/defn load-sticker-pack-success
{:events [:stickers/load-sticker-pack-success]}
[{:keys [db] :as cofx} edn-string id price]
(let [{:keys [stickers] :as pack} (assoc (get (edn/read-string edn-string) 'meta)
:id id :price price)]
(fx/merge cofx
{:db (cond-> db
(and (seq stickers)
(every? valid-sticker? stickers))
(assoc-in [:stickers/packs id] pack))})))
(fx/defn open-sticker-pack
{:events [:stickers/open-sticker-pack]}
[{{:stickers/keys [packs packs-installed] :networks/keys [current-network] :as db} :db :as cofx} id]
(when (and id (string/starts-with? current-network "mainnet"))
(let [pack (or (get packs-installed id)
(get packs id))
contract-address (contracts/get-address db :status/stickers)
pack-contract (contracts/get-address db :status/sticker-pack)
address (ethereum/default-address db)]
(fx/merge cofx
(navigation/open-modal :stickers-pack {:id id})
#(when (and contract-address (not pack))
{:stickers/pack-data-fx [contract-address id]
:stickers/owned-packs-fx [pack-contract address]})))))
(fx/defn load-pack
{:events [:stickers/load-pack]}
[_ url id price]
{:http-get {:url url
:on-success
(fn [o]
(re-frame/dispatch [:stickers/load-sticker-pack-success o id price]))}})
[{db :db :as cofx} id]
(fx/merge
cofx
{:db (assoc-in db [:stickers/packs id :status] constants/sticker-pack-status-installed)
::json-rpc/call [{:method "stickers_install"
:params [(ethereum/chain-id db) id]
:on-success #()}]}))
(fx/defn load-packs
{:events [:stickers/load-packs]}
[{:keys [db]}]
(let [contract (contracts/get-address db :status/stickers)
pack-contract (contracts/get-address db :status/sticker-pack)
address (ethereum/default-address db)]
(when contract
{:stickers/owned-packs-fx [pack-contract address]
:stickers/load-packs-fx [contract]})))
{::json-rpc/call [{:method "stickers_market"
:params [(ethereum/chain-id db)]
:on-success #(re-frame/dispatch [:stickers/stickers-market-success %])}
{:method "stickers_installed"
:params []
:on-success #(re-frame/dispatch [:stickers/stickers-installed-success %])}
{:method "stickers_pending"
:params []
:on-success #(re-frame/dispatch [:stickers/stickers-pending-success %])}
{:method "stickers_recent"
:params []
:on-success #(re-frame/dispatch [:stickers/stickers-recent-success %])}]})
(fx/defn approve-pack
(fx/defn buy-pack
{:events [:stickers/buy-pack]}
[{db :db :as cofx} pack-id price]
(let [address (ethereum/default-address db)
sticker-market-contract (contracts/get-address db :status/sticker-market)
snt-contract (contracts/get-address db :status/snt)]
(signing/eth-transaction-call
cofx
{:contract snt-contract
:method "approveAndCall(address,uint256,bytes)"
:params [sticker-market-contract
price
(abi-spec/encode "buyToken(uint256,address,uint256)" [pack-id address price])]
:on-result [:stickers/pending-pack pack-id]})))
[{db :db} pack-id]
{::json-rpc/call [{:method "stickers_buyPrepareTx"
:params [(ethereum/chain-id db) (ethereum/default-address db) (int pack-id)]
:on-success #(re-frame/dispatch [:signing.ui/sign
{:tx-obj %
:on-result [:stickers/pending-pack pack-id]}])}]})
(fx/defn pending-pack
{:events [:stickers/pending-pack]}
[{{:keys [multiaccount] :as db} :db :as cofx} id]
(let [contract (contracts/get-address db :status/sticker-pack)
address (ethereum/default-address db)
pending (get multiaccount :stickers/packs-pending #{})]
(when contract
(fx/merge cofx
{:db (update db :stickers/packs-pending conj id)
:stickers/owned-packs-fx [contract address]}
(multiaccounts.update/multiaccount-update
:stickers/packs-pending
(conj pending id)
{})
#(when (zero? (count (:stickers/packs-pending db)))
{:stickers/set-pending-timeout-fx nil})))))
[{db :db} id]
{:db (-> db
(assoc-in [:stickers/packs id :status] constants/sticker-pack-status-pending)
(update :stickers/packs-pending conj id))
:stickers/set-pending-timeout-fx nil
::json-rpc/call [{:method "stickers_addPending"
:params [(ethereum/chain-id db) (int id)]
:on-success #()}]})
(fx/defn pending-timeout
{:events [:stickers/pending-timeout]}
[{{:stickers/keys [packs-pending packs-owned] :as db} :db :as cofx}]
(let [packs-diff (clojure.set/difference packs-pending packs-owned)
contract (contracts/get-address db :status/sticker-pack)
address (ethereum/default-address db)]
(when contract
(fx/merge cofx
(merge {:db (assoc db :stickers/packs-pending packs-diff)}
(when-not (zero? (count packs-diff))
{:stickers/owned-packs-fx [contract address]
:stickers/set-pending-timeout-fx nil}))
(multiaccounts.update/multiaccount-update
:stickers/packs-pending
packs-diff
{})))))
[{{:stickers/keys [packs-pending] :as db} :db}]
(when (seq packs-pending)
{::json-rpc/call [{:method "stickers_processPending"
:params [(ethereum/chain-id db)]
:on-success #(re-frame/dispatch [:stickers/stickers-process-pending-success %])}]}))
(fx/defn pack-owned
{:events [:stickers/pack-owned]}
[{db :db} id]
{:db (update db :stickers/packs-owned conj id)})
(fx/defn stickers-process-pending-success
{:events [:stickers/stickers-process-pending-success]}
[{{:stickers/keys [packs-pending packs] :as db} :db} purchased]
(let [purchased-ids (map :id (vals purchased))
packs-pending (apply disj packs-pending purchased-ids)
packs (reduce (fn [packs id]
(assoc-in packs [id :status] constants/sticker-pack-status-owned))
packs
purchased-ids)]
(merge
{:db (-> db
(assoc :stickers/packs packs)
(assoc :stickers/packs-pending packs-pending))}
(when (seq packs-pending)
{:stickers/set-pending-timeout-fx nil}))))
(fx/defn stickers-market-success
{:events [:stickers/stickers-market-success]}
[{:keys [db]} packs]
(let [packs (reduce (fn [acc pack] (assoc acc (:id pack) pack)) {} packs)]
{:db (update db :stickers/packs merge packs)}))
(fx/defn stickers-installed-success
{:events [:stickers/stickers-installed-success]}
[{:keys [db]} packs]
(let [packs (reduce (fn [acc [_ pack]] (assoc acc (:id pack) pack)) {} packs)]
{:db (update db :stickers/packs merge packs)}))
(fx/defn stickers-pending-success
{:events [:stickers/stickers-pending-success]}
[{:keys [db]} packs]
(let [packs (reduce (fn [acc [_ pack]] (assoc acc (:id pack) pack)) {} packs)]
(merge
{:db (-> db
(assoc :stickers/packs-pending (into #{} (keys packs)))
(update :stickers/packs merge packs))}
(when (seq packs)
{:stickers/set-pending-timeout-fx nil}))))
(fx/defn stickers-recent-success
{:events [:stickers/stickers-recent-success]}
[{:keys [db]} packs]
{:db (assoc db :stickers/recent-stickers packs)})
(fx/defn open-sticker-pack
{:events [:stickers/open-sticker-pack]}
[{{:networks/keys [current-network]} :db :as cofx} id]
(when (and id (or config/stickers-test-enabled? (string/starts-with? current-network "mainnet")))
(navigation/open-modal cofx :stickers-pack {:id id})))
(fx/defn select-pack
{:events [:stickers/select-pack]}

View File

@ -1,7 +0,0 @@
(ns status-im.stickers.core-test
(:require [cljs.test :refer-macros [deftest is]]
[status-im.stickers.core :as stickers]))
(deftest valid-sticker?
(is (true? (stickers/valid-sticker? {:hash ""})))
(is (false? (stickers/valid-sticker? {}))))

View File

@ -136,9 +136,7 @@
;;stickers
(reg-root-key-sub :stickers/selected-pack :stickers/selected-pack)
(reg-root-key-sub :stickers/packs :stickers/packs)
(reg-root-key-sub :stickers/installed-packs :stickers/packs-installed)
(reg-root-key-sub :stickers/packs-owned :stickers/packs-owned)
(reg-root-key-sub :stickers/packs-pending :stickers/packs-pending)
(reg-root-key-sub :stickers/recent-stickers :stickers/recent-stickers)
;;mailserver
(reg-root-key-sub :mailserver/current-id :mailserver/current-id)
@ -1336,7 +1334,7 @@
(fn [[{:keys [processing]} sending-image mainnet? one-to-one-chat? {:keys [public?]} reply edit]]
(let [sending-image (seq sending-image)]
{:send (not processing)
:stickers (and mainnet?
:stickers (and (or config/stickers-test-enabled? mainnet?)
(not sending-image)
(not reply))
:image (and (not reply)
@ -1515,23 +1513,20 @@
;;STICKERS =============================================================================================================
(re-frame/reg-sub
:stickers/installed-packs-vals
:<- [:stickers/installed-packs]
:stickers/installed-packs
:<- [:stickers/packs]
(fn [packs]
(vals packs)))
(filter #(= (:status %) constants/sticker-pack-status-installed) (vals packs))))
(re-frame/reg-sub
:stickers/all-packs
:<- [:stickers/packs]
:<- [:stickers/installed-packs]
:<- [:stickers/packs-owned]
:<- [:stickers/packs-pending]
(fn [[packs installed owned pending]]
(map (fn [{:keys [id] :as pack}]
(cond-> pack
(get installed id) (assoc :installed true)
(get owned id) (assoc :owned true)
(get pending id) (assoc :pending true)))
(fn [packs]
(map (fn [{:keys [status] :as pack}]
(-> pack
(assoc :installed (= status constants/sticker-pack-status-installed))
(assoc :pending (= status constants/sticker-pack-status-pending))
(assoc :owned (= status constants/sticker-pack-status-owned))))
(vals packs))))
(re-frame/reg-sub
@ -1541,19 +1536,6 @@
(fn [[{:keys [id]} packs]]
(first (filter #(= (:id %) id) packs))))
(defn find-pack-id-for-hash [sticker-uri packs]
(some (fn [{:keys [stickers id]}]
(when (some #(= sticker-uri (:hash %)) stickers)
id))
packs))
(re-frame/reg-sub
:stickers/recent
:<- [:multiaccount]
:<- [:stickers/installed-packs-vals]
(fn [[{:keys [:stickers/recent-stickers]} packs]]
(map (fn [hash] {:hash hash :pack (find-pack-id-for-hash hash packs)}) recent-stickers)))
;;HOME ==============================================================================================================
(def memo-home-items (atom nil))

View File

@ -8,7 +8,8 @@
[status-im.utils.handlers :as handlers]
status-im.transport.shh
[taoensso.timbre :as log]
[status-im.utils.universal-links.core :as universal-links]))
[status-im.utils.universal-links.core :as universal-links]
[status-im.stickers.core :as stickers]))
(fx/defn set-node-info
{:events [:transport.callback/node-info-fetched]}
@ -65,4 +66,5 @@
(add-mailservers mailservers))}
(fetch-node-info-fx)
(pairing/init)
(stickers/load-packs)
(universal-links/process-stored-event)))

View File

@ -18,7 +18,6 @@
[status-im.utils.debounce :as debounce]
[status-im.utils.http :as http]
[status-im.utils.js-resources :as js-res]
[status-im.utils.contenthash :as contenthash]
[status-im.ui.components.permissions :as components.permissions]
[quo.core :as quo]
[status-im.ui.screens.wallet.components.views :as components]
@ -58,8 +57,7 @@
(reagent/as-element
[react/view styles/web-view-error
[react/image {:style {:width 140 :height 140 :margin-bottom 16}
:source {:uri (contenthash/url
"e3010170122001bbe2f5bfba0305a3bdc2047fddc47ee595a591bdee61de6040ffc2456624e1")}}]
:source {:uri "https://bafybeiabxprplp52amc2hpocar753rd64wk2len55zq54yca77bekzre4e.ipfs.cf-ipfs.com"}}]
[react/i18n-text {:style styles/web-view-error-text :key :web-view-error}]
[react/text {:style styles/web-view-error-text}
(str desc)]]))

View File

@ -14,7 +14,6 @@
[status-im.ui.screens.chat.message.gap :as message.gap]
[status-im.ui.screens.chat.styles.message.message :as style]
[status-im.ui.screens.chat.utils :as chat.utils]
[status-im.utils.contenthash :as contenthash]
[status-im.utils.security :as security]
[status-im.ui.screens.chat.message.reactions :as reactions]
[status-im.ui.screens.chat.image.preview.views :as preview]
@ -558,7 +557,7 @@
:accessibility-label :sticker-message
:on-press (fn [_]
(when pack
(re-frame/dispatch [:stickers/open-sticker-pack pack]))
(re-frame/dispatch [:stickers/open-sticker-pack (str pack)]))
(react/dismiss-keyboard!))
:delay-long-press 100
:on-long-press (fn []
@ -568,8 +567,7 @@
(re-frame/dispatch [:chat.ui/show-profile from]))
:label (i18n/label :t/view-details)}])))})
[react/fast-image {:style {:margin 10 :width 140 :height 140}
;;TODO (perf) move to event
:source {:uri (contenthash/url (-> content :sticker :hash))}}]]
:source {:uri (str (-> content :sticker :url) "&download=true")}}]]
reaction-picker]))
(defmethod ->message constants/content-type-image

View File

@ -2,14 +2,12 @@
(:require-macros [status-im.utils.views :refer [defview letsubs]])
(:require [re-frame.core :as re-frame]
[reagent.core :as reagent]
[status-im.stickers.core :as stickers]
[status-im.ui.components.react :as react]
[status-im.ui.components.icons.icons :as icons]
[quo.design-system.colors :as colors]
[status-im.i18n.i18n :as i18n]
[quo.core :as quo]
[status-im.ui.screens.chat.stickers.styles :as styles]
[status-im.utils.contenthash :as contenthash]
[status-im.utils.debounce :as debounce]))
(def icon-size 28)
@ -26,25 +24,23 @@
:height 64}]
[react/text {:style {:margin-vertical 8 :font-size 17}} (i18n/label :t/you-dont-have-stickers)]
[quo/button {:type :secondary
:on-press #(do
(re-frame/dispatch [:stickers/load-packs])
(re-frame/dispatch [:navigate-to :stickers]))}
:on-press #(re-frame/dispatch [:navigate-to :stickers])}
(i18n/label :t/get-stickers)]])
(defn- stickers-panel [stickers window-width]
[react/view {:width window-width :flex 1}
[react/scroll-view
[react/view {:style styles/stickers-panel}
(for [{:keys [hash] :as sticker} stickers]
^{:key (str hash)}
(for [{:keys [url] :as sticker} stickers]
^{:key (str url)}
[react/touchable-highlight {:style {:height 75 :width 75 :margin 5}
:on-press #(debounce/dispatch-and-chill [:chat/send-sticker sticker] 1000)}
[react/fast-image {:style {:width "100%" :height "100%"}
:accessibility-label :sticker-icon
:source {:uri (contenthash/url (str "0x" hash))}}]])]]])
:source {:uri (str url "&download=true")}}]])]]])
(defview recent-stickers-panel [window-width]
(letsubs [stickers [:stickers/recent]]
(letsubs [stickers [:stickers/recent-stickers]]
(if (seq stickers)
[stickers-panel stickers window-width]
[react/view {:style {:flex 1
@ -98,7 +94,7 @@
[recent-stickers-panel width]
(for [{:keys [stickers id]} installed-packs]
^{:key (str "sticker" id)}
[stickers-panel (map #(assoc % :pack id) (filter stickers/valid-sticker? stickers)) width])]))
[stickers-panel (map #(assoc % :pack id) stickers) width])]))
(defn pack-icon [{:keys [id on-press background-color]
:or {on-press #(re-frame/dispatch [:stickers/select-pack id])}}
@ -118,7 +114,7 @@
(defview stickers-view []
(letsubs [selected-pack [:stickers/selected-pack]
installed-packs [:stickers/installed-packs-vals]]
installed-packs [:stickers/installed-packs]]
[react/view {:style {:background-color colors/white
:flex 1}}
(cond
@ -126,9 +122,7 @@
(not (seq installed-packs)) [no-stickers-yet-panel]
:else [stickers-paging-panel installed-packs selected-pack])
[react/view {:style {:flex-direction :row :padding-horizontal 4}}
[pack-icon {:on-press #(do
(re-frame/dispatch [:stickers/load-packs])
(re-frame/dispatch [:navigate-to :stickers]))
[pack-icon {:on-press #(re-frame/dispatch [:navigate-to :stickers])
:selected? false :background-color colors/blue}
[icons/icon :main-icons/add {:width 20 :height 20 :color colors/white-persist}]]
[react/view {:width 2}]
@ -140,9 +134,9 @@
:width 44
:height 44}]]
(for [{:keys [id thumbnail]} installed-packs]
^{:key id}
^{:key (str "pack-icon" id)}
[pack-icon {:id id
:background-color colors/white}
[react/fast-image {:style {:width icon-size :height icon-size :border-radius (/ icon-size 2)}
:source {:uri (contenthash/url thumbnail)}}]])]
:source {:uri (str thumbnail "&download=true")}}]])]
[scroll-indicator]]]]]))

View File

@ -15,7 +15,6 @@
[status-im.ui.screens.chat.image.preview.views :as preview]
[status-im.ui.screens.chat.photos :as photos]
[status-im.ui.components.tabs :as tabs]
[status-im.utils.contenthash :as contenthash]
[status-im.ui.screens.chat.message.reactions :as reactions]
[status-im.chat.models.reactions :as models.reactions]
[status-im.ui.screens.chat.components.reply :as components.reply]
@ -175,8 +174,6 @@
[tabs/tab-title state :timeline (i18n/label :t/timeline) (= tab :timeline)]
[tabs/tab-title state :status (i18n/label :t/my-status) (= tab :status)]]))
(def image-hash "e3010170122080c27fe972a95dbb4b0ead029d2c73d18610e849fac197e91068a918755e21b2")
(defn timeline []
(let [messages @(re-frame/subscribe [:chats/timeline-messages-stream])
loading-messages? @(re-frame/subscribe [:chats/loading-messages? constants/timeline-chat-id])
@ -191,10 +188,10 @@
(if no-messages?
[react/view {:padding-horizontal 32
:margin-top 64}
[react/image {:style {:width 140
:height 140
:align-self :center}
:source {:uri (contenthash/url image-hash)}}]
[react/fast-image {:style {:width 140
:height 140
:align-self :center}
:source {:uri "https://bafybeieayj76s4vjlw5uwdvnakosy46rqyioqsp2ygl6sedivemhkxrbwi.ipfs.cf-ipfs.com"}}]
[react/view (styles/descr-container)
[react/text {:style {:color colors/gray
:line-height 22}}

View File

@ -5,54 +5,56 @@
[status-im.ui.components.icons.icons :as icons]
[status-im.ui.components.react :as react]
[status-im.ui.screens.stickers.styles :as styles]
[status-im.utils.contenthash :as contenthash]
[status-im.utils.money :as money])
[status-im.utils.money :as money]
[status-im.utils.handlers :refer [<sub]])
(:require-macros [status-im.utils.views :refer [defview letsubs]]))
(defn cache [url]
(str url "&download=true"))
(defn- thumbnail-icon [uri size]
[react/fast-image {:style {:width size :height size :border-radius (/ size 2)}
:source {:uri uri}}])
:source {:uri (cache uri)}}])
(defn- installed-icon []
[react/view styles/installed-icon
[icons/icon :main-icons/check {:color colors/white-persist :height 20 :width 20}]])
(defn price-badge [_]
(let [chain (<sub [:ethereum/chain-keyword])
balance (<sub [:balance-default])]
(fn [{:keys [id price pending owned installed]}]
(let [snt (if (= :mainnet chain) (:SNT balance) (:STT balance))
price (money/bignumber price)
not-enough-snt? (or (nil? snt) (money/equal-to snt 0) (money/greater-than price snt))
free (money/equal-to price 0)]
(if installed
[react/view styles/installed-icon
[icons/icon :main-icons/check {:color colors/white-persist :height 20 :width 20}]]
[react/touchable-highlight {:on-press #(cond pending nil
(or owned free)
(re-frame/dispatch [:stickers/install-pack id])
not-enough-snt? nil
:else (re-frame/dispatch [:stickers/buy-pack id]))}
[react/view (styles/price-badge (and (not (or owned free)) not-enough-snt?))
(when (and (not free) (not owned))
[icons/tiny-icon :tiny-icons/tiny-snt {:color colors/white-persist :container-style {:margin-right 6}}])
(if pending
[react/activity-indicator {:animating true
:color colors/white-persist}]
[react/text {:style {:color colors/white-persist}
:accessibility-label :sticker-pack-price}
(cond owned (i18n/label :t/install)
free (i18n/label :t/free)
:else (str (money/wei-> :eth price)))])]])))))
(defview price-badge [price id owned? pending]
(letsubs [chain [:ethereum/chain-keyword]
balance [:balance-default]]
(let [snt (money/to-number (if (= :mainnet chain) (:SNT balance) (:STT balance)))
not-enough-snt? (> price snt)
no-snt? (or (nil? snt) (zero? snt))]
[react/touchable-highlight {:on-press #(cond pending nil
(or owned? (zero? price))
(re-frame/dispatch [:stickers/install-pack id])
(or no-snt? not-enough-snt?) nil
:else (re-frame/dispatch [:stickers/buy-pack id price]))}
[react/view (styles/price-badge (and (not (or owned? (zero? price))) (or no-snt? not-enough-snt?)))
(when (and (not (zero? price)) (not owned?))
[icons/tiny-icon :tiny-icons/tiny-snt {:color colors/white-persist :container-style {:margin-right 6}}])
(if pending
[react/activity-indicator {:animating true
:color colors/white-persist}]
[react/text {:style {:color colors/white-persist}
:accessibility-label :sticker-pack-price}
(cond owned? (i18n/label :t/install)
(zero? price) (i18n/label :t/free)
:else (str (money/wei-> :eth price)))])]])))
(defn pack-badge [{:keys [name author price thumbnail preview id installed owned pending]}]
(defn pack-badge [{:keys [name author thumbnail preview id] :as pack}]
[react/touchable-highlight {:on-press #(re-frame/dispatch [:navigate-to :stickers-pack {:id id}])}
[react/view {:margin-bottom 27}
[react/fast-image {:style {:height 200 :border-radius 20} :source {:uri (contenthash/url preview)}}]
[react/fast-image {:style {:height 200 :border-radius 20} :source {:uri (cache preview)}}]
[react/view {:height 64 :align-items :center :flex-direction :row}
[thumbnail-icon (contenthash/url thumbnail) 40]
[thumbnail-icon thumbnail 40]
[react/view {:padding-horizontal 16 :flex 1}
[react/text {:accessibility-label :sticker-pack-name} name]
[react/text {:style {:color colors/gray :margin-top 6}
[react/text {:style {:color colors/gray :margin-top 6}
:accessibility-label :sticker-pack-author} author]]
(if installed
[installed-icon]
[price-badge price id owned pending])]]])
[price-badge pack]]]])
(defview packs []
(letsubs [packs [:stickers/all-packs]]
@ -70,28 +72,25 @@
(def sticker-icon-size 60)
(defview pack-main []
(letsubs [{:keys [id name author price thumbnail
stickers installed owned pending]
:as pack}
(letsubs [{:keys [name author thumbnail stickers]
:as pack}
[:stickers/get-current-pack]]
[react/keyboard-avoiding-view {:flex 1}
(if pack
[react/view {:flex 1}
[react/view {:height 74 :align-items :center :flex-direction :row :padding-horizontal 16}
[thumbnail-icon (contenthash/url thumbnail) 64]
[thumbnail-icon thumbnail 64]
[react/view {:padding-horizontal 16 :flex 1}
[react/text {:style {:typography :header}} name]
[react/text {:style {:color colors/gray :margin-top 6}} author]]
(if installed
[installed-icon]
[price-badge price id owned pending])]
[price-badge pack]]
[react/view {:style {:padding-top 8 :flex 1}}
[react/scroll-view {:keyboard-should-persist-taps :handled :style {:flex 1}}
[react/view {:flex-direction :row :flex-wrap :wrap}
(for [{:keys [hash]} stickers]
^{:key hash}
[react/fast-image {:style (styles/sticker-image sticker-icon-size)
:source {:uri (contenthash/url hash)}}])]]]]
(for [{:keys [url]} stickers]
^{:key url}
[react/fast-image {:style (styles/sticker-image sticker-icon-size)
:source {:uri (cache url)}}])]]]]
[react/view {:flex 1 :align-items :center :justify-content :center}
[react/activity-indicator {:animating true}]])]))

View File

@ -54,6 +54,7 @@
(def test-stateofus? (enabled? (get-config :TEST_STATEOFUS "0")))
(def two-minutes-syncing? (enabled? (get-config :TWO_MINUTES_SYNCING "0")))
(def swap-enabled? (enabled? (get-config :SWAP_ENABLED "0")))
(def stickers-test-enabled? (enabled? (get-config :STICKERS_TEST_ENABLED "0")))
;; CONFIG VALUES
(def log-level

View File

@ -1,48 +0,0 @@
(ns status-im.utils.contenthash
"TODO: currently we only support encoding/decoding ipfs contenthash
implementing swarm and other protocols will come later"
(:refer-clojure :exclude [cat])
(:require ["hi-base32" :as hi-base32]
[alphabase.hex :as hex]
[clojure.string :as string]
[status-im.ethereum.core :as ethereum]))
(defn decode
"TODO properly decode the CID
extract the content-type using varint ns"
[hex]
(when (and hex (not= hex "0x"))
(cond
(and (string/starts-with? hex "0xe40101"))
;; content type can be 2 or 4 bytes
;; we expect 1b20 (hash algorithm keccak256 and hash length 64)
;; before the hash so we split the contenthash there
(when-let [hash (second (string/split hex "1b20"))]
{:namespace :swarm
:hash hash})
(and (= 78 (count hex))
(string/starts-with? hex "0xe3010170"))
{:namespace :ipfs
:hash (str "b" (-> hex
(subs 6)
hex/decode
((fn [v] (.encode ^js hi-base32 v)))
(string/replace #"=" "")
string/lower-case))}
(and (string/starts-with? hex "0xe50101700"))
{:namespace :ipns
:hash (-> hex
(subs 14)
((fn [v] (str "0x" v)))
(ethereum/hex-to-utf8))})))
(defn ipfs-url [hash]
(str "https://" hash ".ipfs.cf-ipfs.com"))
(defn url-fn [hex]
(let [{:keys [namespace hash]} (decode (ethereum/normalized-hex hex))]
(case namespace
:ipfs (ipfs-url hash)
"")))
(def url (memoize url-fn))

View File

@ -1,26 +0,0 @@
(ns status-im.utils.contenthash-test
(:require [cljs.test :refer-macros [deftest is testing]]
[status-im.utils.contenthash :as contenthash]))
;; we reuse the exemple from EIP-1577 but omit content type: dag-pb (0x70)
;; in the contenthash as we do not support it yet
(deftest contenthash-decode
(testing "decoding a valid ipfs hash"
(is (= (contenthash/decode "0xe3010170122029f2d17be6139079dc48696d1f582a8530eb9805b561eda517e22a892c7e3f1f")
{:namespace :ipfs
:hash "bafybeibj6lixxzqtsb45ysdjnupvqkufgdvzqbnvmhw2kf7cfkesy7r7d4"})))
(testing "decoding a valid ipns hash"
(is (= (contenthash/decode "0xe5010170000f6170702e756e69737761702e6f7267")
{:namespace :ipns
:hash "app.uniswap.org"})))
(testing "decoding a valid swarm hash"
(is (= (contenthash/decode "0xe40101fa011b20d1de9994b4d039f6548d191eb26786769f580809256b4685ef316805265ea162")
{:namespace :swarm
:hash "d1de9994b4d039f6548d191eb26786769f580809256b4685ef316805265ea162"})))
(testing "decoding an invalid ipfs hash"
(is (nil? (contenthash/decode "0xe301122029f2d17be6139079dc48696d1f582a8530eb9805b561eda517e2"))))
(testing "decoding random garbage"
(is (nil? (contenthash/decode "0xabcdef1234567890"))))
(testing "decoding nil"
(is (nil? (contenthash/decode nil)))))

View File

@ -41,6 +41,10 @@
[bn1 bn2]
(.greaterThan ^js bn1 bn2))
(defn equal-to
[bn1 bn2]
(.eq ^js bn1 bn2))
(defn sub [bn1 bn2]
(.sub ^js bn1 bn2))

View File

@ -3,7 +3,7 @@
"_comment": "Instead use: scripts/update-status-go.sh <rev>",
"owner": "status-im",
"repo": "status-go",
"version": "v0.98.5",
"commit-sha1": "640793fe85d9a9eef9eb3712cda1c5a1ceea401a",
"src-sha256": "1k1iw69yw23k29y0b1yvnggjqlmfnwavk4mq1sf2h3wrlb8x47kd"
"version": "v0.98.6",
"commit-sha1": "0048aaebcc7859a6f0dd7cdf0266fe029f3066fc",
"src-sha256": "1px3ddfbpnlqyzkbp8v4bb8dh5f69cx4lwif8vspw60rr4gpppys"
}