Browse Source

Add TtT chat flow

Signed-off-by: yenda <eric@status.im>
tags/pre-yarn2nix-push
Vitaliy Vlasov 5 months ago
parent
commit
04ccd0e7d8
No account linked to committer's email address
37 changed files with 1507 additions and 752 deletions
  1. 1
    1
      doc/decisions/0013-tribute-to-talk.md
  2. 2
    2
      src/status_im/chat/commands/core.cljs
  3. 3
    3
      src/status_im/chat/commands/impl/transactions.cljs
  4. 16
    5
      src/status_im/chat/models.cljs
  5. 2
    0
      src/status_im/chat/models/message.cljs
  6. 31
    12
      src/status_im/contact/core.cljs
  7. 27
    27
      src/status_im/contact/db.cljs
  8. 4
    1
      src/status_im/data_store/contacts.cljs
  9. 2
    0
      src/status_im/data_store/realm/schemas/account/contact.cljs
  10. 33
    13
      src/status_im/data_store/realm/schemas/account/core.cljs
  11. 1
    1
      src/status_im/ethereum/contracts.cljs
  12. 18
    0
      src/status_im/ethereum/core.cljs
  13. 3
    100
      src/status_im/events.cljs
  14. 201
    17
      src/status_im/subs.cljs
  15. 4
    4
      src/status_im/transport/filters.cljs
  16. 20
    14
      src/status_im/transport/message/protocol.cljs
  17. 230
    152
      src/status_im/tribute_to_talk/core.cljs
  18. 52
    3
      src/status_im/tribute_to_talk/db.cljs
  19. 0
    54
      src/status_im/tribute_to_talk/subs.cljs
  20. 78
    0
      src/status_im/tribute_to_talk/whitelist.cljs
  21. 12
    0
      src/status_im/ui/components/chat_icon/screen.cljs
  22. 14
    12
      src/status_im/ui/components/qr_code_viewer/views.cljs
  23. 3
    5
      src/status_im/ui/screens/chat/message/gap.cljs
  24. 6
    6
      src/status_im/ui/screens/chat/photos.cljs
  25. 75
    21
      src/status_im/ui/screens/chat/styles/main.cljs
  26. 212
    114
      src/status_im/ui/screens/chat/views.cljs
  27. 18
    14
      src/status_im/ui/screens/home/views/inner_item.cljs
  28. 23
    20
      src/status_im/ui/screens/profile/contact/views.cljs
  29. 13
    24
      src/status_im/ui/screens/profile/tribute_to_talk/styles.cljs
  30. 58
    85
      src/status_im/ui/screens/profile/tribute_to_talk/views.cljs
  31. 14
    39
      src/status_im/ui/screens/profile/user/views.cljs
  32. 6
    0
      test/cljs/status_im/test/runner.cljs
  33. 110
    0
      test/cljs/status_im/test/tribute_to_talk/core.cljs
  34. 81
    0
      test/cljs/status_im/test/tribute_to_talk/db.cljs
  35. 131
    0
      test/cljs/status_im/test/tribute_to_talk/whitelist.cljs
  36. 3
    2
      translations/en.json
  37. 0
    1
      translations/ko.json

+ 1
- 1
doc/decisions/0013-tribute-to-talk.md 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.


+ 2
- 2
src/status_im/chat/commands/core.cljs 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"

+ 3
- 3
src/status_im/chat/commands/impl/transactions.cljs 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))

+ 16
- 5
src/status_im/chat/models.cljs 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)))

+ 2
- 0
src/status_im/chat/models/message.cljs 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)))


+ 31
- 12
src/status_im/contact/core.cljs 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)}))

+ 27
- 27
src/status_im/contact/db.cljs 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]

+ 4
- 1
src/status_im/data_store/contacts.cljs 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

+ 2
- 0
src/status_im/data_store/realm/schemas/account/contact.cljs 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}))

+ 33
- 13
src/status_im/data_store/realm/schemas/account/core.cljs 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.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.contact-recovery :as contact-recovery]
[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.local-storage :as local-storage]
[status-im.data-store.realm.schemas.account.mailserver :as mailserver]
[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)}])

+ 1
- 1
src/status_im/ethereum/contracts.cljs View File

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


+ 18
- 0
src/status_im/ethereum/core.cljs 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))))

+ 3
- 100
src/status_im/events.cljs 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

+ 201
- 17
src/status_im/subs.cljs 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 ".")))))))))

+ 4
- 4
src/status_im/transport/filters.cljs 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)])))))


+ 20
- 14
src/status_im/transport/message/protocol.cljs 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

+ 230
- 152
src/status_im/tribute_to_talk/core.cljs 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
(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)]
(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]))]
(update-settings cofx nil))))

(fx/defn check-own-manifest
[cofx]
(check-manifest cofx (get-in cofx [:db :account/account :public-key])))
(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 set-manifest-signing-flow
[{:keys [db] :as cofx} hash]
(let [contenthash (when hash
(contenthash/encode {:hash hash
:namespace :ipfs}))]
(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)})))
(fx/defn check-own-tribute
[cofx]
(check-tribute cofx (get-in cofx [:db :account/account :public-key])))

(fx/defn remove
[{:keys [db] :as cofx}]
(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)))

(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}))

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]
{: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 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}))))
(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)))

(fx/defn on-set-manifest-transaction-failed
(transactions/watch-transaction
transaction-hash
{:trigger-fn
tribute-transaction-trigger
:on-trigger
#(on-set-tribute-transaction-triggered snt-amount %)}))))

(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)))

+ 52
- 3
src/status_im/tribute_to_talk/db.cljs 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))))

+ 0
- 54
src/status_im/tribute_to_talk/subs.cljs 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)))))

+ 78
- 0
src/status_im/tribute_to_talk/whitelist.cljs 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)))

+ 12
- 0
src/status_im/ui/components/chat_icon/screen.cljs 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

+ 14
- 12
src/status_im/ui/components/qr_code_viewer/views.cljs 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"]]))

+ 3
- 5
src/status_im/ui/screens/chat/message/gap.cljs 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})))])])]]]))))


+ 6
- 6
src/status_im/ui/screens/chat/photos.cljs 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)})))

+ 75
- 21
src/status_im/ui/screens/chat/styles/main.cljs 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})

+ 212
- 114
src/status_im/ui/screens/chat/views.cljs 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?
contact universal-link range intro-status] :as chat}]
(let [{:keys [lowest-request-from highest-request-to]} range]