Add TtT chat flow

Signed-off-by: yenda <eric@status.im>
This commit is contained in:
Vitaliy Vlasov 2019-02-22 14:26:40 +02:00 committed by yenda
parent 75e20a0bbd
commit 04ccd0e7d8
No known key found for this signature in database
GPG Key ID: 0095623C0069DCE6
37 changed files with 1504 additions and 749 deletions

View File

@ -79,7 +79,7 @@ A whitelists B if:
The tribute payment is a regular SNT transaction greater or equal to the value of the snt-amount key in the manifest.
The id of the transaction is sent along messages in the payload top level `:tribute-tx-id` key as long as the sender isn't sure that he is whitelisted by the recipient (see `whitelisting rules` above).
The id of the transaction is sent along messages in the payload top level `:tribute-transaction` key as long as the sender isn't sure that he is whitelisted by the recipient (see `whitelisting rules` above).
The UI in status app waits for 1 confirmation on chain before allowing the user to send a message to ensure the recipient will see the payment and not ignore the message.

View File

@ -3,7 +3,7 @@
[status-im.chat.constants :as chat.constants]
[status-im.chat.commands.protocol :as protocol]
[status-im.chat.commands.impl.transactions :as transactions]
[status-im.contact.db :as db]
[status-im.ethereum.core :as ethereum]
[status-im.utils.handlers :as handlers]
[status-im.utils.fx :as fx]))
@ -33,7 +33,7 @@
(keyword (str (protocol/id type) "-button")))
(defn- contact->address [contact]
(str "0x" (db/public-key->address contact)))
(str "0x" (ethereum/public-key->address contact)))
(defn add-chat-contacts
"Enrich command-message by adding contact list of the current private or group chat"

View File

@ -305,9 +305,9 @@
protocol/Yielding
(yield-control [_ {{{amount :amount asset :asset} :params} :content} {:keys [db] :as cofx}]
;; Prefill wallet and navigate there
(let [recipient-contact (or
(get-in db [:contacts/contacts (:current-chat-id db)])
(db.contact/public-key->new-contact (:current-chat-id db)))
(let [recipient-contact (or
(get-in db [:contacts/contacts (:current-chat-id db)])
(db.contact/public-key->new-contact (:current-chat-id db)))
sender-account (:account/account db)
chain (keyword (:chain db))

View File

@ -1,16 +1,17 @@
(ns status-im.chat.models
(:require [re-frame.core :as re-frame]
[status-im.accounts.db :as accounts.db]
[status-im.contact-code.core :as contact-code]
[status-im.contact.core :as contact.core]
[status-im.data-store.chats :as chats-store]
[status-im.data-store.messages :as messages-store]
[status-im.data-store.user-statuses :as user-statuses-store]
[status-im.contact-code.core :as contact-code]
[taoensso.timbre :as log]
[status-im.i18n :as i18n]
[status-im.mailserver.core :as mailserver]
[status-im.transport.chat.core :as transport.chat]
[status-im.transport.utils :as transport.utils]
[status-im.transport.message.protocol :as protocol]
[status-im.transport.message.public-chat :as public-chat]
[status-im.tribute-to-talk.core :as tribute-to-talk]
[status-im.ui.components.colors :as colors]
[status-im.ui.components.desktop.events :as desktop.events]
[status-im.ui.components.react :as react]
@ -23,7 +24,8 @@
[status-im.utils.utils :as utils]
[status-im.utils.config :as config]
[status-im.mailserver.core :as mailserver]
[status-im.transport.partitioned-topic :as transport.topic]))
[status-im.transport.partitioned-topic :as transport.topic]
[taoensso.timbre :as log]))
(defn- get-chat [cofx chat-id]
(get-in cofx [:db :chats chat-id]))
@ -267,7 +269,8 @@
(set-chat-ui-props {:validation-messages nil}))}
(contact-code/listen-to-chat chat-id)
(when platform/desktop?
(mark-messages-seen chat-id))))
(mark-messages-seen chat-id))
(tribute-to-talk/check-tribute chat-id)))
(fx/defn navigate-to-chat
"Takes coeffects map and chat-id, returns effects necessary for navigation and preloading data"
@ -345,3 +348,11 @@
(re-frame/reg-fx
:set-dock-badge-label
set-dock-badge-label)
(fx/defn show-profile
{:events [:chat.ui/show-profile]}
[cofx identity]
(fx/merge (assoc-in cofx [:db :contacts/identity] identity)
(contact.core/create-contact identity)
(tribute-to-talk/check-tribute identity)
(navigation/navigate-to-cofx :profile nil)))

View File

@ -17,6 +17,7 @@
[status-im.transport.message.protocol :as protocol]
[status-im.transport.message.transit :as transit]
[status-im.transport.utils :as transport.utils]
[status-im.tribute-to-talk.core :as tribute-to-talk]
[status-im.ui.components.react :as react]
[status-im.utils.clocks :as utils.clocks]
[status-im.utils.datetime :as time]
@ -473,6 +474,7 @@
:whisper-timestamp (quot now 1000)
:clock-value (utils.clocks/send
last-clock-value))
(tribute-to-talk/add-transaction-hash db)
(add-message-type chat))]
(upsert-and-send cofx message-data)))

View File

@ -1,14 +1,16 @@
(ns status-im.contact.core
(:require [status-im.accounts.db :as accounts.db]
[status-im.chat.models :as chat.models]
[status-im.contact-code.core :as contact-code]
[status-im.contact.db :as contact.db]
[status-im.contact.device-info :as device-info]
[status-im.ethereum.core :as ethereum]
[status-im.data-store.contacts :as contacts-store]
[status-im.mailserver.core :as mailserver]
[status-im.transport.message.contact :as message.contact]
[status-im.transport.message.protocol :as protocol]
[status-im.transport.partitioned-topic :as transport.topic]
[status-im.tribute-to-talk.db :as tribute-to-talk]
[status-im.tribute-to-talk.whitelist :as whitelist]
[status-im.ui.screens.navigation :as navigation]
[status-im.utils.config :as config]
[status-im.utils.fx :as fx]))
@ -16,10 +18,13 @@
(fx/defn load-contacts
[{:keys [db all-contacts]}]
(let [contacts-list (map #(vector (:public-key %) %) all-contacts)
contacts (into {} contacts-list)]
{:db (-> db
(update :contacts/contacts #(merge contacts %))
(assoc :contacts/blocked (contact.db/get-blocked-contacts all-contacts)))}))
contacts (into {} contacts-list)
tr-to-talk-enabled? (-> db tribute-to-talk/get-settings tribute-to-talk/enabled?)]
{:db (cond-> (-> db
(update :contacts/contacts #(merge contacts %))
(assoc :contacts/blocked (contact.db/get-blocked-contacts all-contacts)))
tr-to-talk-enabled?
(assoc :contacts/whitelist (whitelist/get-contact-whitelist all-contacts)))}))
(defn build-contact
[{{:keys [chats] :account/keys [account]
@ -58,7 +63,8 @@
"Add a contact and set pending to false"
[{:keys [db] :as cofx} public-key]
(when (not= (get-in db [:account/account :public-key]) public-key)
(let [contact (-> (build-contact cofx public-key)
(let [contact (-> (get-in db [:contacts/contacts public-key]
(build-contact cofx public-key))
(update :system-tags
(fnil #(conj % :contact/added) #{})))]
(fx/merge cofx
@ -68,9 +74,19 @@
{:chat-ids [public-key]
:topic (transport.topic/discovery-topic-hash)
:fetch? false})
(whitelist/add-to-whitelist public-key)
(send-contact-request contact)
(mailserver/process-next-messages-request)))))
(fx/defn create-contact
"Create entry in contacts"
[{:keys [db] :as cofx} public-key]
(when (not= (get-in db [:account/account :public-key]) public-key)
(let [contact (build-contact cofx public-key)]
(fx/merge cofx
{:db (assoc-in db [:contacts/new-identity] "")}
(upsert-contact contact)))))
(fx/defn add-contacts-filter [{:keys [db]} public-key action]
(when (not= (get-in db [:account/account :public-key]) public-key)
(let [current-public-key (get-in db [:account/account :public-key])]
@ -116,7 +132,7 @@
:name name
:address (or address
(:address contact)
(contact.db/public-key->address public-key))
(ethereum/public-key->address public-key))
:device-info (device-info/merge-info
timestamp
(:device-info contact)
@ -131,11 +147,6 @@
(def receive-contact-request-confirmation handle-contact-update)
(def receive-contact-update handle-contact-update)
(fx/defn open-chat
[cofx public-key]
(fx/merge cofx
(chat.models/start-chat public-key {:navigation-reset? true})))
(fx/defn open-contact-toggle-list
[{:keys [db :as cofx]}]
(fx/merge cofx
@ -143,3 +154,11 @@
:group/selected-contacts #{}
:new-chat-name "")}
(navigation/navigate-to-cofx :contact-toggle-list nil)))
(fx/defn set-tribute
[{:keys [db] :as cofx} public-key tribute-to-talk]
(let [contact (-> (or (build-contact cofx public-key)
(get-in db [:contacts/contacts public-key]))
(assoc :tribute-to-talk (or tribute-to-talk
{:disabled? true})))]
{:db (assoc-in db [:contacts/contacts public-key] contact)}))

View File

@ -2,6 +2,7 @@
(:require [cljs.spec.alpha :as spec]
[status-im.ethereum.core :as ethereum]
[status-im.js-dependencies :as js-dependencies]
[status-im.tribute-to-talk.db :as tribute-to-talk.db]
[status-im.utils.gfycat.core :as gfycat]
[status-im.utils.identicon :as identicon]
status-im.utils.db))
@ -10,29 +11,33 @@
;;Contact
(spec/def :contact/public-key :global/not-empty-string)
(spec/def :contact/name :global/not-empty-string)
(spec/def :contact/address (spec/nilable :global/address))
(spec/def :contact/photo-path (spec/nilable string?))
(spec/def :contact/fcm-token (spec/nilable string?))
(spec/def :contact/last-updated (spec/nilable int?))
(spec/def :contact/last-online (spec/nilable int?))
(spec/def :contact/last-updated (spec/nilable int?))
(spec/def :contact/name :global/not-empty-string)
(spec/def :contact/public-key :global/not-empty-string)
(spec/def :contact/photo-path (spec/nilable string?))
(spec/def :contact/tags (spec/coll-of string? :kind set?))
;; contact/blocked: the user is blocked
;; contact/added: the user was added to the contacts and a contact request was sent
;; contact/request-received: the user sent a contact request
(spec/def :contact/system-tags (spec/coll-of keyword? :kind set?))
(spec/def :contact/tags (spec/coll-of string? :kind set?))
(spec/def :contact/tribute (spec/nilable int?))
(spec/def :contact/tribute-transaction (spec/nilable string?))
(spec/def :contact/contact (spec/keys :req-un [:contact/public-key
(spec/def :contact/contact (spec/keys :req-un [:contact/address
:contact/name
:contact/address
:contact/photo-path
:contact/public-key
:contact/system-tags]
:opt-un [:contact/last-updated
:opt-un [:contact/fcm-token
:contact/last-online
:contact/fcm-token
:contact/tags]))
:contact/last-updated
:contact/tags
:contact/tribute
:contact/tribute-transaction]))
;;Contact list ui props
(spec/def :contact-list-ui/edit? boolean?)
@ -58,19 +63,9 @@
(spec/def :contact/new-tag string?)
(spec/def :ui/contact (spec/keys :opt [:contact/new-tag]))
(defn public-key->address [public-key]
(let [length (count public-key)
normalized-key (case length
132 (subs public-key 4)
130 (subs public-key 2)
128 public-key
nil)]
(when normalized-key
(subs (.sha3 (js-dependencies/web3-prototype) normalized-key #js {:encoding "hex"}) 26))))
(defn public-key->new-contact [public-key]
{:name (gfycat/generate-gfy public-key)
:address (public-key->address public-key)
:address (ethereum/public-key->address public-key)
:photo-path (identicon/identicon public-key)
:public-key public-key
:system-tags #{}})
@ -168,12 +163,17 @@
(active? (get-in db [:contacts/contacts public-key]))))
(defn enrich-contact
[{:keys [system-tags] :as contact}]
(assoc contact
:pending? (pending? contact)
:blocked? (blocked? contact)
:active? (active? contact)
:added? (contains? system-tags :contact/added)))
[{:keys [system-tags tribute-to-talk] :as contact}]
(let [tribute (:snt-amount tribute-to-talk)
tribute-status (tribute-to-talk.db/tribute-status contact)
tribute-label (tribute-to-talk.db/status-label tribute-status tribute)]
(-> contact
(assoc-in [:tribute-to-talk :tribute-status] tribute-status)
(assoc-in [:tribute-to-talk :tribute-label] tribute-label)
(assoc :pending? (pending? contact)
:blocked? (blocked? contact)
:active? (active? contact)
:added? (contains? system-tags :contact/added)))))
(defn enrich-contacts
[contacts]

View File

@ -1,10 +1,12 @@
(ns status-im.data-store.contacts
(:require [re-frame.core :as re-frame]
[taoensso.timbre :as log]
[status-im.data-store.realm.core :as core]))
(defn- deserialize-contact [contact]
(-> contact
(update :tags #(into #{} %))
(update :tribute-to-talk core/deserialize)
(update :system-tags
#(reduce (fn [acc s]
(conj acc (keyword (subs s 1))))
@ -14,7 +16,8 @@
(defn- serialize-contact [contact]
(-> contact
(update :device-info #(or (vals %) []))
(update :system-tags #(mapv str %))))
(update :system-tags #(mapv str %))
(update :tribute-to-talk core/serialize)))
(re-frame/reg-cofx
:data-store/get-all-contacts

View File

@ -128,3 +128,5 @@
:system-tags {:type "string[]"}
:device-info {:type :list
:objectType :contact-device-info}}})
(def v8 (assoc-in v7 [:properties :tribute-to-talk] {:type :string :optional true}))

View File

@ -1,23 +1,23 @@
(ns status-im.data-store.realm.schemas.account.core
(:require [status-im.data-store.realm.schemas.account.chat :as chat]
[status-im.data-store.realm.schemas.account.transport :as transport]
[status-im.data-store.realm.schemas.account.transport-inbox-topic :as transport-inbox-topic]
[status-im.data-store.realm.schemas.account.mailserver-topic :as mailserver-topic]
(:require [status-im.data-store.realm.schemas.account.browser :as browser]
[status-im.data-store.realm.schemas.account.chat :as chat]
[status-im.data-store.realm.schemas.account.chat-requests-range :as chat-requests-range]
[status-im.data-store.realm.schemas.account.contact :as contact]
[status-im.data-store.realm.schemas.account.message :as message]
[status-im.data-store.realm.schemas.account.user-status :as user-status]
[status-im.data-store.realm.schemas.account.contact-device-info :as contact-device-info]
[status-im.data-store.realm.schemas.account.contact-recovery :as contact-recovery]
[status-im.data-store.realm.schemas.account.dapp-permissions :as dapp-permissions]
[status-im.data-store.realm.schemas.account.installation :as installation]
[status-im.data-store.realm.schemas.account.local-storage :as local-storage]
[status-im.data-store.realm.schemas.account.mailserver :as mailserver]
[status-im.data-store.realm.schemas.account.browser :as browser]
[status-im.data-store.realm.schemas.account.dapp-permissions :as dapp-permissions]
[status-im.data-store.realm.schemas.account.request :as request]
[status-im.data-store.realm.schemas.account.membership-update :as membership-update]
[status-im.data-store.realm.schemas.account.installation :as installation]
[status-im.data-store.realm.schemas.account.contact-recovery :as contact-recovery]
[status-im.data-store.realm.schemas.account.mailserver-requests-gap :as mailserver-requests-gap]
[status-im.data-store.realm.schemas.account.mailserver-topic :as mailserver-topic]
[status-im.data-store.realm.schemas.account.membership-update :as membership-update]
[status-im.data-store.realm.schemas.account.message :as message]
[status-im.data-store.realm.schemas.account.migrations :as migrations]
[status-im.data-store.realm.schemas.account.chat-requests-range :as chat-requests-range]))
[status-im.data-store.realm.schemas.account.request :as request]
[status-im.data-store.realm.schemas.account.transport :as transport]
[status-im.data-store.realm.schemas.account.transport-inbox-topic :as transport-inbox-topic]
[status-im.data-store.realm.schemas.account.user-status :as user-status]))
(def v1 [chat/v1
transport/v1
@ -512,6 +512,23 @@
contact-recovery/v1
mailserver-requests-gap/v1])
(def v44 [chat/v15
chat-requests-range/v1
transport/v8
contact/v8
message/v10
mailserver/v11
mailserver-topic/v2
user-status/v2
membership-update/v1
installation/v3
local-storage/v1
browser/v8
dapp-permissions/v9
contact-device-info/v1
contact-recovery/v1
mailserver-requests-gap/v1])
;; put schemas ordered by version
(def schemas [{:schema v1
:schemaVersion 1
@ -641,4 +658,7 @@
:migration migrations/v42}
{:schema v43
:schemaVersion 43
:migration (constantly nil)}
{:schema v44
:schemaVersion 44
:migration (constantly nil)}])

View File

@ -6,7 +6,7 @@
{:mainnet "0x744d70fdbe2ba4cf95131626614a1763df805b9e"
:testnet "0xc55cf4b03948d7ebc8b9e8bad92643703811d162"}
:status/tribute-to-talk
{:testnet "0x3da3fc53e24707f36c5b4433b442e896c4955f0e"}
{:testnet "0xC61aa0287247a0398589a66fCD6146EC0F295432"}
:status/stickers
{:testnet "0x39d16CdB56b5a6a89e1A397A13Fe48034694316E"}})

View File

@ -70,6 +70,11 @@
network (get-in db [:account/account :networks network-id])]
(network->chain-keyword network)))
(defn snt-symbol [db]
(case (chain-keyword db)
:mainnet :SNT
:STT))
(defn sha3
([s]
(.sha3 (dependencies/web3-prototype) (str s)))
@ -88,3 +93,16 @@
(and address1 address2
(= (normalized-address address1)
(normalized-address address2))))
(defn public-key->address [public-key]
(let [length (count public-key)
normalized-key (case length
132 (subs public-key 4)
130 (subs public-key 2)
128 public-key
nil)]
(when normalized-key
(subs (.sha3 (dependencies/web3-prototype)
normalized-key
#js {:encoding "hex"})
26))))

View File

@ -6,6 +6,7 @@
[status-im.accounts.logout.core :as accounts.logout]
[status-im.accounts.recover.core :as accounts.recover]
[status-im.accounts.update.core :as accounts.update]
[status-im.biometric-auth.core :as biomentric-auth]
[status-im.bootnodes.core :as bootnodes]
[status-im.browser.core :as browser]
[status-im.browser.permissions :as browser.permissions]
@ -43,7 +44,6 @@
[status-im.stickers.core :as stickers]
[status-im.transport.core :as transport]
[status-im.transport.message.core :as transport.message]
[status-im.tribute-to-talk.core :as tribute-to-talk]
[status-im.ui.components.bottom-sheet.core :as bottom-sheet]
[status-im.ui.components.react :as react]
[status-im.ui.screens.add-new.new-chat.db :as new-chat.db]
@ -58,11 +58,10 @@
[status-im.utils.logging.core :as logging]
[status-im.utils.utils :as utils]
[status-im.wallet.core :as wallet]
[status-im.wallet.custom-tokens.core :as custom-tokens]
[status-im.wallet.db :as wallet.db]
[status-im.web3.core :as web3]
[status-im.biometric-auth.core :as biomentric-auth]
[taoensso.timbre :as log]
[status-im.wallet.custom-tokens.core :as custom-tokens]))
[taoensso.timbre :as log]))
;; init module
@ -868,12 +867,6 @@
(fn [cofx [_ chat-id message-id]]
(chat.message/toggle-expand-message cofx chat-id message-id)))
(handlers/register-handler-fx
:chat.ui/show-profile
(fn [cofx [_ identity]]
(navigation/navigate-to-cofx
(assoc-in cofx [:db :contacts/identity] identity) :profile nil)))
(handlers/register-handler-fx
:chat.ui/set-chat-input-text
(fn [cofx [_ text]]
@ -1940,97 +1933,7 @@
(stickers/pending-timeout cofx)))
;; Tribute to Talk
(handlers/register-handler-fx
:tribute-to-talk.ui/menu-item-pressed
(fn [cofx _]
(tribute-to-talk/open-settings cofx)))
(handlers/register-handler-fx
:tribute-to-talk.ui/learn-more-pressed
(fn [cofx _]
(tribute-to-talk/open-learn-more cofx)))
(handlers/register-handler-fx
:tribute-to-talk.ui/step-back-pressed
(fn [cofx _]
(tribute-to-talk/step-back cofx)))
(handlers/register-handler-fx
:tribute-to-talk.ui/step-forward-pressed
(fn [cofx _]
(tribute-to-talk/step-forward cofx)))
(handlers/register-handler-fx
:tribute-to-talk.ui/numpad-key-pressed
(fn [cofx [_ numpad-key]]
(tribute-to-talk/update-snt-amount cofx numpad-key)))
(handlers/register-handler-fx
:tribute-to-talk.ui/message-changed
(fn [cofx [_ message]]
(tribute-to-talk/update-message cofx message)))
(handlers/register-handler-fx
:tribute-to-talk.ui/edit-pressed
(fn [cofx _]
(tribute-to-talk/start-editing cofx)))
(handlers/register-handler-fx
:tribute-to-talk.ui/remove-pressed
(fn [cofx _]
(tribute-to-talk/remove cofx)))
(handlers/register-handler-fx
:tribute-to-talk.callback/check-manifest-success
(fn [cofx [_ identity contenthash]]
(tribute-to-talk/fetch-manifest cofx identity contenthash)))
(handlers/register-handler-fx
:tribute-to-talk.callback/no-manifest-found
(fn [cofx [_ identity]]
(tribute-to-talk/update-settings cofx nil)))
(handlers/register-handler-fx
:tribute-to-talk.callback/fetch-manifest-success
(fn [cofx [_ identity {:keys [tribute-to-talk]}]]
(tribute-to-talk/update-settings cofx tribute-to-talk)))
(handlers/register-handler-fx
:tribute-to-talk.callback/fetch-manifest-failure
(fn [cofx [_ identity contenthash]]
(tribute-to-talk/fetch-manifest cofx identity contenthash)))
(handlers/register-handler-fx
:tribute-to-talk.ui/check-manifest
(fn [{:keys [db] :as cofx} [_ identity]]
(tribute-to-talk/check-manifest cofx identity)))
(handlers/register-handler-fx
:tribute-to-talk.callback/manifest-uploaded
(fn [cofx [_ hash]]
(tribute-to-talk/set-manifest-signing-flow cofx hash)))
(handlers/register-handler-fx
:tribute-to-talk.callback/manifest-upload-failed
(fn [cofx [_ error]]
(tribute-to-talk/on-manifest-upload-failed cofx error)))
(handlers/register-handler-fx
:tribute-to-talk.callback/set-manifest-transaction-completed
(fn [cofx [_ id transaction-hash method]]
(tribute-to-talk/on-set-manifest-transaction-completed cofx
transaction-hash)))
(handlers/register-handler-fx
:tribute-to-talk.callback/set-manifest-transaction-failed
(fn [cofx [_ error]]
(tribute-to-talk/on-set-manifest-transaction-failed cofx
error)))
(handlers/register-handler-fx
:tribute-to-talk/check-set-manifest-transaction-timeout
(fn [cofx _]
(tribute-to-talk/check-set-manifest-transaction cofx)))
;; bottom-sheet events
(handlers/register-handler-fx

View File

@ -8,6 +8,7 @@
[status-im.chat.commands.input :as commands.input]
[status-im.chat.constants :as chat.constants]
[status-im.chat.db :as chat.db]
[status-im.chat.models :as chat.models]
[status-im.constants :as constants]
[status-im.contact.db :as contact.db]
[status-im.ethereum.tokens :as tokens]
@ -15,8 +16,13 @@
[status-im.ethereum.transactions.etherscan :as transactions.etherscan]
[status-im.ethereum.core :as ethereum]
[status-im.fleet.core :as fleet]
[status-im.group-chats.db :as group-chats.db]
[status-im.i18n :as i18n]
[status-im.tribute-to-talk.core :as tribute-to-talk]
[status-im.tribute-to-talk.db :as tribute-to-talk.db]
[status-im.tribute-to-talk.whitelist :as whitelist]
[status-im.ui.components.bottom-bar.styles :as tabs.styles]
[status-im.ui.components.colors :as colors]
[status-im.ui.components.toolbar.styles :as toolbar.styles]
[status-im.ui.screens.add-new.new-public-chat.db :as db]
[status-im.ui.screens.chat.stickers.styles :as stickers.styles]
@ -34,7 +40,6 @@
[status-im.utils.universal-links.core :as links]
[status-im.wallet.core :as wallet]
[status-im.wallet.db :as wallet.db]
status-im.tribute-to-talk.subs
status-im.ui.screens.hardwallet.connect.subs
status-im.ui.screens.hardwallet.settings.subs
status-im.ui.screens.hardwallet.pin.subs
@ -473,16 +478,103 @@
(fn [[contacts chats account]]
(chat.db/active-chats contacts chats account)))
(defn enrich-current-one-to-one-chat
[{:keys [contact] :as current-chat} my-public-key ttt-settings
chain-keyword prices currency]
(let [{:keys [tribute-to-talk]} contact
{:keys [disabled? snt-amount message]} tribute-to-talk
whitelisted-by? (whitelist/whitelisted-by? contact)
loading? (and (not whitelisted-by?)
(not tribute-to-talk))
show-input? (or whitelisted-by?
disabled?)
token (case chain-keyword
:mainnet :SNT
:STT)
tribute-status (if loading?
:loading
(tribute-to-talk.db/tribute-status contact))
tribute-label (tribute-to-talk.db/status-label tribute-status snt-amount)]
(cond-> (assoc current-chat
:tribute-to-talk/tribute-status tribute-status
:tribute-to-talk/tribute-label tribute-label)
(#{:required :pending :paid} tribute-status)
(assoc :tribute-to-talk/snt-amount
(tribute-to-talk.db/from-wei snt-amount)
:tribute-to-talk/message
message
:tribute-to-talk/fiat-amount (if snt-amount
(money/fiat-amount-value
snt-amount
token
(-> currency :code keyword)
prices)
"0")
:tribute-to-talk/fiat-currency (:code currency)
:tribute-to-talk/token (str " " (name token)))
(tribute-to-talk.db/enabled? ttt-settings)
(assoc :tribute-to-talk/received? (tribute-to-talk.db/tribute-received?
contact))
(= tribute-status :required)
(assoc :tribute-to-talk/on-share-my-profile
#(re-frame/dispatch
[:profile/share-profile-link my-public-key]))
show-input?
(assoc :show-input? true))))
(defn enrich-current-chat
[{:keys [messages chat-id might-have-join-time-messages?] :as chat}
ranges height input-height]
(assoc chat
:height height
:input-height input-height
:range
(get ranges chat-id)
:intro-status
(if might-have-join-time-messages?
:loading
(if (empty? messages)
:empty
:messages))))
(re-frame/reg-sub
:chats/current-chat
:<- [:chats/active-chats]
:<- [:chats/current-chat-id]
(fn [[chats current-chat-id]]
(let [current-chat (get chats current-chat-id)
messages (:messages current-chat)]
(if (empty? messages)
(assoc current-chat :universal-link (links/generate-link :public-chat :external current-chat-id))
current-chat))))
:<- [:account/public-key]
:<- [:mailserver/ranges]
:<- [:chats/content-layout-height]
:<- [:chats/current-chat-ui-prop :input-height]
:<- [:tribute-to-talk/settings]
:<- [:ethereum/chain-keyword]
:<- [:prices]
:<- [:wallet/currency]
(fn [[chats current-chat-id my-public-key ranges height
input-height ttt-settings chain-keyword prices currency]]
(let [{:keys [group-chat contact messages]
:as current-chat}
(get chats current-chat-id)]
(when current-chat
(cond-> (enrich-current-chat current-chat ranges height input-height)
(empty? messages)
(assoc :universal-link
(links/generate-link :public-chat :external current-chat-id))
(chat.models/public-chat? current-chat)
(assoc :show-input? true)
(and (chat.models/group-chat? current-chat)
(group-chats.db/joined? my-public-key current-chat))
(assoc :show-input? true)
(not group-chat)
(enrich-current-one-to-one-chat my-public-key ttt-settings
chain-keyword prices currency))))))
(re-frame/reg-sub
:chats/current-chat-message
@ -1217,12 +1309,6 @@
(fn [wallet]
(:send-transaction wallet)))
(re-frame/reg-sub
:wallet.send/symbol
:<- [::send-transaction]
(fn [send-transaction]
(:symbol send-transaction)))
(re-frame/reg-sub
:wallet.send/advanced?
:<- [::send-transaction]
@ -1271,10 +1357,12 @@
edit)))
(defn check-sufficient-funds
[transaction balance symbol amount]
(assoc transaction :sufficient-funds?
(or (nil? amount)
(money/sufficient-funds? amount (get balance symbol)))))
[{:keys [sufficient-funds?] :as transaction} balance symbol amount]
(cond-> transaction
(nil? sufficient-funds?)
(assoc :sufficient-funds?
(or (nil? amount)
(money/sufficient-funds? amount (get balance symbol))))))
(defn check-sufficient-gas
[transaction balance symbol amount]
@ -1691,3 +1779,99 @@
:<- [:search/filter]
(fn [[chats search-filter]]
(apply-filter search-filter chats extract-chat-attributes)))
;; TRIBUTE TO TALK
(re-frame/reg-sub
:tribute-to-talk/settings
:<- [:account-settings]
:<- [:ethereum/chain-keyword]
(fn [[settings chain-keyword]]
(get-in settings [:tribute-to-talk chain-keyword])))
(re-frame/reg-sub
:tribute-to-talk/screen-params
:<- [:screen-params]
(fn [screen-params]
(get screen-params :tribute-to-talk)))
(re-frame/reg-sub
:tribute-to-talk/profile
:<- [:tribute-to-talk/settings]
:<- [:tribute-to-talk/screen-params]
(fn [[{:keys [seen? snt-amount]}
{:keys [state unavailable?]}]]
(let [state (or state (if snt-amount :completed :disabled))
snt-amount (tribute-to-talk.db/from-wei snt-amount)]
(when config/tr-to-talk-enabled?
(if unavailable?
{:subtext "Change network to enable Tribute to Talk"
:active? false
:icon :main-icons/tribute-to-talk
:icon-color colors/gray}
(cond-> {:new? (not seen?)}
(and (not (and seen?
snt-amount
(#{:signing :pending :transaction-failed :completed} state))))
(assoc :subtext (i18n/label :t/tribute-to-talk-desc))
(#{:signing :pending} state)
(assoc :activity-indicator {:animating true
:color colors/blue}
:subtext (case state
:pending (i18n/label :t/pending-confirmation)
:signing (i18n/label :t/waiting-to-sign)))
(= state :transaction-failed)
(assoc :icon :main-icons/warning
:icon-color colors/red
:subtext (i18n/label :t/transaction-failed))
(not (#{:signing :pending :transaction-failed} state))
(assoc :icon :main-icons/tribute-to-talk)
(and (= state :completed)
(not-empty snt-amount))
(assoc :accessory-value (str snt-amount " SNT"))))))))
(re-frame/reg-sub
:tribute-to-talk/enabled?
:<- [:tribute-to-talk/settings]
(fn [settings]
(tribute-to-talk.db/enabled? settings)))
(re-frame/reg-sub
:tribute-to-talk/settings-ui
:<- [:tribute-to-talk/settings]
:<- [:tribute-to-talk/screen-params]
:<- [:prices]
:<- [:wallet/currency]
(fn [[{:keys [seen? snt-amount message]
:as settings}
{:keys [step editing? state error]
:or {step :intro}
screen-snt-amount :snt-amount
screen-message :message} prices currency]]
(let [fiat-value (if snt-amount
(money/fiat-amount-value
snt-amount
:SNT
(-> currency :code keyword)
prices)
"0")]
(cond-> {:seen? seen?
:snt-amount (tribute-to-talk.db/from-wei snt-amount)
:message message
:enabled? (tribute-to-talk.db/enabled? settings)
:error error
:step step
:state (or state (if snt-amount :completed :disabled))
:editing? editing?
:fiat-value (str "~" fiat-value " " (:code currency))}
(= step :set-snt-amount)
(assoc :snt-amount (str screen-snt-amount)
:disable-button?
(boolean (and (= step :set-snt-amount)
(or (string/blank? screen-snt-amount)
(#{"0" "0.0" "0.00"} screen-snt-amount)
(string/ends-with? screen-snt-amount ".")))))))))

View File

@ -1,13 +1,13 @@
(ns ^{:doc "API for whisper filters"}
status-im.transport.filters
(:require [re-frame.core :as re-frame]
[status-im.chat.models :as chat]
[status-im.contact.core :as contact]
[status-im.mailserver.core :as mailserver]
[status-im.transport.utils :as utils]
[status-im.utils.fx :as fx]
[status-im.utils.handlers :as handlers]
[status-im.transport.partitioned-topic :as transport.topic]
[taoensso.timbre :as log]
[status-im.contact.core :as contact]))
[taoensso.timbre :as log]))
(defn- receive-message [chat-id js-error js-message]
(re-frame/dispatch [:transport/messages-received js-error js-message chat-id]))
@ -97,7 +97,7 @@
(contact/add-contact public-key)
:open-chat
(contact/open-chat public-key)))]
(chat/start-chat public-key {:navigation-reset? true})))]
filters-fx-fns
[(mailserver/process-next-messages-request)])))))

View File

@ -4,8 +4,9 @@
[status-im.accounts.db :as accounts.db]
[status-im.chat.core :as chat]
[status-im.transport.db :as transport.db]
[status-im.transport.utils :as transport.utils]
[status-im.transport.partitioned-topic :as transport.topic]
[status-im.transport.utils :as transport.utils]
[status-im.tribute-to-talk.whitelist :as whitelist]
[status-im.utils.config :as config]
[status-im.utils.fx :as fx]
[taoensso.timbre :as log]))
@ -114,19 +115,24 @@
(send-direct-message current-public-key nil this)
(send-with-pubkey params)))))
(receive [this chat-id signature timestamp cofx]
{:chat-received-message/add-fx
[(assoc (into {} this)
:old-message-id (transport.utils/old-message-id this)
:message-id (transport.utils/message-id
signature
(.-payload (:js-obj cofx)))
:chat-id chat-id
:whisper-timestamp timestamp
:raw-payload-hash (transport.utils/sha3
(.-payload (:js-obj cofx)))
:from signature
:dedup-id (:dedup-id cofx)
:js-obj (:js-obj cofx))]})
(let [received-message-fx {:chat-received-message/add-fx
[(assoc (into {} this)
:old-message-id (transport.utils/old-message-id this)
:message-id (transport.utils/message-id
signature
(.-payload (:js-obj cofx)))
:chat-id chat-id
:whisper-timestamp timestamp
:raw-payload-hash (transport.utils/sha3
(.-payload (:js-obj cofx)))
:from signature
:dedup-id (:dedup-id cofx)
:js-obj (:js-obj cofx))]}]
(whitelist/filter-message cofx
received-message-fx
message-type
(get-in this [:content :tribute-transaction])
signature)))
(validate [this]
(if (spec/valid? :message/message this)
this

View File

@ -3,18 +3,33 @@
(:require [clojure.string :as string]
[re-frame.core :as re-frame]
[status-im.accounts.update.core :as accounts.update]
[status-im.contact.core :as contact]
[status-im.contact.db :as contact.db]
[status-im.ethereum.contracts :as contracts]
[status-im.ethereum.core :as ethereum]
[status-im.ethereum.json-rpc :as json-rpc]
[status-im.ipfs.core :as ipfs]
[status-im.ethereum.tokens :as tokens]
[status-im.ethereum.transactions.core :as transactions]
[status-im.tribute-to-talk.db :as tribute-to-talk.db]
[status-im.tribute-to-talk.whitelist :as whitelist]
[status-im.ui.screens.navigation :as navigation]
[status-im.utils.contenthash :as contenthash]
[status-im.utils.fx :as fx]
[status-im.utils.money :as money]
[status-im.wallet.core :as wallet]
[status-im.wallet.db :as wallet.db]
[taoensso.timbre :as log]))
(defn add-transaction-hash
[message db]
(let [to (get-in message [:content :chat-id])
tribute-transaction-hash
(get-in db [:contacts/contacts to :tribute-to-talk :transaction-hash])]
(if tribute-transaction-hash
(assoc-in message
[:content :tribute-transaction]
tribute-transaction-hash)
message)))
(fx/defn update-settings
[{:keys [db] :as cofx} {:keys [snt-amount message update] :as new-settings}]
(let [account-settings (get-in db [:account/account :settings])
@ -30,12 +45,13 @@
(and (contains? new-settings :update)
(nil? update))
(dissoc :update))]
(accounts.update/update-settings
cofx
(-> account-settings
(assoc-in [:tribute-to-talk chain-keyword]
tribute-to-talk-settings))
{})))
(fx/merge cofx
(accounts.update/update-settings
(-> account-settings
(assoc-in [:tribute-to-talk chain-keyword]
tribute-to-talk-settings))
{})
(whitelist/enable-whitelist))))
(fx/defn mark-ttt-as-seen
[{:keys [db] :as cofx}]
@ -43,6 +59,7 @@
(update-settings cofx {:seen? true})))
(fx/defn open-settings
{:events [:tribute-to-talk.ui/menu-item-pressed]}
[{:keys [db] :as cofx}]
(let [settings (tribute-to-talk.db/get-settings db)
updated-settings (:update settings)]
@ -59,25 +76,53 @@
(:snt-amount settings)
(merge {:step :edit
:editing? true}
(update settings :snt-amount tribute-to-talk.db/from-wei))
(update settings :snt-amount tribute-to-talk.db/from-wei))
:else
{:step :intro})))))
{:step :intro
:snt-amount "0"})))))
(fx/defn set-step
[{:keys [db]} step]
{:db (assoc-in db [:navigation/screen-params :tribute-to-talk :step] step)})
(fx/defn set-step-finish
(fx/defn set-tribute-signing-flow
[{:keys [db] :as cofx} tribute]
(if-let [contract (contracts/get-address db :status/tribute-to-talk)]
(wallet/eth-transaction-call
cofx
{:contract contract
:method "setTribute(uint256)"
:params [tribute]
:on-result [:tribute-to-talk.callback/set-tribute-transaction-sent]
:on-error [:tribute-to-talk.callback/set-tribute-transaction-failed]})
{:db (assoc-in db
[:navigation/screen-params :tribute-to-talk :state]
:transaction-failed)}))
(fx/defn remove
{:events [:tribute-to-talk.ui/remove-pressed]}
[{:keys [db] :as cofx}]
(fx/merge cofx
{:db (assoc-in db [:navigation/screen-params :tribute-to-talk :state] :signing)}
(set-step :finish)))
{:db (assoc-in db [:navigation/screen-params :tribute-to-talk]
{:step :finish
:state :disabled})}
(set-tribute-signing-flow 0)))
(fx/defn set-step-finish
[{:keys [db] :as cofx}]
(let [tribute (get-in db [:navigation/screen-params :tribute-to-talk :snt-amount])]
(fx/merge cofx
{:db (assoc-in db [:navigation/screen-params :tribute-to-talk :state] :signing)}
(set-tribute-signing-flow (tribute-to-talk.db/to-wei tribute))
(set-step :finish))))
(fx/defn open-learn-more
{:events [:tribute-to-talk.ui/learn-more-pressed]}
[cofx]
(set-step cofx :learn-more))
(fx/defn step-back
{:events [:tribute-to-talk.ui/step-back-pressed]}
[cofx]
(let [{:keys [step editing?]}
(get-in cofx [:db :navigation/screen-params :tribute-to-talk])]
@ -90,31 +135,11 @@
:edit
:intro))
:personalized-message
(set-step cofx :set-snt-amount)
:finish
(set-step cofx :personalized-message))))
(fx/defn upload-manifest
[cofx]
(let [{:keys [message snt-amount]}
(get-in cofx [:db :navigation/screen-params :tribute-to-talk])
manifest {:tribute-to-talk
{:message message
:snt-amount (tribute-to-talk.db/to-wei snt-amount)}}]
(ipfs/add cofx
{:value (js/JSON.stringify
(clj->js manifest))
:on-success
(fn [response]
[:tribute-to-talk.callback/manifest-uploaded
(:hash response)])
:on-failure
(fn [error]
[:tribute-to-talk.callback/manifest-upload-failed error])})))
(set-step cofx :set-snt-amount))))
(fx/defn step-forward
{:events [:tribute-to-talk.ui/step-forward-pressed]}
[cofx]
(let [{:keys [step editing?]}
(get-in cofx [:db :navigation/screen-params :tribute-to-talk])]
@ -123,12 +148,7 @@
(set-step cofx :set-snt-amount)
:set-snt-amount
(set-step cofx :personalized-message)
:personalized-message
(fx/merge cofx
(set-step-finish)
(upload-manifest))
(set-step-finish cofx)
:finish
(navigation/navigate-back cofx))))
@ -154,161 +174,219 @@
(and (string/includes? snt-amount ".")
(> (count (second (string/split snt-amount #"\."))) 1))
snt-amount
;; Disallow values larger or equal to 1 million
(>= (js/parseFloat (str snt-amount numpad-symbol))
tribute-to-talk.db/max-snt-amount)
snt-amount
;; Replace initial "0" by the first digit
(and (= snt-amount "0") (not= numpad-symbol "."))
(str numpad-symbol)
:else (str snt-amount numpad-symbol)))))
(fx/defn update-snt-amount
{:events [:tribute-to-talk.ui/numpad-key-pressed]}
[{:keys [db]} numpad-symbol]
{:db (update-in db
[:navigation/screen-params :tribute-to-talk :snt-amount]
#(get-new-snt-amount % numpad-symbol))})
(fx/defn update-message
[{:keys [db]} message]
{:db (assoc-in db
[:navigation/screen-params :tribute-to-talk :message]
message)})
(fx/defn start-editing
{:events [:tribute-to-talk.ui/edit-pressed]}
[{:keys [db]}]
{:db (assoc-in db
[:navigation/screen-params :tribute-to-talk :step]
:set-snt-amount)})
(fx/defn fetch-manifest
[{:keys [db] :as cofx} identity contenthash]
(contenthash/cat cofx
{:contenthash contenthash
:on-failure
(fn [error]
(re-frame/dispatch
(if (= 503 error)
[:tribute-to-talk.callback/fetch-manifest-failure
identity contenthash]
[:tribute-to-talk.callback/no-manifest-found identity])))
:on-success
(fn [manifest-json]
(let [manifest (js->clj (js/JSON.parse manifest-json)
:keywordize-keys true)]
(re-frame/dispatch
[:tribute-to-talk.callback/fetch-manifest-success
identity manifest])))}))
(fx/defn on-check-tribute-success
{:events [:tribute-to-talk.callback/check-tribute-success]}
[cofx public-key tribute-to-talk]
(let [tribute-to-talk (when (tribute-to-talk.db/valid? tribute-to-talk)
tribute-to-talk)]
(if-let [me? (= public-key
(get-in cofx [:db :account/account :public-key]))]
(update-settings cofx tribute-to-talk)
(contact/set-tribute cofx public-key tribute-to-talk))))
(fx/defn on-no-tribute-found
{:events [:tribute-to-talk.callback/no-tribute-found]}
[cofx public-key]
(if-let [me? (= public-key
(get-in cofx [:db :account/account :public-key]))]
(update-settings cofx nil)
(contact/set-tribute cofx public-key nil)))
(re-frame/reg-fx
:tribute-to-talk/get-manifest
:tribute-to-talk/get-tribute
(fn [{:keys [contract address on-success]}]
(json-rpc/eth-call
{:contract contract
:method "getManifest(address)"
:method "getTribute(address)"
:params [address]
:outputs ["bytes"]
:outputs ["uint256"]
:on-success on-success})))
(fx/defn check-manifest
(fx/defn check-tribute
[{:keys [db] :as cofx} public-key]
(if-let [contract (contracts/get-address db :status/tribute-to-talk)]
(let [address (contact.db/public-key->address public-key)]
{:tribute-to-talk/get-manifest
{:contract contract
:address address
:on-success
(fn [[contenthash]]
(re-frame/dispatch
(if contenthash
[:tribute-to-talk.callback/check-manifest-success
public-key
contenthash]
[:tribute-to-talk.callback/no-manifest-found public-key])))}})
;; update settings if checking own manifest or do nothing otherwise
(when-let [me? (= identity
(get-in cofx [:db :account/account :public-key]))]
(update-settings cofx nil))))
(fx/defn check-own-manifest
[cofx]
(check-manifest cofx (get-in cofx [:db :account/account :public-key])))
(fx/defn set-manifest-signing-flow
[{:keys [db] :as cofx} hash]
(let [contenthash (when hash
(contenthash/encode {:hash hash
:namespace :ipfs}))]
(when (and (not (get-in db [:chats public-key :group-chat]))
(not (get-in db [:contacts/contacts public-key :tribute-to-talk
:transaction-hash]))
(not (whitelist/whitelisted?
(get-in db [:contacts/contacts public-key]))))
(if-let [contract (contracts/get-address db :status/tribute-to-talk)]
(wallet/eth-transaction-call
cofx
{:contract contract
:method "setManifest(bytes)"
:params [contenthash]
:on-result [:tribute-to-talk.callback/set-manifest-transaction-completed]
:on-error [:tribute-to-talk.callback/set-manifest-transaction-failed]})
{:db (assoc-in db
[:navigation/screen-params :tribute-to-talk :state]
:transaction-failed)})))
(let [address (ethereum/public-key->address public-key)]
{:tribute-to-talk/get-tribute
{:contract contract
:address address
:on-success
(fn [[tribute]]
(re-frame/dispatch
(if (pos? tribute)
[:tribute-to-talk.callback/check-tribute-success
public-key
{:snt-amount (str tribute)}]
[:tribute-to-talk.callback/no-tribute-found public-key])))}})
;; update settings if checking own manifest or do nothing otherwise
(if-let [me? (= public-key
(get-in cofx [:db :account/account :public-key]))]
(fx/defn remove
[{:keys [db] :as cofx}]
(fx/merge cofx
{:db (assoc-in db
[:navigation/screen-params :tribute-to-talk :unavailable?]
true)}
(update-settings nil))
(contact/set-tribute cofx public-key nil)))))
(fx/defn check-own-tribute
[cofx]
(check-tribute cofx (get-in cofx [:db :account/account :public-key])))
(fx/defn pay-tribute
{:events [:tribute-to-talk.ui/on-pay-to-chat-pressed]}
[{:keys [db] :as cofx} public-key]
(let [{:keys [name address public-key tribute-to-talk] :as recipient-contact}
(get-in db [:contacts/contacts public-key])
{:keys [snt-amount]} tribute-to-talk
symbol (ethereum/snt-symbol db)
wallet-balance (get-in db [:wallet :balance symbol]
(money/bignumber 0))
amount-text (str (tribute-to-talk.db/from-wei snt-amount))]
(wallet/eth-transaction-call
cofx
{:contract (contracts/get-address db :status/snt)
:method "transfer(address,uint256)"
:params [address snt-amount]
:details {:to-name name
:public-key public-key
:from-chat? true
:asset symbol
:amount-text amount-text
:sufficient-funds?
(money/sufficient-funds? snt-amount wallet-balance)
:send-transaction-message? true}
:on-result [:tribute-to-talk.callback/pay-tribute-transaction-sent
public-key]})))
(defn tribute-transaction-trigger
[db {:keys [block error?]}]
(let [current-block (get db :ethereum/current-block)
transaction-block (or block
current-block)]
(or error?
(pos? (- current-block
(js/parseInt transaction-block))))))
(fx/defn on-pay-tribute-transaction-triggered
[{:keys [db] :as cofx}
public-key
{:keys [error? transfer symbol] :as transaction}]
(when (and transfer
(= symbol (ethereum/snt-symbol db))
(not error?))
(whitelist/mark-tribute-paid cofx public-key)))
(fx/defn on-pay-tribute-transaction-sent
{:events [:tribute-to-talk.callback/pay-tribute-transaction-sent]}
[{:keys [db] :as cofx} public-key id transaction-hash method]
(fx/merge cofx
{:db (assoc-in db [:navigation/screen-params :tribute-to-talk]
{:step :finish
:state :disabled})}
(set-manifest-signing-flow nil)))
{:db (assoc-in db [:contacts/contacts public-key
:tribute-to-talk :transaction-hash]
transaction-hash)}
(navigation/navigate-to-clean :wallet-transaction-sent-modal {})
(transactions/watch-transaction
transaction-hash
{:trigger-fn
tribute-transaction-trigger
:on-trigger
#(on-pay-tribute-transaction-triggered public-key %)})))
(fx/defn check-set-manifest-transaction
[{:keys [db] :as cofx}]
(let [transaction (get-in (tribute-to-talk.db/get-settings db) [:update :transaction])]
(when transaction
(let [confirmed? (pos? (js/parseInt
(get-in cofx [:db :wallet :transactions
transaction :confirmations]
0)))
;;TODO support failed transactions
failed? false]
(cond
failed?
(fx/merge cofx
{:db (assoc-in db [:navigation/screen-params :tribute-to-talk :state] :transaction-failed)}
(update-settings {:update nil}))
(fx/defn on-set-tribute-transaction-triggered
[{:keys [db] :as cofx}
tribute
{:keys [error?] :as transaction}]
(if error?
(fx/merge cofx
{:db (assoc-in db [:navigation/screen-params
:tribute-to-talk :state]
:transaction-failed)}
(update-settings {:update nil}))
(fx/merge cofx
{:db (assoc-in db [:navigation/screen-params
:tribute-to-talk :state]
(if tribute
:completed
:disabled))}
(check-own-tribute)
(update-settings {:update nil}))))
confirmed?
(fx/merge cofx
{:db (assoc-in db [:navigation/screen-params :tribute-to-talk :state] :completed)}
check-own-manifest
(update-settings {:update nil}))
(not confirmed?)
{:dispatch-later [{:ms 10000
:dispatch [:tribute-to-talk/check-set-manifest-transaction-timeout]}]})))))
(fx/defn on-set-manifest-transaction-completed
[{:keys [db] :as cofx} transaction-hash]
(fx/defn on-set-tribute-transaction-sent
{:events [:tribute-to-talk.callback/set-tribute-transaction-sent]}
[{:keys [db] :as cofx} id transaction-hash method]
(let [{:keys [snt-amount message]} (get-in db [:navigation/screen-params
:tribute-to-talk])]
(fx/merge cofx
{:db (assoc-in db [:navigation/screen-params :tribute-to-talk :state] :pending)}
{:db (assoc-in db [:navigation/screen-params
:tribute-to-talk :state]
:pending)}
(navigation/navigate-to-clean :wallet-transaction-sent-modal {})
(update-settings {:update {:transaction transaction-hash
:snt-amount snt-amount
:message message}})
check-set-manifest-transaction)))
(transactions/watch-transaction
transaction-hash
{:trigger-fn
tribute-transaction-trigger
:on-trigger
#(on-set-tribute-transaction-triggered snt-amount %)}))))
(fx/defn on-set-manifest-transaction-failed
(fx/defn on-set-tribute-transaction-failed
{:events [:tribute-to-talk.callback/set-tribute-transaction-failed]}
[{:keys [db] :as cofx} error]
(log/error :set-manifest-transaction-failed error)
(log/error :set-tribute-transaction-failed error)
{:db (assoc-in db
[:navigation/screen-params :tribute-to-talk :state]
:transaction-failed)})
(fx/defn on-manifest-upload-failed
[{:keys [db] :as cofx} error]
(log/error :upload-manifest-failed error)
{:db (assoc-in db
[:navigation/screen-params :tribute-to-talk :state]
:transaction-failed)})
(fx/defn watch-set-tribute-transaction
"check if there is a pending transaction to set the tribute and
add a watch on that transaction
if there is a transaction check if the trigger is valid already"
[{:keys [db] :as cofx}]
(when-let [tribute-update (get (tribute-to-talk.db/get-settings db)
:update)]
(let [{:keys [transaction snt-amount]} tribute-update]
(fx/merge cofx
(transactions/watch-transaction
transaction
{:trigger-fn
tribute-transaction-trigger
:on-trigger
#(on-set-tribute-transaction-triggered snt-amount %)})
(when-let [transaction (get-in db [:wallet :transactions
transaction])]
(transactions/check-transaction transaction))))))
(fx/defn init
[cofx]
(fx/merge cofx
check-own-manifest
check-set-manifest-transaction))
(check-own-tribute)
(watch-set-tribute-transaction)))

View File

@ -1,6 +1,14 @@
(ns status-im.tribute-to-talk.db
(:require [status-im.ethereum.core :as ethereum]
[status-im.js-dependencies :as dependencies]))
[status-im.i18n :as i18n]
[status-im.js-dependencies :as dependencies]
[status-im.utils.money :as money]))
(defn tribute-received?
[contact]
(contains? (:system-tags contact) :tribute-to-talk/received))
(def max-snt-amount 1000000)
(defn utils [] (dependencies/web3-utils))
@ -14,11 +22,52 @@
(when s
(.fromWei (utils) s)))
(defn tribute-status
[{:keys [system-tags tribute-to-talk] :as contact}]
(let [{:keys [snt-amount transaction-hash]} tribute-to-talk]
(cond (contains? system-tags :tribute-to-talk/paid) :paid
(not (nil? transaction-hash)) :pending
(pos? snt-amount) :required
:else :none)))
(defn status-label
[tribute-status tribute]
(case tribute-status
:paid (i18n/label :t/tribute-state-paid)
:pending (i18n/label :t/tribute-state-pending)
:required (i18n/label :t/tribute-state-required
{:snt-amount (from-wei tribute)})
nil))
(defn valid?
[{:keys [snt-amount]}]
(when (string? snt-amount)
(try (let [converted-snt-amount (from-wei snt-amount)]
(and (= (to-wei converted-snt-amount)
snt-amount)
(< 0 (js/parseFloat converted-snt-amount) max-snt-amount)))
(catch :default err nil))))
(defn get-settings
[db]
(let [chain-keyword (ethereum/chain-keyword db)]
(get-in db [:account/account :settings :tribute-to-talk chain-keyword])))
(defn enabled?
[db]
(:snt-amount (get-settings db)))
[settings]
(:snt-amount settings))
(defn valid-tribute-transaction?
[db tribute-required tribute-transaction from-public-key]
(let [{:keys [value block from] :as transaction}
(get-in db [:wallet :transactions tribute-transaction])
current-block (get db :ethereum/current-block)
transaction-block (or block
current-block)]
(and transaction
(pos? (- current-block
(js/parseInt transaction-block)))
(.lessThanOrEqualTo (money/bignumber tribute-required)
(money/bignumber value))
(ethereum/address= (ethereum/public-key->address from-public-key)
from))))

View File

@ -1,54 +0,0 @@
(ns status-im.tribute-to-talk.subs
(:require [clojure.string :as string]
[re-frame.core :as re-frame]
[status-im.tribute-to-talk.db :as tribute-to-talk]
[status-im.utils.money :as money]))
(re-frame/reg-sub
:tribute-to-talk/settings
(fn [db]
(tribute-to-talk/get-settings db)))
(re-frame/reg-sub
:tribute-to-talk/screen-params
(fn [db]
(get-in db [:navigation/screen-params :tribute-to-talk])))
(re-frame/reg-sub
:tribute-to-talk/ui
:<- [:tribute-to-talk/settings]
:<- [:tribute-to-talk/screen-params]
:<- [:prices]
:<- [:wallet/currency]
(fn [[{:keys [seen? snt-amount message update]}
{:keys [step editing? state error]
:or {step :intro}
screen-snt-amount :snt-amount
screen-message :message} prices currency]]
(let [fiat-value (if snt-amount
(money/fiat-amount-value
snt-amount
:SNT
(-> currency :code keyword)
prices)
"0")]
(cond-> {:seen? seen?
:snt-amount (tribute-to-talk/from-wei snt-amount)
:message message
:disabled? (nil? snt-amount)
:error error
:step step
:state (or state (if snt-amount :completed :disabled))
:editing? editing?
:fiat-value (str "~" fiat-value " " (:code currency))}
(= step :set-snt-amount)
(assoc :snt-amount (str screen-snt-amount)
:disable-button?
(boolean (and (= step :set-snt-amount)
(or (string/blank? screen-snt-amount)
(#{"0" "0.0" "0.00"} screen-snt-amount)
(string/ends-with? screen-snt-amount ".")))))
(= step :personalized-message)
(assoc :message screen-message)))))

View File

@ -0,0 +1,78 @@
(ns status-im.tribute-to-talk.whitelist
(:require [status-im.contact.db :as contact.db]
[status-im.data-store.contacts :as contacts-store]
[status-im.tribute-to-talk.db :as tribute-to-talk.db]
[status-im.utils.fx :as fx]))
(defn whitelisted-by? [{:keys [system-tags]}]
(or (contains? system-tags :contact/request-received)
(contains? system-tags :tribute-to-talk/paid)
(contains? system-tags :tribute-to-talk/received)))
(defn whitelisted? [{:keys [system-tags]}]
(or (contains? system-tags :contact/added)
(contains? system-tags :tribute-to-talk/paid)
(contains? system-tags :tribute-to-talk/received)))
(defn get-contact-whitelist
[contacts]
(reduce (fn [acc {:keys [public-key] :as contact}]
(if (whitelisted? contact)
(conj acc public-key) acc))
(hash-set) contacts))
(fx/defn add-to-whitelist
"Add contact to whitelist"
[{:keys [db]} public-key]
{:db (update db :contacts/whitelist (fnil conj #{}) public-key)})
(defn- mark-tribute
[{:keys [db now] :as cofx} public-key tag]
(let [contact (-> (contact.db/public-key->contact
(:contacts/contacts db)
public-key)
(assoc :last-updated now)
(update :system-tags conj tag))]
(fx/merge cofx
{:db (-> db
(assoc-in [:contacts/contacts public-key] contact))
:data-store/tx [(contacts-store/save-contact-tx contact)]}
(add-to-whitelist public-key))))
(fx/defn mark-tribute-paid
[cofx public-key]
(mark-tribute cofx public-key :tribute-to-talk/paid))
(fx/defn mark-tribute-received
[cofx public-key]
(mark-tribute cofx public-key :tribute-to-talk/received))
(fx/defn enable-whitelist
[{:keys [db] :as cofx}]
(if (tribute-to-talk.db/enabled? (tribute-to-talk.db/get-settings db))
{:db (assoc db :contacts/whitelist
(get-contact-whitelist (vals (:contacts/contacts db))))}
{:db (dissoc db :contacts/whitelist)}))
(fx/defn filter-message
"clojure semantics of filter, if true the message is allowed
if it is a user message and tribute to talk is enabled, the user must be
in the whitelist or there must be a valid tribute transaction id passed
along the message"
[{:keys [db] :as cofx} received-message-fx message-type tribute-transaction from]
;; if it is not a user-message or the user is whitelisted it passes
(if (or (not= :user-message message-type)
(contains? (:contacts/whitelist db) from))
received-message-fx
;; if ttt is disabled it passes
(if-let [snt-amount (:snt-amount (tribute-to-talk.db/get-settings db))]
(let [contact (get-in db [:contacts/contacts from])]
;; if the tribute is not paid the message is dropped
;; otherwise it passes and the user is added to the whitelist
;; through system tags
(when (tribute-to-talk.db/valid-tribute-transaction?
db snt-amount tribute-transaction from)
(fx/merge cofx
received-message-fx
(mark-tribute-received from))))
received-message-fx)))

View File

@ -96,6 +96,18 @@
(when dapp?
[dapp-badge styles])])
(defn contact-icon-view-chat [contact]
[contact-icon-view contact
{:container styles/container-chat-list
:online-view-wrapper styles/online-view-wrapper
:online-view styles/online-view
:online-dot-left styles/online-dot-left
:online-dot-right styles/online-dot-right
:size 60
:chat-icon styles/chat-icon-chat-list
:default-chat-icon (styles/default-chat-icon-chat-list colors/default-chat-color)
:default-chat-icon-text styles/default-chat-icon-text}])
(defn contact-icon-contacts-tab [contact]
[contact-icon-view contact
{:container styles/container-chat-list

View File

@ -3,7 +3,7 @@
[status-im.react-native.js-dependencies :as rn-dependencies]
[status-im.ui.components.qr-code-viewer.styles :as styles]
[status-im.ui.components.react :as react]
[status-im.ui.screens.profile.tribute-to-talk.views :as tr-to-talk])
[status-im.ui.screens.profile.tribute-to-talk.views :as tribute-to-talk])
(:require-macros [status-im.utils.views :refer [defview letsubs]]))
(defn qr-code [props]
@ -11,16 +11,17 @@
(rn-dependencies/qr-code)
(clj->js (merge {:inverted true} props))))
(defview qr-code-viewer-component [{:keys [style hint-style footer-style footer-button value hint legend]}]
(letsubs [{:keys [width]} [:dimensions/window]
{:keys [disabled?]} [:tribute-to-talk/ui]]
[(react/scroll-view) {:content-container-style {:align-items :center
:margin-top 16
:justify-content :center}
:style (merge {:flex 1} style)}
(when-not disabled?
(defview qr-code-viewer-component
[{:keys [style hint-style footer-style footer-button value hint legend
show-tribute-to-talk-warning?]}]
(letsubs [{:keys [width]} [:dimensions/window]]
[(react/scroll-view) {:content-container-style {:align-items :center
:margin-top 16
:justify-content :center}
:style (merge {:flex 1} style)}
(when show-tribute-to-talk-warning?
[react/view {:style {:margin-horizontal 16}}
[tr-to-talk/enabled-note]])
[tribute-to-talk/enabled-note]])
(when width
(let [size (int (min width styles/qr-code-max-width))]
[react/view {:style (styles/qr-code-container size)
@ -40,8 +41,9 @@
:margin-bottom 16}}
[footer-button value]])]))
(defn qr-code-viewer [{:keys [style hint-style footer-style footer-button value hint legend]
:as params}]
(defn qr-code-viewer
[{:keys [style hint-style footer-style footer-button value hint legend]
:as params}]
(if value
[qr-code-viewer-component params]
[react/view [react/text "no value"]]))

View File

@ -21,13 +21,12 @@
(views/defview gap
[{:keys [gaps first-gap?]} idx list-ref]
(views/letsubs [in-progress? [:chats/fetching-gap-in-progress?
(views/letsubs [{:keys [range intro-status]} [:chats/current-chat]
in-progress? [:chats/fetching-gap-in-progress?
(if first-gap?
[:first-gap]
(:ids gaps))]
connected? [:mailserver/connected?]
range [:chats/range]
intro-status [:chats/current-chat-intro-status]]
connected? [:mailserver/connected?]]
(let [ids (:ids gaps)
intro-loading? (= intro-status :loading)]
(when-not (and first-gap? intro-loading?)
@ -52,4 +51,3 @@
"\n"
(i18n/label :t/load-messages-before
{:date date})))])])]]]))))

View File

@ -1,10 +1,10 @@
(ns status-im.ui.screens.chat.photos
(:require-macros [status-im.utils.views :refer [defview letsubs]])
(:require [status-im.ui.components.react :as react]
(:require [clojure.string :as string]
[status-im.ui.components.react :as react]
[status-im.ui.screens.chat.styles.photos :as style]
[status-im.utils.identicon :as identicon]
[clojure.string :as string]
[status-im.utils.image :as utils.image]))
[status-im.utils.image :as utils.image])
(:require-macros [status-im.utils.views :refer [defview letsubs]]))
(defn photo [photo-path {:keys [size
accessibility-label]}]
@ -14,10 +14,10 @@
:accessibility-label (or accessibility-label :chat-icon)}]
[react/view {:style (style/photo-border size)}]])
(defview member-photo [from]
(defview member-photo [from & [size]]
(letsubs [photo-path [:chats/photo-path from]]
(photo (if (string/blank? photo-path)
(identicon/identicon from)
photo-path)
{:accessibility-label :member-photo
:size style/default-size})))
:size (or size style/default-size)})))

View File

@ -1,7 +1,5 @@
(ns status-im.ui.screens.chat.styles.main
(:require-macros [status-im.utils.styles :refer [defstyle defnstyle]])
(:require [status-im.ui.components.styles :as component.styles]
[status-im.ui.components.colors :as colors]))
(:require [status-im.ui.components.colors :as colors]))
(def scroll-root
{:flex 1})
@ -193,12 +191,27 @@
(def empty-chat-container
{:flex 1
:flex-direction :column
:justify-content :center
:align-items :center
:padding-vertical 50
:margin-right 6})
#_(defn intro-header-container
[height status no-messages]
(let [adjusted-height (if (< height 280) 324 height)]
(if (or no-messages (= status (or :loading :empty)))
{:flex 1
:flex-direction :column
:justify-content :center
:align-items :center
:height adjusted-height
:padding-horizontal 32}
{:flex 1
:flex-direction :column
:justify-content :center
:align-items :center
:padding-horizontal 32})))
(defn intro-header-container
[height status no-messages]
(let [adjusted-height (if (< height 280) 324 height)]
@ -207,13 +220,11 @@
:flex-direction :column
:justify-content :center
:align-items :center
:height adjusted-height
:padding-horizontal 32}
:height adjusted-height}
{:flex 1
:flex-direction :column
:justify-content :center
:align-items :center
:padding-horizontal 32})))
:align-items :center})))
(defn intro-header-icon [diameter color]
{:width diameter
@ -231,22 +242,36 @@
:line-height 72})
(def intro-header-chat-name
{:font-size 22
:font-weight "700"
:line-height 28
:text-align :center
:margin-bottom 8
:color colors/black})
{:font-size 22
:font-weight "700"
:line-height 28
:text-align :center
:margin-bottom 8
:margin-horizontal 32
:color colors/black})
(def intro-header-description-container
{:flex-wrap :wrap
:align-items :flex-start
:flex-direction :row})
{:flex-wrap :wrap
:align-items :flex-start
:flex-direction :row
:margin-horizontal 32})
(def loading-text
{:color colors/gray
:font-size 15
:line-height 22
:letter-spacing -0.2
:margin-right 4
:text-align :center})
(def empty-chat-text-name
{:margin-bottom 5})
(def intro-header-description
{:color colors/gray
:line-height 22
:text-align :center})
{:color colors/gray
:line-height 22
:text-align :center
:margin-horizontal 32})
(def group-chat-icon
{:color colors/white
@ -272,4 +297,33 @@
{:color colors/blue
:margin-bottom 40})
(def messages-list-vertical-padding 46)
(def messages-list-vertical-padding 46)
(def are-you-friends-bubble
{:border-radius 8
:border-width 1
:margin-top 4
:border-color colors/gray-lighter
:align-self :flex-start
:padding-vertical 12
:margin-horizontal 8
:padding-horizontal 16
:margin-bottom 50})
(def are-you-friends-text
{:line-height 22
:text-align :center
:font-size 15
:color colors/gray})
(def share-my-profile
{:color colors/blue
:text-align :center
:margin-top 11
:line-height 22
:font-size 15})
(def tribute-received-note
{:font-size 13
:line-height 18
:text-align :center})

View File

@ -1,10 +1,9 @@
(ns status-im.ui.screens.chat.views
(:require [re-frame.core :as re-frame]
[reagent.core :as reagent]
[status-im.chat.models :as models.chat]
[status-im.contact.db :as contact.db]
[status-im.group-chats.db :as group-chats.db]
[status-im.i18n :as i18n]
[status-im.tribute-to-talk.core :as tribute-to-talk]
[status-im.ui.components.animation :as animation]
[status-im.ui.components.button.view :as buttons]
[status-im.ui.components.chat-icon.screen :as chat-icon.screen]
@ -21,19 +20,20 @@
[status-im.ui.screens.chat.bottom-info :as bottom-info]
[status-im.ui.screens.chat.input.input :as input]
[status-im.ui.screens.chat.message.datemark :as message-datemark]
[status-im.ui.screens.chat.message.gap :as gap]
[status-im.ui.screens.chat.message.message :as message]
[status-im.ui.screens.chat.message.options :as message-options]
[status-im.ui.screens.chat.stickers.views :as stickers]
[status-im.ui.screens.chat.styles.main :as style]
[status-im.ui.screens.chat.toolbar-content :as toolbar-content]
[status-im.utils.platform :as platform]
[status-im.utils.utils :as utils]
[status-im.utils.datetime :as datetime]
[status-im.ui.screens.chat.message.gap :as gap]
[reagent.core :as reagent])
[status-im.ui.screens.profile.tribute-to-talk.views
:as
tribute-to-talk.views]
[status-im.utils.platform :as platform])
(:require-macros [status-im.utils.views :refer [defview letsubs]]))
(defn add-contact-bar [public-key]
(defn add-contact-bar
[public-key]
[react/touchable-highlight
{:on-press
#(re-frame/dispatch [:contact.ui/add-to-contact-pressed public-key])
@ -44,11 +44,13 @@
{:color colors/blue}]
[react/i18n-text {:style style/add-contact-text :key :add-to-contacts}]]])
(defn- on-options [chat-id chat-name group-chat? public?]
(defn- on-options
[chat-id chat-name group-chat? public?]
(list-selection/show {:title chat-name
:options (actions/actions group-chat? chat-id public?)}))
(defn chat-toolbar [{:keys [chat-name group-chat chat-id contact]} public? modal?]
(defn chat-toolbar
[{:keys [chat-name group-chat chat-id contact]} public? modal?]
[react/view
[status-bar/status-bar (when modal? {:type :modal-white})]
[toolbar/toolbar
@ -59,16 +61,18 @@
toolbar/nav-back-home)
[toolbar-content/toolbar-content-view]
(when-not modal?
[toolbar/actions [{:icon :main-icons/more
:icon-opts {:color :black
:accessibility-label :chat-menu-button}
:handler #(on-options chat-id chat-name group-chat public?)}]])]
[toolbar/actions
[{:icon :main-icons/more
:icon-opts {:color :black
:accessibility-label :chat-menu-button}
:handler #(on-options chat-id chat-name group-chat public?)}]])]
[connectivity/connectivity-view]
(when (and (not group-chat)
(not (contact.db/added? contact)))
[add-contact-bar chat-id])])
(defmulti message-row (fn [{{:keys [type]} :row}] type))
(defmulti message-row
(fn [{{:keys [type]} :row}] type))
(defmethod message-row :datemark
[{{:keys [value]} :row}]
@ -111,9 +115,25 @@
[(react/animated-view) {:style (style/message-view-animated opacity)}
message-view])]]))
(defn tribute-to-talk-header
[name]
[react/nested-text {:style (assoc style/intro-header-description
:margin-bottom 32)}
(i18n/label :t/tribute-required-by-account {:account-name name})
[{:style {:color colors/blue}
:on-press #(re-frame/dispatch [:navigate-to :tribute-learn-more])}
(str " " (i18n/label :learn-more))]])
(defn intro-header
[name]
[react/text {:style (assoc style/intro-header-description
:margin-bottom 32)}
(str (i18n/label :t/empty-chat-description-one-to-one) name)])
(defn join-chat-button [chat-id]
[buttons/secondary-button {:style style/join-button
:on-press #(re-frame/dispatch [:group-chats.ui/join-pressed chat-id])}
[buttons/secondary-button
{:style style/join-button
:on-press #(re-frame/dispatch [:group-chats.ui/join-pressed chat-id])}
(i18n/label :t/join-group-chat)])
(defn decline-chat [chat-id]
@ -135,7 +155,9 @@
[inviter-name {:keys [name group-chat color chat-id]}]
[react/view style/empty-chat-container
[react/view {:style {:margin-bottom 170}}
[chat-icon.screen/profile-icon-view nil name color false 100 {:default-chat-icon-text style/group-chat-icon}]]
[chat-icon.screen/profile-icon-view
nil name color false 100
{:default-chat-icon-text style/group-chat-icon}]]
[react/view {:style style/group-chat-join-footer}
[react/view {:style style/group-chat-join-container}
[react/view
@ -146,96 +168,175 @@
[join-chat-button chat-id]
[decline-chat chat-id]]]])
;; TODO refactor this big view into chunks
(defview chat-intro-header-container
(defn group-chat-description-loading
[]
[react/view {:style (merge style/intro-header-description-container
{:margin-bottom 36
:height 44})}
[react/text {:style style/intro-header-description}
(i18n/label :t/loading)]
[react/activity-indicator {:animating true
:size :small
:color colors/gray}]])
(defn group-chat-description-container
[{:keys [group-chat name pending-invite-inviter-name
inviter-name color chat-id chat-name public?
universal-link]} no-messages]
(letsubs [intro-status [:chats/current-chat-intro-status]
height [:chats/content-layout-height]
input-height [:chats/current-chat-ui-prop :input-height]
{:keys [:lowest-request-from :highest-request-to]} [:chats/range]
all-loaded? [:chats/all-loaded?]]
(let [icon-text (if public? chat-id name)
intro-name (if public? chat-name name)]
;; TODO This when check ought to be unnecessary but for now it prevents
;; jerky motion when fresh chat is created, when input-height can be null
;; affecting the calculation of content-layout-height to be briefly adjusted
(when (or input-height pending-invite-inviter-name)
[react/touchable-without-feedback
{:style {:flex 1
:align-items :flex-start}
:on-press (fn [_]
(re-frame/dispatch
[:chat.ui/set-chat-ui-props {:messages-focused? true
:show-stickers? false}])
(react/dismiss-keyboard!))}
[react/view
(style/intro-header-container height intro-status no-messages)
;; Icon section
[react/view {:style {:margin-top 42
:margin-bottom 24}}
[chat-icon.screen/chat-intro-icon-view
icon-text chat-id
{:default-chat-icon (style/intro-header-icon 120 color)
:default-chat-icon-text style/intro-header-icon-text
:size 120}]]
;; Chat title section
[react/text {:style style/intro-header-chat-name} intro-name]
;; Description section
(if group-chat
(cond
(= intro-status :loading)
[react/view {:style (merge style/intro-header-description-container
{:margin-bottom 36
:height 44})}
[react/text {:style style/intro-header-description}
(i18n/label :t/loading)]
[react/activity-indicator {:animating true
:size :small
:color colors/gray}]]
contact universal-link range intro-status] :as chat}]
(let [{:keys [lowest-request-from highest-request-to]} range]
(case intro-status
:loading
[group-chat-description-loading]
(= intro-status :empty)
(when public?
[react/nested-text {:style (merge style/intro-header-description
{:margin-bottom 36})}
(let [quiet-hours (quot (- highest-request-to lowest-request-from)
(* 60 60))
quiet-time (if (<= quiet-hours 24)
(i18n/label :t/quiet-hours
{:quiet-hours quiet-hours})
(i18n/label :t/quiet-days
{:quiet-days (quot quiet-hours 24)}))]
(i18n/label :t/empty-chat-description-public
{:quiet-hours quiet-time}))
[{:style {:color colors/blue}
:on-press #(list-selection/open-share
{:message
(i18n/label
:t/share-public-chat-text {:link universal-link})})}
(i18n/label :t/empty-chat-description-public-share-this)]])
:empty
(when public?
[react/nested-text {:style (merge style/intro-header-description
{:margin-bottom 36})}
(let [quiet-hours (quot (- highest-request-to lowest-request-from)
(* 60 60))
quiet-time (if (<= quiet-hours 24)
(i18n/label :t/quiet-hours
{:quiet-hours quiet-hours})
(i18n/label :t/quiet-days
{:quiet-days (quot quiet-hours 24)}))]
(i18n/label :t/empty-chat-description-public
{:quiet-hours quiet-time}))
[{:style {:color colors/blue}
:on-press #(list-selection/open-share
{:message
(i18n/label
:t/share-public-chat-text {:link universal-link})})}
(i18n/label :t/empty-chat-description-public-share-this)]])
(= intro-status :messages)
(when (not public?)
(if pending-invite-inviter-name
[react/nested-text {:style style/intro-header-description}
[{:style {:color :black}} pending-invite-inviter-name]
(i18n/label :t/join-group-chat-description
{:username ""
:group-name intro-name})]
(if (not= inviter-name "Unknown")
[react/nested-text {:style style/intro-header-description}
(i18n/label :t/joined-group-chat-description
{:username ""
:group-name intro-name})
[{:style {:color :black}} inviter-name]]
[react/text {:style style/intro-header-description}
(i18n/label :t/created-group-chat-description
{:group-name intro-name})]))))
[react/nested-text {:style (merge style/intro-header-description
{:margin-bottom 36})}
(i18n/label :t/empty-chat-description-one-to-one)
[{} intro-name]])]]))))
:messages
(when (not public?)
(if pending-invite-inviter-name
[react/nested-text {:style style/intro-header-description}
[{:style {:color :black}} pending-invite-inviter-name]
(i18n/label :t/join-group-chat-description
{:username ""
:group-name chat-name})]
(if (not= inviter-name "Unknown")
[react/nested-text {:style style/intro-header-description}
(i18n/label :t/joined-group-chat-description
{:username ""
:group-name chat-name})
[{:style {:color :black}} inviter-name]]
[react/text {:style style/intro-header-description}
(i18n/label :t/created-group-chat-description
{:group-name chat-name})]))))))
(defn pay-to-chat-messages
[snt-amount chat-id tribute-status tribute-label
fiat-amount fiat-currency token]
[tribute-to-talk.views/pay-to-chat-message
{:snt-amount snt-amount
:public-key chat-id
:tribute-status tribute-status
:tribute-label tribute-label
:fiat-amount fiat-amount
:fiat-currency fiat-currency
:token token
:style {:margin-horizontal 8
:align-items :flex-start
:align-self (if snt-amount :flex-start :flex-end)}}])
(defn one-to-one-chat-description-container
[{:keys [chat-id name contact show-input? tribute-to-talk]
:tribute-to-talk/keys [my-message received? message tribute-status
tribute-label snt-amount on-share-my-profile
fiat-amount fiat-currency token]}]
(case tribute-status
:loading
[react/view (assoc (dissoc style/empty-chat-container :flex)
:justify-content :flex-end)
[react/view {:style {:align-items :center :justify-content :flex-end}}
[react/view {:style {:flex-direction :row :justify-content :center}}
[react/text {:style style/loading-text}
(i18n/label :t/loading)]
[react/activity-indicator {:color colors/gray
:animating true}]]]]
:required
[react/view
[tribute-to-talk-header name]
[pay-to-chat-messages snt-amount chat-id tribute-status tribute-label
fiat-amount fiat-currency token]
[react/view {:style style/are-you-friends-bubble}
[react/text {:style (assoc style/are-you-friends-text
:font-weight "500")}
(i18n/label :t/tribute-to-talk-are-you-friends)]
[react/text {:style style/are-you-friends-text}
(i18n/label :t/tribute-to-talk-ask-to-be-added)]
[react/text {:style style/share-my-profile
:on-press on-share-my-profile}
(i18n/label :t/share-my-profile)]]]
:pending
[react/view
[tribute-to-talk-header name]
[pay-to-chat-messages snt-amount chat-id tribute-status tribute-label
fiat-amount fiat-currency token]]
(:paid :none)
[react/view
[intro-header name]
(when (= tribute-status :paid)
[pay-to-chat-messages snt-amount chat-id tribute-status tribute-label
fiat-amount fiat-currency token])
(when received?
[pay-to-chat-messages nil nil nil nil nil nil nil])
(when (or (= tribute-status :paid) received?)
[react/view {:style {:margin-top 16 :margin-horizontal 8}}
[react/nested-text {:style style/tribute-received-note}
(when received?
[{:style (assoc style/tribute-received-note :color colors/gray)}
(i18n/label :tribute-to-talk-tribute-received1)])
[{:style (assoc style/tribute-received-note :font-weight "500")}
name]
[{:style (assoc style/tribute-received-note :color colors/gray)}
(i18n/label (if received? :tribute-to-talk-tribute-received2
:tribute-to-talk-contact-received-your-tribute))]]])]
[intro-header name]))
(defn chat-intro-header-container
[{:keys [group-chat name pending-invite-inviter-name
inviter-name color chat-id chat-name public?
contact universal-link intro-status height input-height] :as chat}
no-messages]
(let [icon-text (if public? chat-id name)
intro-name (if public? chat-name name)]
;; TODO This when check ought to be unnecessary but for now it prevents
;; jerky motion when fresh chat is created, when input-height can be null
;; affecting the calculation of content-layout-height to be briefly adjusted
(when (or input-height
pending-invite-inviter-name
(not= (get-in contact [:tribute-to-talk :snt-amount]) 0))
[react/touchable-without-feedback
{:style {:flex 1
:align-items :flex-start}
:on-press (fn [_]
(re-frame/dispatch
[:chat.ui/set-chat-ui-props {:messages-focused? true
:show-stickers? false}])
(react/dismiss-keyboard!))}
[react/view (style/intro-header-container height intro-status no-messages)
;; Icon section
[react/view {:style {:margin-top 42
:margin-bottom 24}}
[chat-icon.screen/chat-intro-icon-view
icon-text chat-id
{:default-chat-icon (style/intro-header-icon 120 color)
:default-chat-icon-text style/intro-header-icon-text
:size 120}]]
;; Chat title section
[react/text {:style style/intro-header-chat-name} intro-name]
;; Description section
(if group-chat
[group-chat-description-container chat]
[one-to-one-chat-description-container chat])]])))
(defonce messages-list-ref (atom nil))
@ -257,9 +358,10 @@
default-limit-step)))
(defview messages-view
[{:keys [group-chat chat-id pending-invite-inviter-name] :as chat}
[{:keys [group-chat chat-id pending-invite-inviter-name contact] :as chat}
modal?]
(letsubs [messages [:chats/current-chat-messages-stream]
photo-path [:chats/photo-path chat-id]
current-public-key [:account/public-key]]
{:component-will-mount
(fn []
@ -353,14 +455,10 @@
:idx #(or (:message-id message-obj) (:value message-obj))
:list-ref messages-list-ref}]))]]])))
(defn show-input-container? [my-public-key current-chat]
(or (not (models.chat/group-chat? current-chat))
(group-chats.db/joined? my-public-key current-chat)))
(defview chat-root [modal?]
(letsubs [{:keys [public? chat-id] :as current-chat} [:chats/current-chat]
(letsubs [{:keys [public? chat-id show-input?] :as current-chat}
[:chats/current-chat]
current-chat-id [:chats/current-chat-id]
my-public-key [:account/public-key]
show-bottom-info? [:chats/current-chat-ui-prop :show-bottom-info?]
show-message-options? [:chats/current-chat-ui-prop :show-message-options?]
show-stickers? [:chats/current-chat-ui-prop :show-stickers?]]
@ -385,7 +483,7 @@
(if platform/desktop?
[messages-view-desktop current-chat modal?]
[messages-view current-chat modal?])]
(when (show-input-container? my-public-key current-chat)
(when show-input?
[input/container])
(when show-stickers?
[stickers/stickers-view])

View File

@ -1,20 +1,20 @@
(ns status-im.ui.screens.home.views.inner-item
(:require-macros [status-im.utils.views :refer [defview letsubs]])
(:require [re-frame.core :as re-frame]
[clojure.string :as str]
[status-im.constants :as constants]
(:require [clojure.string :as string]
[re-frame.core :as re-frame]
[status-im.chat.commands.core :as commands]
[status-im.chat.commands.receiving :as commands-receiving]
[status-im.constants :as constants]
[status-im.i18n :as i18n]
[status-im.ui.components.chat-icon.screen :as chat-icon.screen]
[status-im.ui.components.colors :as colors]
[status-im.ui.components.common.common :as components.common]
[status-im.ui.components.icons.vector-icons :as vector-icons]
[status-im.ui.components.list.views :as list]
[status-im.ui.components.react :as react]
[status-im.ui.screens.home.styles :as styles]
[status-im.utils.core :as utils]
[status-im.i18n :as i18n]
[status-im.utils.datetime :as time]
[status-im.ui.components.colors :as colors]
[status-im.ui.components.icons.vector-icons :as vector-icons]
[status-im.ui.components.chat-icon.screen :as chat-icon.screen]
[status-im.ui.components.common.common :as components.common]
[status-im.ui.components.list.views :as list]))
[status-im.utils.datetime :as time])
(:require-macros [status-im.utils.views :refer [defview letsubs]]))
(defview command-short-preview [message]
(letsubs [id->command [:chats/id->command]
@ -38,7 +38,7 @@
[react/image {:style {:margin 2 :width 30 :height 30}
:source {:uri (:uri content)}}]
(str/blank? (:text content))
(string/blank? (:text content))
[react/text {:style styles/last-message-text}
""]
@ -111,6 +111,10 @@
[react/view styles/message-status-container
[message-timestamp timestamp]]]
[react/view styles/item-lower-container
[message-content-text {:content last-message-content
:content-type last-message-content-type}]
(let [{:keys [tribute-status tribute-label]} (:tribute-to-talk contact)]
(if (or group-chat
(#{:none :paid} tribute-status))
[message-content-text {:content last-message-content
:content-type last-message-content-type}]
[react/text {:style styles/last-message-text} tribute-label]))
[unviewed-indicator chat-id]]]]]))

View File

@ -16,26 +16,29 @@
toolbar/default-nav-back
[toolbar/content-title ""]])
(defn actions [{:keys [public-key added?]}]
(concat (if added?
[{:label (i18n/label :t/in-contacts)
:icon :main-icons/in-contacts
:disabled? true
:accessibility-label :in-contacts-button}]
[{:label (i18n/label :t/add-to-contacts)
:icon :main-icons/add-contact
:action #(re-frame/dispatch [:contact.ui/add-to-contact-pressed public-key])
:accessibility-label :add-to-contacts-button}])
[{:label (i18n/label :t/send-message)
:icon :main-icons/message
:action #(re-frame/dispatch [:contact.ui/send-message-pressed {:public-key public-key}])
:accessibility-label :start-conversation-button}
{:label (i18n/label :t/send-transaction)
:icon :main-icons/send
:action #(re-frame/dispatch [:profile/send-transaction public-key])
:accessibility-label :send-transaction-button}]
(when-not platform/desktop?
[{:label (i18n/label :t/share-profile-link)
(defn actions
[{:keys [public-key added? tribute-to-talk] :as contact}]
(let [{:keys [tribute-status tribute-label]} tribute-to-talk]
(concat (if added?
[{:label (i18n/label :t/in-contacts)
:icon :main-icons/in-contacts
:disabled? true
:accessibility-label :in-contacts-button}]
[{:label (i18n/label :t/add-to-contacts)
:icon :main-icons/add-contact
:action #(re-frame/dispatch [:contact.ui/add-to-contact-pressed public-key])
:accessibility-label :add-to-contacts-button}])
[(cond-> {:label (i18n/label :t/send-message)
:icon :main-icons/message
:action #(re-frame/dispatch [:contact.ui/send-message-pressed {:public-key public-key}])
:accessibility-label :start-conversation-button}
(not (#{:none :paid} tribute-status))
(assoc :subtext tribute-label))
{:label (i18n/label :t/send-transaction)
:icon :main-icons/send
:action #(re-frame/dispatch [:profile/send-transaction public-key])
:accessibility-label :send-transaction-button}
{:label (i18n/label :t/share-profile-link)
:icon :main-icons/share
:action #(re-frame/dispatch [:profile/share-profile-link public-key])
:accessibility-label :share-profile-link}])))

View File

@ -98,24 +98,6 @@
{:typography :main-medium
:color colors/gray})
(def personalized-message-container
{:flex-grow 1
:align-items :center
:margin-horizontal 16
:justify-content :flex-start})
(def personalized-message-title
{:margin-top 24
:margin-bottom 10
:align-self :flex-start})
(def personalized-message-input
{:border-radius 8
:background-color colors/gray-lighter
:text-align-vertical :top
:padding-horizontal 16
:padding-vertical 16})
(def edit-view-message-container
{:border-radius 8
:background-color colors/blue-light
@ -220,22 +202,29 @@
:width 238
:border-color colors/gray-lighter})
(def chat-sample-bubble
(defn chat-bubble [tribute-sender?]
{:background-color (if tribute-sender? colors/blue-light colors/blue)
:padding-horizontal 12
:padding-vertical 6
:margin-top 4
:border-radius 8})
(def pay-to-chat-bubble
{:background-color colors/blue-light
:padding-horizontal 12
:padding-top 8
:padding-vertical 8
:margin-top 4
:border-radius 8})
(def pay-to-chat-container
{:justify-content :flex-start
{:justify-content :center
:align-items :center
:flex-direction :row
:height 44})
:padding-top 12
:padding-bottom 4})
(def pay-to-chat-text
{:typography :main-medium
:color colors/blue})
{:color colors/blue})
(defn payment-status-icon [pending?]
{:width 24

View File

@ -3,7 +3,6 @@
[re-frame.core :as re-frame]
[status-im.i18n :as i18n]
[status-im.react-native.resources :as resources]
[status-im.tribute-to-talk.core :as tribute-to-talk]
[status-im.ui.components.colors :as colors]
[status-im.ui.components.common.common :as components.common]
[status-im.ui.components.icons.vector-icons :as icons]
@ -19,36 +18,35 @@
(defn steps-numbers [editing?]
{:intro 1
:set-snt-amount (if editing? 1 2)
:personalized-message (if editing? 2 3)})
:set-snt-amount (if editing? 1 2)})
(def step-forward-label
{:intro :t/get-started
:set-snt-amount :t/continue
:personalized-message :t/tribute-to-talk-sign-and-set-tribute
:finish :t/ok-got-it})
(defn intro
[]
[react/view {:style styles/intro-container}
[react/view {:style {:flex 1
:min-height 32}}]
[(react/scroll-view)
[react/view {:style styles/intro-container}
[react/view {:style {:flex 1
:min-height 32}}]
[react/image {:source (resources/get-image :tribute-to-talk)
:style styles/intro-image}]
[react/view {:style {:flex 1
:min-height 32}}]
[react/image {:source (resources/get-image :tribute-to-talk)
:style styles/intro-image}]
[react/view {:style {:flex 1
:min-height 32}}]
[react/view
[react/i18n-text {:style styles/intro-text
:key :tribute-to-talk}]
[react/i18n-text {:style (assoc styles/description-label :margin-top 12)
:key :tribute-to-talk-desc}]
[react/view {:style styles/learn-more-link}
[react/text {:style styles/learn-more-link-text
:on-press #(re-frame/dispatch
[:tribute-to-talk.ui/learn-more-pressed])}
(i18n/label :t/learn-more)]]]])
[react/view
[react/i18n-text {:style styles/intro-text
:key :tribute-to-talk}]
[react/i18n-text {:style (assoc styles/description-label :margin-top 12)
:key :tribute-to-talk-desc}]
[react/view {:style styles/learn-more-link}
[react/text {:style styles/learn-more-link-text
:on-press #(re-frame/dispatch
[:tribute-to-talk.ui/learn-more-pressed])}
(i18n/label :t/learn-more)]]]]])
(defn snt-asset-value
[fiat-value]
@ -107,26 +105,6 @@
[react/i18n-text {:style (assoc styles/description-label :margin-horizontal 16)
:key :tribute-to-talk-set-snt-amount}]])
(defn personalized-message
[message]
[(react/scroll-view)
{:content-container-style styles/personalized-message-container}
[react/view {:style styles/personalized-message-title}
[react/nested-text {:style {:text-align :center}}
(i18n/label :t/personalized-message)
[{:style styles/description-label} (str " (" (i18n/label :t/optional) ")")]]]
[react/text-input
(cond-> {:style (assoc styles/personalized-message-input :height 144
:align-self :stretch)
:multiline true
:on-change-text #(re-frame/dispatch
[:tribute-to-talk.ui/message-changed %1])
:placeholder (i18n/label :t/tribute-to-talk-message-placeholder)}
(not (string/blank? message))
(assoc :default-value message))]
[react/text {:style (assoc styles/description-label :margin-top 16 :margin-horizontal 32)}
(i18n/label :t/tribute-to-talk-you-can-leave-a-message)]])
(defn finish
[snt-amount state]
[react/view {:style styles/intro-container}
@ -218,7 +196,7 @@
(i18n/label :t/tribute-to-talk-add-friends)]])
(defn edit
[snt-amount message fiat-value]
[snt-amount fiat-value]
[(react/scroll-view) {:content-container-style styles/edit-container}
[react/view {:style styles/edit-screen-top-row}
[react/view {:style {:flex-direction :row
@ -239,12 +217,6 @@
[:tribute-to-talk.ui/edit-pressed])
:style styles/edit-label}
(i18n/label :t/edit)]]
(when-not (string/blank? message)
[react/view {:flex-direction :row
:margin-bottom 16}
[react/view {:style styles/edit-view-message-container}
[react/text message]]
[react/view {:flex 1}]])
[separator]
[react/text {:style styles/edit-note}
(i18n/label :t/tribute-to-talk-you-require-snt)]
@ -265,43 +237,43 @@
:min-height 24}]
[enabled-note]])
(defn pay-to-chat-message [{:keys [snt-amount fiat-amount fiat-currency
personalized-message style public-key tribute-status]}]
(defn pay-to-chat-message
[{:keys [snt-amount style public-key tribute-status
tribute-label fiat-amount fiat-currency token]}]
[react/view {:style style}
[react/view {:style {:flex-direction :row
:align-items :center}}
[react/view {:style {:background-color colors/white
:justify-content :center :align-items :center}}
:justify-content :center
:align-items :center}}
[icons/icon :tiny-icons/tribute-to-talk {:color colors/blue}]]
[react/text {:style {:color colors/gray
:font-size 13
:margin-left 4}}
(i18n/label :t/tribute-to-talk)]]
(when-not (string/blank? personalized-message)
[react/view {:style styles/chat-sample-bubble}
[react/text (i18n/label :t/tribute-to-talk-sample-text)]])
[react/view {:style styles/chat-sample-bubble}
;;TODO replace hardcoded values
[react/nested-text {:style {:font-size 22}}
(str snt-amount)
[{:style {:font-size 22 :color colors/gray}} " SNT"]]
[react/nested-text
{:style {:font-size 12}}
(str "~" fiat-amount)
[{:style {:font-size 12 :color colors/gray}} (str " " fiat-currency)]]
[react/view {:style styles/pay-to-chat-container}
(if (or (nil? public-key) (= tribute-status :required))
[react/text (cond-> {:style styles/pay-to-chat-text}
public-key
(assoc :on-press #(re-frame/dispatch [:tribute-to-talk.ui/on-pay-to-chat-pressed
public-key])))
(i18n/label :t/pay-to-chat)]
[react/view {:style {:flex-direction :row}}
[react/view {:style (styles/payment-status-icon (= tribute-status :pending))}
[icons/icon (if (= tribute-status :pending) :tiny-icons/tiny-pending :tiny-icons/tiny-check)
{:color (if (= tribute-status :pending) colors/black colors/white)}]]
[react/text {:style styles/payment-status-text}
nil]])]]])
(when snt-amount
[react/view {:style styles/pay-to-chat-bubble}
[react/nested-text {:style {:font-size 22}}
(str snt-amount)
[{:style {:font-size 22 :color colors/gray}} token]]
[react/nested-text
{:style {:font-size 12}}
(str "~" fiat-amount)
[{:style {:font-size 12 :color colors/gray}}
(str " " fiat-currency)]]
(if (or (nil? public-key) (= tribute-status :required))
[react/view {:style styles/pay-to-chat-container}
[react/text (cond-> {:style styles/pay-to-chat-text}
public-key
(assoc :on-press
#(re-frame/dispatch [:tribute-to-talk.ui/on-pay-to-chat-pressed
public-key])))
(i18n/label :t/pay-to-chat)]]
[react/view {:style styles/pay-to-chat-container}
[react/view {:style (styles/payment-status-icon (= tribute-status :pending))}
[icons/icon (if (= tribute-status :pending) :tiny-icons/tiny-pending :tiny-icons/tiny-check)
{:color (if (= tribute-status :pending) colors/black colors/white)}]]
[react/text {:style styles/payment-status-text} tribute-label]])])])
(defn learn-more [owner?]
[react/view {:flex 1}
@ -323,17 +295,19 @@
:t/tribute-to-talk-paywall-learn-more-1))]]
[separator]
[pay-to-chat-message {:snt-amount 1000
:fiat-amount 3.48
:fiat-currency (i18n/label :t/usd-currency)
:personalized-message (i18n/label :t/tribute-to-talk-sample-text)
:style (assoc styles/learn-more-section :margin-top 24)}]
:token " SNT"
:fiat-currency "USD"
:fiat-amount "5"
:style (assoc styles/learn-more-section
:align-items :flex-start
:margin-top 24)}]
[react/view {:style styles/learn-more-text-container-2}
[react/text {:style styles/learn-more-text}
(i18n/label (if owner? :t/tribute-to-talk-learn-more-2
:t/tribute-to-talk-paywall-learn-more-2))]]
[react/view {:style (assoc styles/learn-more-section
:flex-direction :row
:align-item :flex-stretch
:align-items :stretch
:padding-horizontal 16
:padding-vertical 12)}
[react/view {:style (styles/icon-view colors/blue-light)}
@ -347,9 +321,9 @@
:t/tribute-to-talk-paywall-learn-more-3))]]]])
(defview tribute-to-talk []
(letsubs [{:keys [step snt-amount editing? message
(letsubs [{:keys [step snt-amount editing?
fiat-value disable-button? state]}
[:tribute-to-talk/ui]]
[:tribute-to-talk/settings-ui]]
[react/keyboard-avoiding-view {:style styles/container}
[(react/safe-area-view) {:style {:flex 1}}
[status-bar/status-bar]
@ -380,9 +354,8 @@
(case step
:intro [intro]
:set-snt-amount [set-snt-amount snt-amount]
:edit [edit snt-amount message fiat-value]
:edit [edit snt-amount fiat-value]
:learn-more [learn-more step]
:personalized-message [personalized-message message]
:finish [finish snt-amount state])
(when-not (#{:learn-more :edit} step)

View File

@ -83,7 +83,8 @@
:style styles/share-link-button}]))
(defview qr-viewer []
(letsubs [{:keys [value contact]} [:qr-modal]]
(letsubs [{:keys [value contact]} [:qr-modal]
ttt-enabled? [:tribute-to-talk/enabled?]]
[react/view styles/qr-code-viewer
[status-bar/status-bar {:type :modal-white}]
[qr-viewer-toolbar (:name contact) value]
@ -92,7 +93,8 @@
{:style styles/qr-code
:value value
:hint (i18n/label :t/qr-code-public-key-hint)
:legend (str value)}
:legend (str value)
:show-tribute-to-talk-warning? ttt-enabled?}
(when-not platform/desktop?
{:footer-button qr-code-share-button}))]]))
@ -284,36 +286,14 @@
:accessory-value active-contacts-count
:action-fn #(re-frame/dispatch [:navigate-to :contacts-list])}])
(defn tribute-to-talk-item [state snt-amount seen?]
(defn tribute-to-talk-item
[opts]
[list.views/big-list-item
(cond-> {:text (i18n/label :t/tribute-to-talk)
:accessibility-label :notifications-button
:new? (not seen?)
:action-fn #(re-frame/dispatch
[:tribute-to-talk.ui/menu-item-pressed])}
(and (not (and seen?
snt-amount
(#{:signing :pending :transaction-failed} state))))
(assoc :subtext (i18n/label :t/tribute-to-talk-desc))
(#{:signing :pending} state)
(assoc :activity-indicator {:animating true
:color colors/blue}
:subtext (case state
:pending (i18n/label :t/pending-confirmation)
:signing (i18n/label :t/waiting-to-sign)))
(= state :transaction-failed)
(assoc :icon :main-icons/warning
:icon-color colors/red
:subtext (i18n/label :t/transaction-failed))
(not (#{:signing :pending :transaction-failed} state))
(assoc :icon :main-icons/tribute-to-talk)
(and (= state :completed)
(not-empty snt-amount))
(assoc :accessory-value (str snt-amount " SNT")))])
(merge {:text (i18n/label :t/tribute-to-talk)
:accessibility-label :notifications-button
:action-fn #(re-frame/dispatch
[:tribute-to-talk.ui/menu-item-pressed])}
opts)])
(defview extensions-settings []
(letsubs [{:keys [label view on-close]} [:get-screen-params :my-profile-ext-settings]]
@ -332,9 +312,7 @@
login-data [:accounts/login]
scroll (reagent/atom nil)
active-contacts-count [:contacts/active-count]
{tribute-to-talk-seen? :seen?
snt-amount :snt-amount
tribute-to-talk-state :state} [:tribute-to-talk/ui]]
tribute-to-talk [:tribute-to-talk/profile]]
(let [shown-account (merge current-account changed-account)
;; We scroll on the component once rendered. setTimeout is necessary,
;; likely to allow the animation to finish.
@ -367,11 +345,8 @@
:on-change-text-event :my-profile/update-name}]]
[share-profile-item (dissoc current-account :mnemonic)]
[contacts-list-item active-contacts-count]
(when config/tr-to-talk-enabled?
[tribute-to-talk-item
tribute-to-talk-state
snt-amount
tribute-to-talk-seen?])
(when tribute-to-talk
[tribute-to-talk-item tribute-to-talk])
[my-profile-settings current-account shown-account currency (nil? login-data) extensions]
(when (nil? login-data)
[advanced shown-account on-show-advanced])]]])))

View File

@ -43,6 +43,9 @@
[status-im.test.search.core]
[status-im.test.sign-in.flow]
[status-im.test.transport.core]
[status-im.test.tribute-to-talk.core]
[status-im.test.tribute-to-talk.db]
[status-im.test.tribute-to-talk.whitelist]
[status-im.test.ui.screens.add-new.models]
[status-im.test.ui.screens.currency-settings.models]
[status-im.test.ui.screens.wallet.db]
@ -119,6 +122,9 @@
'status-im.test.search.core
'status-im.test.sign-in.flow
'status-im.test.transport.core
'status-im.test.tribute-to-talk.core
'status-im.test.tribute-to-talk.db
'status-im.test.tribute-to-talk.whitelist
'status-im.test.ui.screens.add-new.models
'status-im.test.ui.screens.currency-settings.models
'status-im.test.ui.screens.wallet.db

View File

@ -0,0 +1,110 @@
(ns status-im.test.tribute-to-talk.core
(:require [cljs.test :refer-macros [deftest testing is]]
[status-im.tribute-to-talk.core :as tribute-to-talk]
[status-im.utils.money :as money]))
(deftest get-new-snt-amount
(testing "staying under the limit"
(is (= (tribute-to-talk/get-new-snt-amount
"999999.9" "9")
"999999.99")))
(testing "getting over the limit"
(is (= (tribute-to-talk/get-new-snt-amount
"100000" "0")
"100000")))
(testing "replace initial 0 by the first digit"
(is (= (tribute-to-talk/get-new-snt-amount
"0" "1")
"1")))
(testing "disallow more than two dots"
(is (= (tribute-to-talk/get-new-snt-amount
"0." ".")
"0."))
(is (= (tribute-to-talk/get-new-snt-amount
"0.0" ".")
"0.0")))
(testing "disallow more than two digits after dot"
(is (= (tribute-to-talk/get-new-snt-amount
"0.0" "0")
"0.00"))
(is (= (tribute-to-talk/get-new-snt-amount
"0.00" "1")
"0.00")))
(testing "0 remains if removed is pressed on last digit"
(is (= (tribute-to-talk/get-new-snt-amount
"0" :remove)
"0"))
(is (= (tribute-to-talk/get-new-snt-amount
"1" :remove)
"0")))
(testing "dot is removed when last digit after dot is removed"
(= (tribute-to-talk/get-new-snt-amount
"1." :remove)
"1")
(= (tribute-to-talk/get-new-snt-amount
"1.1" :remove)
"1")))
(def recipient-pk "0x04263d74e55775280e75b4a4e9a45ba59fc372793a869c5d9c4fa2100556d9963e3f4fbfa1724ec94a46e6da057540ab248ed1f5eb956e36e3129ecd50fade2c97")
(def recipient-address "0xdff1a5e4e57d9723b3294e0f4413372e3ea9a8ff")
(def user-cofx
{:db {:account/account
{:address "954d4393515747ea75808a0301fb73317ae1e460"
:network "testnet_rpc"
:networks {"testnet_rpc" {:config {:NetworkId 3}}}
:settings {:tribute-to-talk {:testnet {:snt-amount "1000000000000000000"}}}}
:contacts/contacts
{recipient-pk {:name "bob"
:address recipient-address
:public-key recipient-pk
:tribute-to-talk {:snt-amount "1000000000000000000"}}}
:wallet {:balance {:STT (money/bignumber "1000000000000000000")}}}})
(deftest pay-tribute
(testing "transaction with enough funds"
(let [results (tribute-to-talk/pay-tribute
user-cofx
recipient-pk)]
(is (= (dissoc (get-in results [:db :wallet :send-transaction]) :amount)
{:on-result
[:tribute-to-talk.callback/pay-tribute-transaction-sent
"0x04263d74e55775280e75b4a4e9a45ba59fc372793a869c5d9c4fa2100556d9963e3f4fbfa1724ec94a46e6da057540ab248ed1f5eb956e36e3129ecd50fade2c97"],
:to-name "bob",
:amount-text "1",
:method "eth_sendTransaction",
:symbol :ETH,
:send-transaction-message? true,
:from-chat? true,
:from "0x954d4393515747ea75808a0301fb73317ae1e460",
:id "approve",
:sufficient-funds? true,
:on-error nil,
:public-key
"0x04263d74e55775280e75b4a4e9a45ba59fc372793a869c5d9c4fa2100556d9963e3f4fbfa1724ec94a46e6da057540ab248ed1f5eb956e36e3129ecd50fade2c97",
:asset :STT,
:to "0xc55cf4b03948d7ebc8b9e8bad92643703811d162",
:data
"0xa9059cbb000000000000000000000000dff1a5e4e57d9723b3294e0f4413372e3ea9a8ff0000000000000000000000000000000000000000000000000de0b6b3a7640000"}))))
(testing "insufficient funds"
(is (= (get-in (tribute-to-talk/pay-tribute
(assoc-in user-cofx [:db :wallet] nil)
recipient-pk)
[:db :wallet :send-transaction :sufficient-funds?])
false))))
(deftest tribute-transaction-trigger
(testing "transaction error"
(is (tribute-to-talk/tribute-transaction-trigger
{:ethereum/current-block 10}
{:block "5"
:error? true})))
(testing "transaction confirmed"
(is (tribute-to-talk/tribute-transaction-trigger
{:ethereum/current-block 10}
{:block "5"})))
(testing "transaction not confirmed yet"
(is (not (tribute-to-talk/tribute-transaction-trigger
{:ethereum/current-block 5}
{:block "5"})))))

View File

@ -0,0 +1,81 @@
(ns status-im.test.tribute-to-talk.db
(:require [cljs.test :refer-macros [deftest testing is]]
[status-im.tribute-to-talk.db :as db]))
(deftest tribute-status
(is (= (db/tribute-status {:system-tags #{:tribute-to-talk/paid}
:tribute-to-talk {:snt-amount 1000
:transaction-hash "0x"}})
:paid))
(is (= (db/tribute-status {:tribute-to-talk {:snt-amount 1000
:transaction-hash "0x"}})
:pending))
(is (= (db/tribute-status {:tribute-to-talk {:snt-amount 1000}})
:required))
(is (= (db/tribute-status {:tribute-to-talk {:snt-amount 0}})
:none))
(is (= (db/tribute-status {})
:none)))
(deftest valid?
(is (db/valid? {:snt-amount "1000"}))
(is (not (db/valid? {:snt-amount "-1000"})))
(is (not (db/valid? {:snt-amount "1000001000000000000000000"})))
(is (not (db/valid? {:snt-amount 5})))
(is (not (db/valid? {:snt-amount "abcdef"}))))
(def sender-pk "0x04263d74e55775280e75b4a4e9a45ba59fc372793a869c5d9c4fa2100556d9963e3f4fbfa1724ec94a46e6da057540ab248ed1f5eb956e36e3129ecd50fade2c97")
(def sender-address "0xdff1a5e4e57d9723b3294e0f4413372e3ea9a8ff")
(deftest valid-tribute-transaction?
(testing "a valid transaction"
(is (db/valid-tribute-transaction?
{:wallet {:transactions
{"transaction-hash-1"
{:value "1000000000000000000"
:block "5"
:from sender-address}}}
:ethereum/current-block 8}
"1000000000000000000"
"transaction-hash-1"
sender-pk)))
(testing "a transaction"
(testing "with insufficient value transfered"
(is (not (db/valid-tribute-transaction?
{:wallet {:transactions
{"transaction-hash-1"
{:value "1"
:block "5"
:from sender-address}}}
:ethereum/current-block 8}
"1000000000000000000"
"transaction-hash-1"
sender-pk)))
(testing "that was not confirmed yet"
(is (not (db/valid-tribute-transaction?
{:wallet {:transactions
{"transaction-hash-1"
{:value "1000000000000000000"
:block "8"
:from sender-address}}}
:ethereum/current-block 8}
"1000000000000000000"
"transaction-hash-1"
sender-pk))))
(testing "from someone else"
(is (not (db/valid-tribute-transaction?
{:wallet {:transactions
{"transaction-hash-1"
{:value "1000000000000000000"
:block "5"
:from "another address"}}}
:ethereum/current-block 8}
"1000000000000000000"
"transaction-hash-1"
sender-pk))))))
(testing "a transaction that does not exist"
(is (not (db/valid-tribute-transaction?
{:ethereum/current-block 8}
"1000000000000000000"
"transaction-hash-1"
sender-pk)))))

View File

@ -0,0 +1,131 @@
(ns status-im.test.tribute-to-talk.whitelist
(:require [cljs.test :refer-macros [deftest testing is]]
[status-im.tribute-to-talk.whitelist :as whitelist]))
(def user-contacts
{"not whitelisted"
{:public-key "not whitelisted"
:system-tags #{}}
"not whitelisted2"
{:public-key "not whitelisted2"
:system-tags #{:contact/request-received}}
"whitelisted because tribute paid"
{:public-key "whitelisted because tribute paid"
:system-tags #{:tribute-to-talk/paid}}
"whitelisted because tribute received"
{:public-key "whitelisted because tribute received"
:system-tags #{:tribute-to-talk/received}}
"whitelisted because added"
{:public-key "whitelisted because added"
:system-tags #{:contact/added}}})
(deftest get-contact-whitelist
(is (= 3
(count (whitelist/get-contact-whitelist
(vals user-contacts))))))
(deftest add-to-whitelist
(testing "adding contact to whitelist"
(is (= #{"bob" "bob2"}
(get-in (whitelist/add-to-whitelist
{:db {:contacts/whitelist #{"bob2"}}} "bob")
[:db :contacts/whitelist])))
(testing "when there is no whitelist yet"
(is (= #{"bob"}
(get-in (whitelist/add-to-whitelist
{:db {}} "bob")
[:db :contacts/whitelist]))
(whitelist/add-to-whitelist {:db {}} "bob")))
(testing "when already added"
(is (= #{"bob"}
(get-in (whitelist/add-to-whitelist
{:db {:contacts/whitelist #{"bob"}}} "bob")
[:db :contacts/whitelist]))))))
(deftest mark-tribute-paid
(let [result (whitelist/mark-tribute-paid {:db {}} "bob")]
(testing "contact was added to whitelist"
(is (= (get-in result
[:db :contacts/whitelist])
#{"bob"})))
(testing "contact was tagged as tribute paid"
(is (= (get-in result
[:db :contacts/contacts "bob" :system-tags])
#{:tribute-to-talk/paid})))
(testing "change is persisted"
(is (:data-store/tx result)))))
(deftest mark-tribute-received
(let [result (whitelist/mark-tribute-received {:db {}} "bob")]
(testing "contact was added to whitelist"
(is (= (get-in result
[:db :contacts/whitelist])
#{"bob"})))
(testing "contact was tagged as tribute paid"
(is (= (get-in result
[:db :contacts/contacts "bob" :system-tags])
#{:tribute-to-talk/received})))
(testing "change is persisted"
(is (:data-store/tx result)))))
(def sender-pk "0x04263d74e55775280e75b4a4e9a45ba59fc372793a869c5d9c4fa2100556d9963e3f4fbfa1724ec94a46e6da057540ab248ed1f5eb956e36e3129ecd50fade2c97")
(def sender-address "0xdff1a5e4e57d9723b3294e0f4413372e3ea9a8ff")
(def ttt-enabled-account
{:db {:account/account {:network "testnet_rpc"
:networks {"testnet_rpc" {:config {:NetworkId 3}}}
:settings {:tribute-to-talk {:testnet {:snt-amount "1000000000000000000"}}}}
:contacts/contacts user-contacts
:wallet {:transactions
{"transaction-hash-1"
{:value "1000000000000000000"
:block "5"
:from sender-address}}}
:ethereum/current-block 8}})
(def ttt-disabled-account
{:db {:account/account {:network "testnet_rpc"
:networks {"testnet_rpc" {:config {:NetworkId 3}}}
:settings {:tribute-to-talk {}}}
:contacts/contacts user-contacts}})
(deftest enable-whitelist
(testing "ttt enabled account"
(is (= (get-in (whitelist/enable-whitelist ttt-enabled-account)
[:db :contacts/whitelist]))))
(testing "ttt disabled account"
(is (not (get-in (whitelist/enable-whitelist ttt-disabled-account)
[:db :contacts/whitelist])))))
(deftest filter-message
(testing "not a user message"
(whitelist/filter-message
ttt-enabled-account
:unfiltered-fx
:not-user-message
nil
"public-key"))
(testing "user is whitelisted"
(whitelist/filter-message
(whitelist/enable-whitelist ttt-enabled-account)
:unfiltered-fx
:user-message
nil
"whitelisted because added"))
(testing "tribute to talk is disabled"
(whitelist/filter-message
ttt-disabled-account
:unfiltered-fx
:user-message
nil
"public-key"))
(testing "user is not whitelisted but transaction is valid"
(let [result (whitelist/filter-message
ttt-enabled-account
#(assoc % :message-received true)
:user-message
"transaction-hash-1"
sender-pk)]
(is (contains? (get-in result [:db :contacts/whitelist])
sender-pk))
(is (:message-received result)))))

View File

@ -919,7 +919,6 @@
"tribute-to-talk": "Tribute to talk",
"tribute-to-talk-desc": "Monetize your attention by requiring SNT for new people to start a chat",
"tribute-to-talk-set-snt-amount": "Set the amount of SNT required for new people to start a chat",
"personalized-message": "Personalized message",
"optional": "optional",
"pending-confirmation": "Pending confirmation...",
"waiting-to-sign": "Waiting to sign transaction...",
@ -953,7 +952,9 @@
"tribute-to-talk-paywall-learn-more-1": "Our time and attention are our most valuable assets. Tribute to Talk lets you contact new people in exchange for an SNT payment.",
"tribute-to-talk-paywall-learn-more-2": "To start a chat with someone who has a tribute set, simply pay the required SNT and you will be added as a contact.",
"tribute-to-talk-paywall-learn-more-3": "If you know them, you can share your profile outside of Status to be added for free.",
"tribute-to-talk-contact-received-your-tribute": " received your tribute. You are now contacts and can securely chat with each other.",
"tribute-to-talk-tribute-received1": "Tribute received. You and ",
"tribute-to-talk-tribute-received2": " are now contacts and can securely chat with each other.",
"tribute-to-talk-contact-received-your-tribute": " received your tribute. You can now securely chat with each other.",
"pay-to-chat": "Pay to chat",
"share-chat": "Share chat",
"share-link": "Share link",

View File

@ -644,7 +644,6 @@
"peers": "주변 노드 (Peers)",
"pending": "대기 중",
"permissions": "권한",
"personalized-message": "개인 메시지",
"pfs": "PFS 활성화",
"pfs-warning-content": "PFS 지원은 아직 테스트 중이므로 스테이터스는 오류에 대해 책임지지 않습니다. PFS를 활성화하면, 0.9.32 이상의 버전을 사용하는 유저들은 귀하의 개인, 공개 메시지를 읽을 수 있습니다. 아직 모든 메시지에 대한 PFS를 보장하지는 않으며, 귀하의 메시지를 암호화하지만 수신 메시지는 암호화 하지 않습니다.",
"pfs-warning-title": "경고, 테스트 중인 기능임",