diff --git a/doc/decisions/0013-tribute-to-talk.md b/doc/decisions/0013-tribute-to-talk.md index 4f0aad3eec..810e96aa4b 100644 --- a/doc/decisions/0013-tribute-to-talk.md +++ b/doc/decisions/0013-tribute-to-talk.md @@ -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. diff --git a/src/status_im/chat/commands/core.cljs b/src/status_im/chat/commands/core.cljs index 697965ca76..cafbe3a929 100644 --- a/src/status_im/chat/commands/core.cljs +++ b/src/status_im/chat/commands/core.cljs @@ -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" diff --git a/src/status_im/chat/commands/impl/transactions.cljs b/src/status_im/chat/commands/impl/transactions.cljs index 4fefc91f42..7ca5f45442 100644 --- a/src/status_im/chat/commands/impl/transactions.cljs +++ b/src/status_im/chat/commands/impl/transactions.cljs @@ -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)) diff --git a/src/status_im/chat/models.cljs b/src/status_im/chat/models.cljs index 5b7e50e8ed..28ed4ca4cd 100644 --- a/src/status_im/chat/models.cljs +++ b/src/status_im/chat/models.cljs @@ -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))) diff --git a/src/status_im/chat/models/message.cljs b/src/status_im/chat/models/message.cljs index bad02941fb..d76a39c5c7 100644 --- a/src/status_im/chat/models/message.cljs +++ b/src/status_im/chat/models/message.cljs @@ -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))) diff --git a/src/status_im/contact/core.cljs b/src/status_im/contact/core.cljs index 78790dabc4..fb445ec9f4 100644 --- a/src/status_im/contact/core.cljs +++ b/src/status_im/contact/core.cljs @@ -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)})) diff --git a/src/status_im/contact/db.cljs b/src/status_im/contact/db.cljs index e73f8d666c..c1ffb48a20 100644 --- a/src/status_im/contact/db.cljs +++ b/src/status_im/contact/db.cljs @@ -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] diff --git a/src/status_im/data_store/contacts.cljs b/src/status_im/data_store/contacts.cljs index ac989808b2..983082f24e 100644 --- a/src/status_im/data_store/contacts.cljs +++ b/src/status_im/data_store/contacts.cljs @@ -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 diff --git a/src/status_im/data_store/realm/schemas/account/contact.cljs b/src/status_im/data_store/realm/schemas/account/contact.cljs index 31020abf1a..102c9ca754 100644 --- a/src/status_im/data_store/realm/schemas/account/contact.cljs +++ b/src/status_im/data_store/realm/schemas/account/contact.cljs @@ -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})) diff --git a/src/status_im/data_store/realm/schemas/account/core.cljs b/src/status_im/data_store/realm/schemas/account/core.cljs index 7fde632699..b2002612f5 100644 --- a/src/status_im/data_store/realm/schemas/account/core.cljs +++ b/src/status_im/data_store/realm/schemas/account/core.cljs @@ -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)}]) diff --git a/src/status_im/ethereum/contracts.cljs b/src/status_im/ethereum/contracts.cljs index d79c93a1bd..8bb655f27f 100644 --- a/src/status_im/ethereum/contracts.cljs +++ b/src/status_im/ethereum/contracts.cljs @@ -6,7 +6,7 @@ {:mainnet "0x744d70fdbe2ba4cf95131626614a1763df805b9e" :testnet "0xc55cf4b03948d7ebc8b9e8bad92643703811d162"} :status/tribute-to-talk - {:testnet "0x3da3fc53e24707f36c5b4433b442e896c4955f0e"} + {:testnet "0xC61aa0287247a0398589a66fCD6146EC0F295432"} :status/stickers {:testnet "0x39d16CdB56b5a6a89e1A397A13Fe48034694316E"}}) diff --git a/src/status_im/ethereum/core.cljs b/src/status_im/ethereum/core.cljs index bb2e5a48a2..f32a89dcf2 100644 --- a/src/status_im/ethereum/core.cljs +++ b/src/status_im/ethereum/core.cljs @@ -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)))) diff --git a/src/status_im/events.cljs b/src/status_im/events.cljs index 1a3fa97832..db2f6f0ec9 100644 --- a/src/status_im/events.cljs +++ b/src/status_im/events.cljs @@ -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 diff --git a/src/status_im/subs.cljs b/src/status_im/subs.cljs index 14b0993c82..afb6af7569 100644 --- a/src/status_im/subs.cljs +++ b/src/status_im/subs.cljs @@ -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 "."))))))))) diff --git a/src/status_im/transport/filters.cljs b/src/status_im/transport/filters.cljs index 0c33d74385..55bce50917 100644 --- a/src/status_im/transport/filters.cljs +++ b/src/status_im/transport/filters.cljs @@ -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)]))))) diff --git a/src/status_im/transport/message/protocol.cljs b/src/status_im/transport/message/protocol.cljs index 4b08213b00..3ac018dd92 100644 --- a/src/status_im/transport/message/protocol.cljs +++ b/src/status_im/transport/message/protocol.cljs @@ -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 diff --git a/src/status_im/tribute_to_talk/core.cljs b/src/status_im/tribute_to_talk/core.cljs index d41363fcca..5da77225c3 100644 --- a/src/status_im/tribute_to_talk/core.cljs +++ b/src/status_im/tribute_to_talk/core.cljs @@ -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))) diff --git a/src/status_im/tribute_to_talk/db.cljs b/src/status_im/tribute_to_talk/db.cljs index e84538913c..480bcd661d 100644 --- a/src/status_im/tribute_to_talk/db.cljs +++ b/src/status_im/tribute_to_talk/db.cljs @@ -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)))) diff --git a/src/status_im/tribute_to_talk/subs.cljs b/src/status_im/tribute_to_talk/subs.cljs deleted file mode 100644 index 2da0f1e0d8..0000000000 --- a/src/status_im/tribute_to_talk/subs.cljs +++ /dev/null @@ -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))))) diff --git a/src/status_im/tribute_to_talk/whitelist.cljs b/src/status_im/tribute_to_talk/whitelist.cljs new file mode 100644 index 0000000000..83b66d6c90 --- /dev/null +++ b/src/status_im/tribute_to_talk/whitelist.cljs @@ -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))) diff --git a/src/status_im/ui/components/chat_icon/screen.cljs b/src/status_im/ui/components/chat_icon/screen.cljs index 7b186aa25d..4ce918bea8 100644 --- a/src/status_im/ui/components/chat_icon/screen.cljs +++ b/src/status_im/ui/components/chat_icon/screen.cljs @@ -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 diff --git a/src/status_im/ui/components/qr_code_viewer/views.cljs b/src/status_im/ui/components/qr_code_viewer/views.cljs index 71ba0a31d9..b0d3a2f90b 100644 --- a/src/status_im/ui/components/qr_code_viewer/views.cljs +++ b/src/status_im/ui/components/qr_code_viewer/views.cljs @@ -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"]])) diff --git a/src/status_im/ui/screens/chat/message/gap.cljs b/src/status_im/ui/screens/chat/message/gap.cljs index fa938179fe..fcd49c3a45 100644 --- a/src/status_im/ui/screens/chat/message/gap.cljs +++ b/src/status_im/ui/screens/chat/message/gap.cljs @@ -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})))])])]]])))) - diff --git a/src/status_im/ui/screens/chat/photos.cljs b/src/status_im/ui/screens/chat/photos.cljs index 8e4b0a8fa8..6837bda1f1 100644 --- a/src/status_im/ui/screens/chat/photos.cljs +++ b/src/status_im/ui/screens/chat/photos.cljs @@ -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)}))) diff --git a/src/status_im/ui/screens/chat/styles/main.cljs b/src/status_im/ui/screens/chat/styles/main.cljs index 0052d67600..ca2a9565d7 100644 --- a/src/status_im/ui/screens/chat/styles/main.cljs +++ b/src/status_im/ui/screens/chat/styles/main.cljs @@ -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) \ No newline at end of file +(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}) diff --git a/src/status_im/ui/screens/chat/views.cljs b/src/status_im/ui/screens/chat/views.cljs index 34dca52737..dc60ed71af 100644 --- a/src/status_im/ui/screens/chat/views.cljs +++ b/src/status_im/ui/screens/chat/views.cljs @@ -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]) diff --git a/src/status_im/ui/screens/home/views/inner_item.cljs b/src/status_im/ui/screens/home/views/inner_item.cljs index 2441b27978..8e9df686e9 100644 --- a/src/status_im/ui/screens/home/views/inner_item.cljs +++ b/src/status_im/ui/screens/home/views/inner_item.cljs @@ -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]]]]])) diff --git a/src/status_im/ui/screens/profile/contact/views.cljs b/src/status_im/ui/screens/profile/contact/views.cljs index 519910c953..ae9135a0b4 100644 --- a/src/status_im/ui/screens/profile/contact/views.cljs +++ b/src/status_im/ui/screens/profile/contact/views.cljs @@ -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}]))) diff --git a/src/status_im/ui/screens/profile/tribute_to_talk/styles.cljs b/src/status_im/ui/screens/profile/tribute_to_talk/styles.cljs index f2c3a60dc6..9f615493c2 100644 --- a/src/status_im/ui/screens/profile/tribute_to_talk/styles.cljs +++ b/src/status_im/ui/screens/profile/tribute_to_talk/styles.cljs @@ -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 diff --git a/src/status_im/ui/screens/profile/tribute_to_talk/views.cljs b/src/status_im/ui/screens/profile/tribute_to_talk/views.cljs index 045065045f..646095fc67 100644 --- a/src/status_im/ui/screens/profile/tribute_to_talk/views.cljs +++ b/src/status_im/ui/screens/profile/tribute_to_talk/views.cljs @@ -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) diff --git a/src/status_im/ui/screens/profile/user/views.cljs b/src/status_im/ui/screens/profile/user/views.cljs index d020aff964..2cdb566876 100644 --- a/src/status_im/ui/screens/profile/user/views.cljs +++ b/src/status_im/ui/screens/profile/user/views.cljs @@ -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])]]]))) diff --git a/test/cljs/status_im/test/runner.cljs b/test/cljs/status_im/test/runner.cljs index 083ac3c3de..1921421253 100644 --- a/test/cljs/status_im/test/runner.cljs +++ b/test/cljs/status_im/test/runner.cljs @@ -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 diff --git a/test/cljs/status_im/test/tribute_to_talk/core.cljs b/test/cljs/status_im/test/tribute_to_talk/core.cljs new file mode 100644 index 0000000000..7f16d1b471 --- /dev/null +++ b/test/cljs/status_im/test/tribute_to_talk/core.cljs @@ -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"}))))) diff --git a/test/cljs/status_im/test/tribute_to_talk/db.cljs b/test/cljs/status_im/test/tribute_to_talk/db.cljs new file mode 100644 index 0000000000..f0eb820e9f --- /dev/null +++ b/test/cljs/status_im/test/tribute_to_talk/db.cljs @@ -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))))) diff --git a/test/cljs/status_im/test/tribute_to_talk/whitelist.cljs b/test/cljs/status_im/test/tribute_to_talk/whitelist.cljs new file mode 100644 index 0000000000..e3f5c5da85 --- /dev/null +++ b/test/cljs/status_im/test/tribute_to_talk/whitelist.cljs @@ -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))))) diff --git a/translations/en.json b/translations/en.json index 2ffed80191..c2b575035e 100644 --- a/translations/en.json +++ b/translations/en.json @@ -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", diff --git a/translations/ko.json b/translations/ko.json index f91d1b9749..9e19a02f79 100644 --- a/translations/ko.json +++ b/translations/ko.json @@ -644,7 +644,6 @@ "peers": "주변 노드 (Peers)", "pending": "대기 중", "permissions": "권한", - "personalized-message": "개인 메시지", "pfs": "PFS 활성화", "pfs-warning-content": "PFS 지원은 아직 테스트 중이므로 스테이터스는 오류에 대해 책임지지 않습니다. PFS를 활성화하면, 0.9.32 이상의 버전을 사용하는 유저들은 귀하의 개인, 공개 메시지를 읽을 수 있습니다. 아직 모든 메시지에 대한 PFS를 보장하지는 않으며, 귀하의 메시지를 암호화하지만 수신 메시지는 암호화 하지 않습니다.", "pfs-warning-title": "경고, 테스트 중인 기능임",