[ISSUE #3313] Migrated public chat creation flow

Signed-off-by: Julien Eluard <julien.eluard@gmail.com>
This commit is contained in:
Julien Eluard 2018-02-16 12:15:26 +01:00
parent cc6414c204
commit 33f1d8d26b
No known key found for this signature in database
GPG Key ID: 6FD7DB5437FCBEF6
25 changed files with 446 additions and 359 deletions

View File

@ -2,20 +2,19 @@
(:require [clojure.set :as set]
[cljs.core.async :as async]
[re-frame.core :as re-frame]
[status-im.utils.handlers :as handlers]
[status-im.utils.async :as async-utils]
[status-im.chat.models :as model]
[status-im.chat.console :as console-chat]
[status-im.chat.constants :as chat-const]
[status-im.data-store.contacts :as contacts-store]
[status-im.data-store.chats :as chats-store]
[status-im.data-store.contacts :as contacts-store]
[status-im.data-store.messages :as messages-store]
[status-im.data-store.pending-messages :as pending-messages-store]
[status-im.ui.screens.navigation :as navigation]
[status-im.constants :as constants]
[status-im.i18n :as i18n]
[status-im.protocol.core :as protocol]
[status-im.constants :as const]
[status-im.chat.models :as models]
[status-im.chat.console :as console]
[status-im.chat.constants :as chat.constants]
[status-im.data-store.chats :as chats]
[status-im.data-store.messages :as messages]
[status-im.data-store.pending-messages :as pending-messages]
[status-im.ui.components.list-selection :as list-selection]
[status-im.ui.screens.navigation :as navigation]
[status-im.utils.async :as utils.async]
[status-im.utils.handlers :as handlers]
status-im.chat.events.commands
status-im.chat.events.requests
status-im.chat.events.animation
@ -31,76 +30,76 @@
:stored-unviewed-messages
(fn [cofx _]
(assoc cofx :stored-unviewed-messages
(messages-store/get-unviewed (-> cofx :db :current-public-key)))))
(messages/get-unviewed (-> cofx :db :current-public-key)))))
(re-frame/reg-cofx
:get-stored-message
(fn [cofx _]
(assoc cofx :get-stored-message messages-store/get-by-id)))
(assoc cofx :get-stored-message messages/get-by-id)))
(re-frame/reg-cofx
:get-stored-messages
(fn [cofx _]
(assoc cofx :get-stored-messages messages-store/get-by-chat-id)))
(assoc cofx :get-stored-messages messages/get-by-chat-id)))
(re-frame/reg-cofx
:stored-message-ids
(fn [cofx _]
(assoc cofx :stored-message-ids (messages-store/get-stored-message-ids))))
(assoc cofx :stored-message-ids (messages/get-stored-message-ids))))
(re-frame/reg-cofx
:all-stored-chats
(fn [cofx _]
(assoc cofx :all-stored-chats (chats-store/get-all))))
(assoc cofx :all-stored-chats (chats/get-all))))
(re-frame/reg-cofx
:get-stored-chat
(fn [cofx _]
(assoc cofx :get-stored-chat chats-store/get-by-id)))
(assoc cofx :get-stored-chat chats/get-by-id)))
(re-frame/reg-cofx
:inactive-chat-ids
(fn [cofx _]
(assoc cofx :inactive-chat-ids (chats-store/get-inactive-ids))))
(assoc cofx :inactive-chat-ids (chats/get-inactive-ids))))
;;;; Effects
(def ^:private realm-queue (async-utils/task-queue 2000))
(def ^:private realm-queue (utils.async/task-queue 2000))
(re-frame/reg-fx
:update-message
(fn [message]
(async/go (async/>! realm-queue #(messages-store/update-message message)))))
(async/go (async/>! realm-queue #(messages/update-message message)))))
(re-frame/reg-fx
:save-message
(fn [message]
(async/go (async/>! realm-queue #(messages-store/save message)))))
(async/go (async/>! realm-queue #(messages/save message)))))
(re-frame/reg-fx
:delete-messages
(fn [chat-id]
(async/go (async/>! realm-queue #(messages-store/delete-by-chat-id chat-id)))))
(async/go (async/>! realm-queue #(messages/delete-by-chat-id chat-id)))))
(re-frame/reg-fx
:delete-pending-messages
(fn [chat-id]
(async/go (async/>! realm-queue #(pending-messages-store/delete-all-by-chat-id chat-id)))))
(async/go (async/>! realm-queue #(pending-messages/delete-all-by-chat-id chat-id)))))
(re-frame/reg-fx
:save-chat
(fn [chat]
(async/go (async/>! realm-queue #(chats-store/save chat)))))
(async/go (async/>! realm-queue #(chats/save chat)))))
(re-frame/reg-fx
:deactivate-chat
(fn [chat-id]
(async/go (async/>! realm-queue #(chats-store/set-inactive chat-id)))))
(async/go (async/>! realm-queue #(chats/set-inactive chat-id)))))
(re-frame/reg-fx
:delete-chat
(fn [chat-id]
(async/go (async/>! realm-queue #(chats-store/delete chat-id)))))
(async/go (async/>! realm-queue #(chats/delete chat-id)))))
(re-frame/reg-fx
:protocol-send-seen
@ -118,20 +117,20 @@
:set-chat-ui-props
[re-frame/trim-v]
(fn [db [kvs]]
(model/set-chat-ui-props db kvs)))
(models/set-chat-ui-props db kvs)))
(handlers/register-handler-db
:toggle-chat-ui-props
[re-frame/trim-v]
(fn [db [ui-element]]
(model/toggle-chat-ui-prop db ui-element)))
(models/toggle-chat-ui-prop db ui-element)))
(handlers/register-handler-db
:show-message-details
[re-frame/trim-v]
(fn [db [details]]
(model/set-chat-ui-props db {:show-bottom-info? true
:bottom-info details})))
(models/set-chat-ui-props db {:show-bottom-info? true
:bottom-info details})))
(def index-messages (partial into {} (map (juxt :message-id identity))))
@ -146,7 +145,7 @@
(update-in [:chats current-chat-id :messages] merge new-messages)
(update-in [:chats current-chat-id :not-loaded-message-ids] #(apply disj % (keys new-messages)))
(assoc-in [:chats current-chat-id :all-loaded?]
(> const/default-number-of-messages (count new-messages))))}))))
(> constants/default-number-of-messages (count new-messages))))}))))
(handlers/register-handler-db
:message-appeared
@ -156,17 +155,17 @@
(defn init-console-chat
[{:keys [chats] :accounts/keys [current-account-id] :as db}]
(if (chats const/console-chat-id)
(if (chats constants/console-chat-id)
{:db db}
(cond-> {:db (-> db
(assoc :current-chat-id const/console-chat-id)
(update :chats assoc const/console-chat-id console-chat/chat))
:dispatch-n [[:add-contacts [console-chat/contact]]]
:save-chat console-chat/chat
:save-all-contacts [console-chat/contact]}
(assoc :current-chat-id constants/console-chat-id)
(update :chats assoc constants/console-chat-id console/chat))
:dispatch-n [[:add-contacts [console/contact]]]
:save-chat console/chat
:save-all-contacts [console/contact]}
(not current-account-id)
(update :dispatch-n concat [[:chat-received-message/add-when-commands-loaded console-chat/intro-message1]]))))
(update :dispatch-n concat [[:chat-received-message/add-when-commands-loaded console/intro-message1]]))))
(handlers/register-handler-fx
:init-console-chat
@ -238,8 +237,8 @@
:show-mnemonic
[re-frame/trim-v]
(fn [{:keys [db]} [mnemonic signing-phrase]]
(let [crazy-math-message? (contains? (get-in db [:chats chat-const/console-chat-id]) chat-const/crazy-math-message-id)
messages-events (->> (console-chat/passphrase-messages mnemonic signing-phrase crazy-math-message?)
(let [crazy-math-message? (contains? (get-in db [:chats chat.constants/console-chat-id]) chat.constants/crazy-math-message-id)
messages-events (->> (console/passphrase-messages mnemonic signing-phrase crazy-math-message?)
(mapv #(vector :chat-received-message/add %)))]
{:dispatch-n messages-events})))
@ -247,30 +246,28 @@
(handlers/register-handler-fx
:account-generation-message
(fn [{:keys [db]} _]
(when-not (contains? (get-in db [:chats chat-const/console-chat-id]) chat-const/passphrase-message-id)
{:dispatch [:chat-received-message/add console-chat/account-generation-message]})))
(when-not (contains? (get-in db [:chats chat.constants/console-chat-id]) chat.constants/passphrase-message-id)
{:dispatch [:chat-received-message/add console/account-generation-message]})))
(handlers/register-handler-fx
:move-to-internal-failure-message
(fn [{:keys [db]} _]
(when-not (contains? (get-in db [:chats chat-const/console-chat-id]) chat-const/move-to-internal-failure-message-id)
{:dispatch [:chat-received-message/add console-chat/move-to-internal-failure-message]})))
(when-not (contains? (get-in db [:chats chat.constants/console-chat-id]) chat.constants/move-to-internal-failure-message-id)
{:dispatch [:chat-received-message/add console/move-to-internal-failure-message]})))
(handlers/register-handler-fx
:browse-link-from-message
(fn [{{:contacts/keys [contacts]} :db} [_ link]]
(fn [_ [_ link]]
{:browse link}))
(defn preload-chat-data
"Takes coeffects map and chat-id, returns effects necessary when navigating to chat"
[{:keys [db]} chat-id]
(let [messages (get-in db [:chats chat-id :messages])
chat-loaded-event (get-in db [:chats chat-id :chat-loaded-event])
jail-loaded? (get-in db [:contacts/contacts chat-id :jail-loaded?])]
(let [chat-loaded-event (get-in db [:chats chat-id :chat-loaded-event])]
(cond-> {:db (-> db
(assoc :current-chat-id chat-id)
(assoc-in [:chats chat-id :was-opened?] true)
(model/set-chat-ui-props {:validation-messages nil})
(models/set-chat-ui-props {:validation-messages nil})
(update-in [:chats chat-id] dissoc :chat-loaded-event))}
chat-loaded-event
@ -282,7 +279,7 @@
(fn [{:keys [db] :as cofx} [chat-id event]]
(if (get (:chats db) chat-id)
{:db (assoc-in db [:chats chat-id :chat-loaded-event] event)}
(-> (model/add-chat cofx chat-id) ; chat not created yet, we have to create it
(-> (models/add-chat cofx chat-id) ; chat not created yet, we have to create it
(assoc-in [:db :chats chat-id :chat-loaded-event] event)))))
;; TODO(janherich): remove this unnecessary event in the future (only model function `add-chat` will stay)
@ -290,7 +287,7 @@
:add-chat
[(re-frame/inject-cofx :get-stored-chat) re-frame/trim-v]
(fn [cofx [chat-id chat-props]]
(model/add-chat cofx chat-id chat-props)))
(models/add-chat cofx chat-id chat-props)))
(defn navigate-to-chat
"Takes coeffects map and chat-id, returns effects necessary for navigation and preloading data"
@ -316,7 +313,7 @@
(when (not= (:current-public-key db) contact-id) ; don't allow to open chat with yourself
(if (get (:chats db) contact-id)
(navigate-to-chat cofx contact-id navigation-replace?) ; existing chat, just preload and displey
(let [add-chat-fx (model/add-chat cofx contact-id)] ; new chat, create before preload & display
(let [add-chat-fx (models/add-chat cofx contact-id)] ; new chat, create before preload & display
(merge add-chat-fx
(navigate-to-chat (assoc cofx :db (:db add-chat-fx))
contact-id
@ -327,7 +324,7 @@
:update-chat!
[re-frame/trim-v]
(fn [cofx [chat]]
(model/update-chat cofx chat)))
(models/update-chat cofx chat)))
(handlers/register-handler-fx
:remove-chat
@ -344,3 +341,19 @@
(assoc :delete-chat chat-id)
(not debug?)
(assoc :deactivate-chat chat-id)))))
(handlers/register-handler-fx
:delete-chat
[re-frame/trim-v]
(fn [cofx [chat-id]]
(-> (models/remove-chat cofx chat-id)
(update :db navigation/replace-view :home))))
(handlers/register-handler-fx
:delete-chat?
[re-frame/trim-v]
(fn [_ [chat-id group?]]
{:show-confirmation {:title (i18n/label :t/delete-confirmation)
:content (i18n/label (if group? :t/delete-group-chat-confirmation :t/delete-chat-confirmation))
:confirm-button-text (i18n/label :t/delete)
:on-accept #(re-frame/dispatch [:delete-chat chat-id])}}))

View File

@ -1,24 +1,19 @@
(ns status-im.chat.handlers
(:require [re-frame.core :refer [after dispatch reg-fx]]
[clojure.string :as string]
[status-im.ui.components.styles :refer [default-chat-color]]
[status-im.chat.constants :as chat-consts]
[status-im.chat.models :as chat]
(:require [clojure.string :as string]
[re-frame.core :as re-frame]
[status-im.chat.models :as models]
[status-im.i18n :as i18n]
[status-im.protocol.core :as protocol]
[status-im.data-store.messages :as messages]
[status-im.constants :refer [text-content-type
content-type-command
content-type-command-request
console-chat-id]]
[status-im.ui.components.styles :as components.styles]
[status-im.utils.handlers :as handlers]
[status-im.utils.random :as random]
[status-im.utils.handlers :refer [register-handler register-handler-fx] :as u]
status-im.chat.events))
(register-handler
(handlers/register-handler
:leave-group-chat
;; todo order of operations tbd
(after (fn [_ _] (dispatch [:navigation-replace :home])))
(u/side-effect!
(re-frame/after (fn [_ _] (re-frame/dispatch [:navigation-replace :home])))
(handlers/side-effect!
(fn [{:keys [web3 current-chat-id chats current-public-key]} _]
(let [{:keys [public-key private-key public?]} (chats current-chat-id)]
(protocol/stop-watching-group!
@ -32,31 +27,39 @@
:private private-key}
:message {:from current-public-key
:message-id (random/id)}})))
(dispatch [:remove-chat current-chat-id]))))
(re-frame/dispatch [:remove-chat current-chat-id]))))
(register-handler :update-group-message
(u/side-effect!
(handlers/register-handler-fx
:leave-group-chat?
(fn []
{:show-confirmation {:title (i18n/label :t/leave-confirmation)
:content (i18n/label :t/leave-group-chat-confirmation)
:confirm-button-text (i18n/label :t/leave)
:on-accept #(re-frame/dispatch [:leave-group-chat])}}))
(handlers/register-handler :update-group-message
(handlers/side-effect!
(fn [{:keys [current-public-key web3 chats]}
[_ {:keys [from]
{:keys [group-id keypair timestamp]} :payload}]]
(let [{:keys [private public]} keypair
{:keys [group-admin is-active] :as chat} (get chats group-id)]
(when (and (= from group-admin)
(when (and (= from group-admin
(or (nil? chat)
(chat/new-update? chat timestamp)))
(dispatch [:update-chat! {:chat-id group-id
:public-key public
:private-key private
:updated-at timestamp}])
(models/new-update? chat timestamp))))
(re-frame/dispatch [:update-chat! {:chat-id group-id
:public-key public
:private-key private
:updated-at timestamp}])
(when is-active
(protocol/start-watching-group!
{:web3 web3
:group-id group-id
:identity current-public-key
:keypair keypair
:callback #(dispatch [:incoming-message %1 %2])})))))))
:callback #(re-frame/dispatch [:incoming-message %1 %2])})))))))
(reg-fx
(re-frame/reg-fx
::start-watching-group
(fn [{:keys [group-id web3 current-public-key keypair]}]
(protocol/start-watching-group!
@ -64,15 +67,15 @@
:group-id group-id
:identity current-public-key
:keypair keypair
:callback #(dispatch [:incoming-message %1 %2])})))
:callback #(re-frame/dispatch [:incoming-message %1 %2])})))
(register-handler-fx
(handlers/register-handler-fx
:create-new-public-chat
(fn [{:keys [db]} [_ topic]]
(let [exists? (boolean (get-in db [:chats topic]))
chat {:chat-id topic
:name topic
:color default-chat-color
:color components.styles/default-chat-color
:group-chat true
:public? true
:is-active true
@ -86,7 +89,7 @@
{:dispatch-n [[:navigate-to-clean :home]
[:navigate-to-chat topic]]}))))
(reg-fx
(re-frame/reg-fx
::start-listen-group
(fn [{:keys [new-chat web3 current-public-key]}]
(let [{:keys [chat-id public-key private-key contacts name]} new-chat
@ -108,7 +111,7 @@
:identity current-public-key
:keypair {:public public-key
:private private-key}
:callback #(dispatch [:incoming-message %1 %2])}))))
:callback #(re-frame/dispatch [:incoming-message %1 %2])}))))
(defn group-name-from-contacts [contacts selected-contacts username]
(->> (select-keys contacts selected-contacts)
@ -130,14 +133,14 @@
:public-key public
:private-key private
:name chat-name
:color default-chat-color
:color components.styles/default-chat-color
:group-chat true
:group-admin current-public-key
:is-active true
:timestamp (random/timestamp)
:contacts selected-contacts'}))
(register-handler-fx
(handlers/register-handler-fx
:create-new-group-chat-and-open
(fn [{:keys [db]} [_ group-name]]
(let [new-chat (prepare-group-chat (select-keys db [:group/selected-contacts :current-public-key :username
@ -152,7 +155,7 @@
:dispatch-n [[:navigate-to-clean :home]
[:navigate-to-chat (:chat-id new-chat)]]})))
(register-handler-fx
(handlers/register-handler-fx
:group-chat-invite-received
(fn [{{:keys [current-public-key] :as db} :db}
[_ {:keys [from]
@ -173,7 +176,7 @@
:timestamp timestamp
:is-active true}]
(when (or (nil? chat)
(chat/new-update? chat timestamp))
(models/new-update? chat timestamp))
{::start-watching-group (merge {:group-id group-id
:keypair keypair}
(select-keys db [:web3 :current-public-key]))
@ -181,7 +184,7 @@
[:update-chat! new-chat]
[:add-chat group-id new-chat])})))))
(register-handler-fx
(handlers/register-handler-fx
:show-profile
(fn [{db :db} [_ identity]]
{:db (assoc db :contacts/identity identity)

View File

@ -13,7 +13,7 @@
(update-in db [:chat-ui-props current-chat-id ui-element] not))
(defn- create-new-chat
[{:keys [db now] :as cofx} chat-id]
[{:keys [db now]} chat-id]
(let [name (get-in db [:contacts/contacts chat-id :name])]
{:chat-id chat-id
:name (or name (gfycat/generate-gfy chat-id))
@ -28,7 +28,7 @@
([cofx chat-id]
(add-chat cofx chat-id {}))
([{:keys [db get-stored-chat] :as cofx} chat-id chat-props]
(let [{:keys [chats deleted-chats]} db
(let [{:keys [deleted-chats]} db
new-chat (merge (if (get deleted-chats chat-id)
(assoc (get-stored-chat chat-id) :is-active true)
(create-new-chat cofx chat-id))
@ -64,3 +64,16 @@
(and (> timestamp added-to-at)
(> timestamp removed-at)
(> timestamp removed-from-at)))
(defn remove-chat [{:keys [db]} chat-id]
(let [{:keys [chat-id group-chat debug?]} (get-in db [:chats chat-id])]
(cond-> {:db (-> db
(update :chats dissoc chat-id)
(update :deleted-chats (fnil conj #{}) chat-id))
:delete-pending-messages chat-id}
(or group-chat debug?)
(assoc :delete-messages chat-id)
debug?
(assoc :delete-chat chat-id)
(not debug?)
(assoc :deactivate-chat chat-id))))

View File

@ -1,19 +0,0 @@
(ns status-im.chat.new-public-chat.db
(:require [cljs.spec.alpha :as spec]
status-im.utils.db
[status-im.constants :refer [console-chat-id]]
[clojure.string :as string]
[status-im.utils.homoglyph :as utils]))
(defn legal-name? [username]
(let [username (some-> username string/trim)]
(not (utils/matches username console-chat-id))))
(spec/def ::legal-name legal-name?)
(spec/def ::name (spec/and :global/not-empty-string
::legal-name))
(spec/def ::topic (spec/and :global/not-empty-string
::legal-name
(partial re-matches #"[a-z0-9\-]+")))

View File

@ -1,46 +0,0 @@
(ns status-im.chat.new-public-chat.styles
(:require-macros [status-im.utils.styles :refer [defstyle]])
(:require [status-im.ui.components.styles :as common]))
(def group-chat-name-input
{:font-size 17
:padding-bottom 0
:letter-spacing -0.2
:color common/text1-color})
(defstyle group-chat-topic-input
{:font-size 14
:line-height 16
:color common/text1-color
:padding-left 13
:ios {:padding-bottom 0}})
(defstyle topic-hash-style
{:width 10
:position :absolute
:android {:top 8 :left 3}
:ios {:top 6 :left 3}})
(def topic-hash
(merge group-chat-name-input
topic-hash-style))
(def group-chat-name-wrapper
{:padding-top 0
:height 40
:padding-bottom 0})
(def group-container
{:flex 1
:flex-direction :column
:background-color common/color-white})
(def chat-name-container
{:padding-left 16
:margin-top 10})
(defstyle members-text
{:color common/color-gray4
:ios {:letter-spacing -0.2
:font-size 16}
:android {:font-size 14}})

View File

@ -1,57 +0,0 @@
(ns status-im.chat.new-public-chat.view
(:require-macros [status-im.utils.views :refer [defview letsubs]])
(:require [re-frame.core :refer [dispatch]]
status-im.utils.db
[status-im.ui.components.react :as react :refer [text]]
[status-im.ui.components.text-field.view :refer [text-field]]
[status-im.ui.components.styles :as components.styles]
[status-im.ui.components.status-bar.view :refer [status-bar]]
[status-im.ui.components.toolbar.view :as toolbar]
[status-im.chat.new-public-chat.styles :as styles]
[status-im.chat.new-public-chat.db :as v]
[status-im.i18n :refer [label]]
[cljs.spec.alpha :as spec]))
(defview new-public-chat-toolbar []
(letsubs [topic [:get :public-group-topic]]
(let [create-btn-enabled? (spec/valid? ::v/topic topic)]
[react/view
[status-bar]
[toolbar/toolbar {}
toolbar/default-nav-back
[toolbar/content-title (label :t/new-public-group-chat)]
[toolbar/actions [{:icon :icons/ok
:icon-opts {:color (if create-btn-enabled? components.styles/color-blue4 components.styles/color-gray11)}
:handler (when create-btn-enabled?
#(dispatch [:create-new-public-chat topic]))}]]]])))
(defview chat-name-input []
(letsubs [topic [:get :public-group-topic]]
[react/view
[text-field
{:error (cond
(not (spec/valid? :global/not-empty-string topic))
(label :t/empty-topic)
(not (spec/valid? ::v/topic topic))
(label :t/topic-format))
:wrapper-style styles/group-chat-name-wrapper
:error-color components.styles/color-blue
:line-color components.styles/color-gray4
:label-hidden? true
:input-style styles/group-chat-topic-input
:auto-focus true
:on-change-text #(dispatch [:set :public-group-topic %])
:value topic
:validator #(re-matches #"[a-z\-]*" %)
:auto-capitalize :none}]
[react/text {:style styles/topic-hash} "#"]]))
(defn new-public-chat []
[react/view styles/group-container
[new-public-chat-toolbar]
[react/view styles/chat-name-container
[react/text {:style styles/members-text
:font :medium}
(label :t/public-group-topic)]
[chat-name-input]]])

View File

@ -29,11 +29,11 @@
[react/text {:style style/add-contact-text}
(i18n/label :t/add-to-contacts)]]])))
(defn- on-options [chat-id chat-name group-chat?]
(list-selection/show {:title chat-name
:options (actions/actions group-chat? chat-id)}))
(defn- on-options [chat-id chat-name group-chat? public?]
(list-selection/show {:title (if public? (str "#" chat-name) chat-name)
:options (actions/actions group-chat? chat-id public?)}))
(defview chat-toolbar []
(defview chat-toolbar [public?]
(letsubs [accounts [:get-accounts]
creating? [:get :accounts/creating-account?]
{:keys [group-chat name chat-id]} [:get-current-chat]]
@ -48,7 +48,7 @@
[toolbar-content/toolbar-content-view]
[toolbar/actions [{:icon :icons/options
:icon-opts {:color :black}
:handler #(on-options chat-id name group-chat)}]]]
:handler #(on-options chat-id name group-chat public?)}]]]
[add-contact-bar]]))
(defmulti message-row (fn [{{:keys [type]} :row}] type))
@ -95,12 +95,12 @@
:keyboardShouldPersistTaps (if platform/android? :always :handled)}]))
(defview chat []
(letsubs [{:keys [group-chat input-text]} [:get-current-chat]
(letsubs [{:keys [group-chat public? input-text]} [:get-current-chat]
show-bottom-info? [:get-current-chat-ui-prop :show-bottom-info?]
layout-height [:get :layout-height]
current-view [:get :view-id]]
[react/view {:style style/chat-view}
[chat-toolbar]
[chat-toolbar public?]
(when (= :chat current-view)
[messages-view-animation
[messages-view group-chat]])

View File

@ -4,27 +4,37 @@
(defn view-profile [chat-id group?]
{:label (i18n/label :t/view-profile)
:action #(re-frame/dispatch [ (if group? :show-group-chat-profile :show-profile) chat-id])})
:action #(re-frame/dispatch [(if group? :show-group-chat-profile :show-profile) chat-id])})
(defn delete-chat [chat-id]
{:label (i18n/label :t/delete-chat)
;; TODO(jeluard) Refactor this or Jan will have an heart attack
:action #(do (re-frame/dispatch [:remove-chat chat-id])
(re-frame/dispatch [:navigation-replace :home]))})
(defn- clear-history []
{:label (i18n/label :t/clear-history)
:action #(re-frame/dispatch [:clear-history?])})
(defn leave-group-chat [chat-id]
{:label (i18n/label :t/leave-group-chat)
:action #(re-frame/dispatch [:leave-group-chat chat-id])})
(defn- delete-chat [chat-id group?]
{:label (i18n/label :t/delete-chat)
:action #(re-frame/dispatch [:delete-chat? chat-id group?])})
(defn- user-chat-actions [chat-id group?]
[(view-profile chat-id group?)
(delete-chat chat-id)])
(defn- leave-group-chat [chat-id public?]
{:label (i18n/label (if public? :t/leave-public-chat :t/leave-group-chat))
:action #(re-frame/dispatch [:leave-group-chat? chat-id])})
(defn- chat-actions [chat-id]
[(view-profile chat-id false)
(delete-chat chat-id false)])
(defn- group-chat-actions [chat-id]
(into (user-chat-actions chat-id true)
[(leave-group-chat chat-id)]))
[(view-profile chat-id true)
(clear-history)
(delete-chat chat-id true)
(leave-group-chat chat-id false)])
(defn actions [group-chat? chat-id]
(if group-chat?
(group-chat-actions chat-id)
(user-chat-actions chat-id false)))
(defn- public-chat-actions [chat-id]
[(clear-history)
(delete-chat chat-id true)
(leave-group-chat chat-id true)])
(defn actions [group-chat? chat-id public?]
(cond
public? (public-chat-actions chat-id)
group-chat? (group-chat-actions chat-id)
:else (chat-actions chat-id)))

View File

@ -393,7 +393,7 @@
(handlers/register-handler-fx
:check-sync
(fn [{{:keys [web3] :as db} :db} _]
(fn [{{:keys [web3]} :db} _]
{::web3-get-syncing web3
:dispatch-later [{:ms 10000 :dispatch [:check-sync]}]}))
@ -547,7 +547,7 @@
[re-frame/trim-v]
(fn [{{:keys [web3 contacts/contacts] :as db} :db}
[{:keys [from]
{:keys [group-id timestamp message-id] :as payload} :payload}]]
{:keys [group-id timestamp message-id]} :payload}]]
(let [chat (get-in db [:chats group-id])
new-update? (chat/new-update? chat timestamp)]
(when new-update?

View File

@ -151,8 +151,11 @@
:chats "Chats"
:delete-chat "Delete chat"
:group-chat "Group chat"
:delete-chat-confirmation "Are you sure you want to delete this chat?"
:delete-group-chat-confirmation "Are you sure you want to delete this group chat?"
:new-group-chat "New group chat"
:new-public-group-chat "Join public chat"
:public-chat "Public chat"
:edit-chats "Edit chats"
:search-chats "Search chats"
:empty-topic "Empty topic"
@ -204,10 +207,18 @@
:save "Save"
:create "Create"
:delete "Delete"
:delete-confirmation "Delete?"
:leave "Leave"
:leave-confirmation "Leave?"
:clear "Clear"
:clear-history "Clear history"
:clear-history-confirmation "Clear history?"
:clear-group-history-confirmation "Are you sure you want to clear history in this group chat?"
:mute-notifications "Mute notifications"
:leave-chat "Leave chat"
:leave-group-chat-confirmation "Are you sure you want to leave this group?"
:leave-group-chat "Leave group chat"
:leave-public-chat "Leave public chat"
:chat-settings "Chat settings"
:edit "Edit"
:add-members "Add members"
@ -415,4 +426,5 @@
:browser "Browser"
:enter-dapp-url "Enter a ÐApp URL"
:dapp "ÐApp"
:selected "Selected"
:selected-dapps "Selected ÐApps"})

View File

@ -17,12 +17,6 @@
:height 40
:border-radius 20}))
(defn default-chat-icon-menu-item [color]
(merge (default-chat-icon color)
{:width 24
:height 24
:border-radius 12}))
(defn default-chat-icon-profile [color]
(merge (default-chat-icon color)
{:width 64
@ -144,13 +138,6 @@
(def online-dot-left (merge online-dot {:left 2.8}))
(def online-dot-right (merge online-dot {:left 7.2}))
(def photo-pencil
{:margin-left 8
:margin-right 2
:margin-top 6
:font-size 12
:color :white})
(def online-dot-menu-item
(merge online-dot
{:top 4

View File

@ -12,3 +12,5 @@
(def blue "#4360df") ;; Used as main wallet color
(def red "#ff2d55") ;; Used to highlight errors or "dangerous" actions
(def text-light-gray "#212121") ;; Used for labels (home items)
(def text black)

View File

@ -44,9 +44,7 @@
(def icon-wrapper-size (+ icon-size (* 2 8)))
(def item-icon-wrapper
{:width icon-wrapper-size
:height icon-wrapper-size
:align-items :center
{:align-items :center
:justify-content :center})
(def item-icon
@ -68,7 +66,7 @@
(def right-item-wrapper
{:justify-content :center
:margin-right horizontal-margin})
:margin-right 12})
(def base-separator
{:height 1

View File

@ -83,8 +83,7 @@
[checkbox/checkbox props]])
(def item-icon-forward
[item-icon {:style styles/item-icon
:icon :icons/forward
[item-icon {:icon :icons/forward
:icon-opts {:color colors/white-light-transparent}}])
(defn- wrap-render-fn [f]

View File

@ -35,18 +35,18 @@
(let [duration (:label-animation-duration config)
animation (animation/parallel (into []
(concat
(when (or (nil? value-blank?) value-blank?)
[(animation/timing top {:toValue to-top
:duration duration})
(animation/timing font-size {:toValue to-font-size
:duration duration})])
[(animation/timing line-width {:toValue to-line-width
:duration duration})
(animation/timing line-height {:toValue to-line-height
:duration duration})])))]
(when (or (nil? value-blank?) value-blank?)
[(animation/timing top {:toValue to-top
:duration duration})
(animation/timing font-size {:toValue to-font-size
:duration duration})])
[(animation/timing line-width {:toValue to-line-width
:duration duration})
(animation/timing line-height {:toValue to-line-height
:duration duration})])))]
(animation/start animation (fn [arg]
(when (.-finished arg)
(log/debug "Field animation finished"))))))
(when (.-finished arg)
(log/debug "Field animation finished"))))))
; Invoked once before the component is mounted. The return value will be used
; as the initial value of this.state.
@ -65,11 +65,11 @@
(defn component-will-mount [component]
(let [{:keys [value]} (reagent/props component)
data {:label-top (animation/create-value (if (string/blank? value)
(:label-bottom config)
(:label-top config)))
(:label-bottom config)
(:label-top config)))
:label-font-size (animation/create-value (if (string/blank? value)
(:label-font-large config)
(:label-font-small config)))
(:label-font-large config)
(:label-font-small config)))
:float-label? (if (string/blank? value) false true)}]
;(log/debug "component-will-mount")
(reagent/set-state component data)))
@ -78,7 +78,7 @@
(do
(log/debug "input focused")
(reagent/set-state component {:has-focus true
:float-label? true})
:float-label? true})
(field-animation (merge animation
{:to-line-width (:max-line-width (reagent/state component))}))
(when onFocus (onFocus))))
@ -86,7 +86,7 @@
(defn on-input-blur [{:keys [component value animation onBlur]}]
(log/debug "Input blurred")
(reagent/set-state component {:has-focus false
:float-label? (if (string/blank? value) false true)})
:float-label? (if (string/blank? value) false true)})
(field-animation animation (string/blank? value))
(when onBlur (onBlur)))
@ -107,7 +107,7 @@
max-length]} (reagent/state component)
{:keys [wrapper-style input-style label-hidden? line-color focus-line-color focus-line-height
secure-text-entry label-color error-color error label value on-focus on-blur validator
auto-focus on-change-text on-change on-end-editing editable placeholder
auto-focus on-change-text on-change on-end-editing on-submit-editing editable placeholder
placeholder-text-color auto-capitalize multiline number-of-lines]}
(merge default-props (reagent/props component))
valid-value (or valid-value "")
@ -120,52 +120,52 @@
(when-not label-hidden?
[react/animated-text {:style (styles/label label-top label-font-size label-color)} label])
[react/text-input {:ref #(reset! input-ref %)
:style (merge styles/text-input input-style)
:placeholder (or placeholder "")
:placeholder-text-color placeholder-text-color
:editable editable
:multiline multiline
:number-of-lines number-of-lines
:secure-text-entry secure-text-entry
:auto-capitalize auto-capitalize
:on-focus #(on-input-focus {:component component
:animation {:top label-top
:to-top (:label-top config)
:font-size label-font-size
:to-font-size (:label-font-small config)
:line-width line-width
:line-height line-height
:to-line-height focus-line-height}
:onFocus on-focus})
:on-blur #(on-input-blur {:component component
:value (or current-value value)
:animation {:top label-top
:to-top (:label-bottom config)
:font-size label-font-size
:to-font-size (:label-font-large config)
:line-width line-width
:line-height line-height
:to-line-width 0
:to-line-height 1}
:onBlur on-blur})
:on-change-text (fn [text]
(reagent/set-state component {:current-value text})
(if (or (not validator) (validator text))
(do
(reagent/set-state component {:valid-value text
:temp-value nil})
(on-change-text text))
(reagent/set-state component {:temp-value valid-value
:max-length (count valid-value)})))
:on-change on-change
:default-value value
:value temp-value
:max-length max-length
:on-submit-editing #(.blur @input-ref)
:on-end-editing (when on-end-editing on-end-editing)
:auto-focus (true? auto-focus)}]
:style (merge styles/text-input input-style)
:placeholder (or placeholder "")
:placeholder-text-color placeholder-text-color
:editable editable
:multiline multiline
:number-of-lines number-of-lines
:secure-text-entry secure-text-entry
:auto-capitalize auto-capitalize
:on-focus #(on-input-focus {:component component
:animation {:top label-top
:to-top (:label-top config)
:font-size label-font-size
:to-font-size (:label-font-small config)
:line-width line-width
:line-height line-height
:to-line-height focus-line-height}
:onFocus on-focus})
:on-blur #(on-input-blur {:component component
:value (or current-value value)
:animation {:top label-top
:to-top (:label-bottom config)
:font-size label-font-size
:to-font-size (:label-font-large config)
:line-width line-width
:line-height line-height
:to-line-width 0
:to-line-height 1}
:onBlur on-blur})
:on-change-text (fn [text]
(reagent/set-state component {:current-value text})
(if (or (not validator) (validator text))
(do
(reagent/set-state component {:valid-value text
:temp-value nil})
(on-change-text text))
(reagent/set-state component {:temp-value valid-value
:max-length (count valid-value)})))
:on-change on-change
:default-value value
:value temp-value
:max-length max-length
:on-submit-editing #(do (.blur @input-ref) (when on-submit-editing (on-submit-editing)))
:on-end-editing (when on-end-editing on-end-editing)
:auto-focus (true? auto-focus)}]
[react/view {:style (styles/underline-container line-color)
:onLayout #(reagent/set-state component {:max-line-width (get-width %)})}
:onLayout #(reagent/set-state component {:max-line-width (get-width %)})}
[react/animated-view {:style (styles/underline focus-line-color line-width line-height)}]]
[react/text {:style (styles/error-text error-color)} error]]))
@ -178,6 +178,6 @@
(let [{:keys [temp-value]} (reagent/state comp)]
(when temp-value
(reagent/set-state comp {:temp-value nil
:max-length nil}))))}]
:max-length nil}))))}]
;(log/debug "Creating text-field component: " data)
(reagent/create-class component-data)))

View File

@ -0,0 +1,19 @@
(ns status-im.ui.screens.add-new.new-public-chat.db
(:require [clojure.string :as string]
[cljs.spec.alpha :as spec]
[status-im.constants :as constants]
[status-im.utils.homoglyph :as utils]
status-im.utils.db))
(defn- legal-name? [username]
(let [username (some-> username string/trim)]
(not (utils/matches username constants/console-chat-id))))
(spec/def ::legal-name legal-name?)
(spec/def ::name (spec/and :global/not-empty-string
::legal-name))
(spec/def ::topic (spec/and :global/not-empty-string
::legal-name
(partial re-matches #"[a-z0-9\-]+")))

View File

@ -0,0 +1,70 @@
(ns status-im.ui.screens.add-new.new-public-chat.styles
(:require-macros [status-im.utils.styles :refer [defstyle]])
(:require [status-im.ui.components.colors :as colors]
[status-im.ui.components.styles :as styles]))
(def group-chat-name-input
{:font-size 17
:padding-bottom 0
:letter-spacing -0.2
:color colors/text})
(defstyle group-chat-topic-input
{:font-size 14
:line-height 16
:color colors/text
:padding-left -1
:ios {:padding-bottom 0}})
(defstyle topic-hash-style
{:width 10
:position :absolute
:android {:top 8 :left 3}
:ios {:top 6 :left 3}})
(def topic-hash
(merge group-chat-name-input
{:margin-left 14}))
(def group-chat-name-wrapper
{:flex 1
:padding-top 0
:height 40
:padding-bottom 0})
(def group-container
{:flex 1
:flex-direction :column})
(def chat-name-container
{:margin-top 10})
(defstyle members-text
{:color colors/gray
:ios {:letter-spacing -0.2
:font-size 16}
:android {:font-size 14}})
(def section-title
(merge members-text
{:padding-horizontal 16}))
(def input-container
{:flex-direction :row
:align-items :center
:border-radius styles/border-radius
:height 52
:background-color colors/gray-light
:margin-top 24})
(def public-chat-icon
{:background-color colors/blue
:border-radius 50
:width 40
:height 40
:align-items :center
:justify-content :center})
(def public-chat-icon-symbol
{:font-size 20
:color colors/white})

View File

@ -0,0 +1,75 @@
(ns status-im.ui.screens.add-new.new-public-chat.view
(:require-macros [status-im.utils.views :as views])
(:require [cljs.spec.alpha :as spec]
[re-frame.core :as re-frame]
[status-im.i18n :as i18n]
[status-im.ui.components.list.views :as list]
[status-im.ui.components.react :as react]
[status-im.ui.components.styles :as components.styles]
[status-im.ui.components.status-bar.view :as status-bar]
[status-im.ui.components.text-field.view :as text-field]
[status-im.ui.components.toolbar.view :as toolbar]
[status-im.ui.screens.add-new.styles :as add-new.styles]
[status-im.ui.screens.add-new.new-public-chat.styles :as styles]
[status-im.ui.screens.add-new.new-public-chat.db :as v]
status-im.utils.db))
(defn- topic-error-label [topic]
(cond
(not (spec/valid? :global/not-empty-string topic))
(i18n/label :t/empty-topic)
(not (spec/valid? ::v/topic topic))
(i18n/label :t/topic-format)))
(defn- chat-name-input [topic]
[react/view (merge add-new.styles/input-container {:margin-top 8})
[react/text {:style styles/topic-hash} "#"]
[text-field/text-field
{:wrapper-style styles/group-chat-name-wrapper
:line-color components.styles/color-transparent
:focus-line-color components.styles/color-transparent
:label-hidden? true
:input-style styles/group-chat-topic-input
:on-change-text #(re-frame/dispatch [:set :public-group-topic %])
:on-submit-editing #(when topic (re-frame/dispatch [:create-new-public-chat topic]))
:value topic
:validator #(re-matches #"[a-z\-]+" %)
:auto-capitalize :none}]])
(defn- public-chat-icon [topic]
[react/view styles/public-chat-icon
[react/text {:uppercase? true
:style styles/public-chat-icon-symbol}
(first topic)]])
(defn- render-topic [topic]
[react/touchable-highlight {:on-press #(re-frame/dispatch [:create-new-public-chat topic])}
[react/view
[list/item
[public-chat-icon topic]
[list/item-primary-only
topic]
[list/item-icon {:icon :icons/forward
:icon-opts {:color :gray}}]]]])
(def default-public-chats ["status" "openbounty" "ethereum"])
(views/defview new-public-chat []
(views/letsubs [topic [:get :public-group-topic]]
[react/view styles/group-container
[status-bar/status-bar]
[toolbar/simple-toolbar
(i18n/label :t/public-chat)]
[react/view styles/chat-name-container
[react/text {:style styles/section-title
:font :medium}
(i18n/label :t/public-group-topic)]
[chat-name-input topic]]
[react/view styles/chat-name-container
[react/text {:style styles/section-title
:font :medium}
(i18n/label :t/selected)]]
[list/flat-list {:data default-public-chats
:render-fn render-topic
:default-separator? true}]]))

View File

@ -8,14 +8,14 @@
status-im.ui.screens.qr-scanner.db
status-im.ui.screens.group.db
status-im.chat.specs
status-im.chat.new-public-chat.db
status-im.commands.specs
status-im.ui.screens.profile.db
status-im.ui.screens.discover.db
status-im.ui.screens.network-settings.db
status-im.ui.screens.offline-messaging-settings.db
status-im.ui.screens.browser.db
status-im.ui.screens.add-new.db))
status-im.ui.screens.add-new.db
status-im.ui.screens.add-new.new-public-chat.db))
;; initial state of app-db
(def app-db {:current-public-key nil

View File

@ -1,28 +1,27 @@
(ns status-im.ui.screens.group.chat-settings.events
(:require [re-frame.core :refer [dispatch reg-fx]]
[status-im.utils.handlers :refer [register-handler-fx]]
[status-im.protocol.core :as protocol]
[status-im.utils.random :as random]
[status-im.chat.handlers :as chat-events]
[status-im.data-store.messages :as messages]
(:require [re-frame.core :as re-frame]
[status-im.constants :as constants]
[status-im.data-store.chats :as chats]
[status-im.constants :refer [text-content-type]]))
;;;; COFX
[status-im.data-store.contacts :as contacts]
[status-im.data-store.messages :as messages]
[status-im.i18n :as i18n]
[status-im.protocol.core :as protocol]
[status-im.utils.handlers :as handlers]
[status-im.utils.random :as random]))
;;;; FX
(reg-fx
(re-frame/reg-fx
::save-chat-property
(fn [[current-chat-id property-name value]]
(chats/save-property current-chat-id property-name value)))
(reg-fx
(re-frame/reg-fx
::add-members-to-chat
(fn [{:keys [current-chat-id selected-participants]}]
(chats/add-contacts current-chat-id selected-participants)))
(reg-fx
(re-frame/reg-fx
::remove-members-from-chat
(fn [[current-chat-id participants]]
(chats/remove-contacts current-chat-id participants)))
@ -31,7 +30,7 @@
{:from "system"
:message-id message-id
:content content
:content-type text-content-type})
:content-type constants/text-content-type})
(defn removed-participant-message [chat-id identity contact-name]
(let [message-text (str "You've removed " (or contact-name identity))]
@ -39,14 +38,14 @@
(assoc :chat-id chat-id)
(messages/save))))
(reg-fx
(re-frame/reg-fx
::create-removing-messages
(fn [{:keys [current-chat-id participants contacts/contacts]}]
(doseq [participant participants]
(let [contact-name (get-in contacts [participant :name])]
(removed-participant-message current-chat-id participant contact-name)))))
(reg-fx
(re-frame/reg-fx
::notify-about-new-members
(fn [{:keys [current-chat-id selected-participants
current-public-key chats web3]}]
@ -63,14 +62,14 @@
:admin current-public-key}
:message {:from current-public-key
:message-id (random/id)}}]
(dispatch [:update-chat! {:chat-id current-chat-id
:public-key public
:private-key private}])
(re-frame/dispatch [:update-chat! {:chat-id current-chat-id
:public-key public
:private-key private}])
(protocol/start-watching-group! {:web3 web3
:group-id current-chat-id
:identity current-public-key
:keypair new-keypair
:callback #(dispatch [:incoming-message %1 %2])})
:callback #(re-frame/dispatch [:incoming-message %1 %2])})
(protocol/invite-to-group!
(-> group-message
(assoc-in [:group :keypair] new-keypair)
@ -87,7 +86,7 @@
:message {:from current-public-key
:message-id (random/id)}})))))
(reg-fx
(re-frame/reg-fx
::notify-about-removing
(fn [{:keys [web3 current-chat-id participants chats current-public-key]}]
(let [{:keys [private public] :as new-keypair} (protocol/new-keypair!)
@ -99,9 +98,9 @@
identities (-> (map :identity contacts)
set
(clojure.set/difference participants))]
(dispatch [:update-chat! {:chat-id current-chat-id
:private-key private
:public-key public}])
(re-frame/dispatch [:update-chat! {:chat-id current-chat-id
:private-key private
:public-key public}])
(doseq [participant participants]
(let [id (random/id)]
(doseq [keypair [old-keypair new-keypair]]
@ -117,7 +116,7 @@
:group-id current-chat-id
:identity current-public-key
:keypair new-keypair
:callback #(dispatch [:incoming-message %1 %2])})
:callback #(re-frame/dispatch [:incoming-message %1 %2])})
(protocol/update-group!
{:web3 web3
:group {:id current-chat-id
@ -131,14 +130,14 @@
;;;; Handlers
(register-handler-fx
(handlers/register-handler-fx
:show-group-chat-profile
(fn [{db :db} [_ chat-id]]
{:db (assoc db :new-chat-name (get-in db [:chats chat-id :name])
:group/group-type :chat-group)
:dispatch [:navigate-to :chat-group-settings]}))
(register-handler-fx
(handlers/register-handler-fx
:add-new-group-chat-participants
(fn [{{:keys [current-chat-id selected-participants] :as db} :db} _]
(let [new-identities (map #(hash-map :identity %) selected-participants)]
@ -149,10 +148,10 @@
::notify-about-new-members (select-keys db [:current-chat-id :selected-participants
:current-public-key :chats :web3])})))
(defn remove-identities [collection identities]
(defn- remove-identities [collection identities]
(remove #(identities (:identity %)) collection))
(register-handler-fx
(handlers/register-handler-fx
:remove-group-chat-participants
(fn [{{:keys [current-chat-id] :as db} :db} [_ participants]]
{:db (update-in db [:chats current-chat-id :contacts] remove-identities participants)
@ -162,14 +161,22 @@
::create-removing-messages (merge {:participants participants}
(select-keys db [:current-chat-id :contacts/contacts]))}))
(register-handler-fx
(handlers/register-handler-fx
:set-chat-name
(fn [{{:keys [current-chat-id new-chat-name] :as db} :db} _]
{:db (assoc-in db [:chats current-chat-id :name] new-chat-name)
::save-chat-property [current-chat-id :name new-chat-name]}))
(register-handler-fx
(handlers/register-handler-fx
:clear-history
(fn [{{:keys [current-chat-id] :as db} :db} _]
{:db (assoc-in db [:chats current-chat-id :messages] {})
:delete-messages current-chat-id}))
(handlers/register-handler-fx
:clear-history?
(fn [_ _]
{:show-confirmation {:title (i18n/label :t/clear-history-confirmation)
:content (i18n/label :t/clear-group-history-confirmation)
:confirm-button-text (i18n/label :t/clear)
:on-accept #(re-frame/dispatch [:clear-history])}}))

View File

@ -16,7 +16,7 @@
(pop stack))]
(conj stack' view-id)))
(defn- replace-view [db view-id]
(defn replace-view [db view-id]
(-> db
(update :navigation-stack replace-top-element view-id)
(assoc :view-id view-id)))

View File

@ -10,7 +10,7 @@
(-> (accounts-events/account-update {:db db}
{:wnode wnode :last-updated now})
(merge {:dispatch [:navigate-to-clean :accounts]
:stop-whisper nil}))))
:stop-whisper nil}))))
(handlers/register-handler-fx
:connect-wnode

View File

@ -13,7 +13,7 @@
[status-im.chat.screen :refer [chat]]
[status-im.ui.screens.add-new.views :refer [add-new]]
[status-im.ui.screens.add-new.new-chat.views :refer [new-chat]]
[status-im.chat.new-public-chat.view :refer [new-public-chat]]
[status-im.ui.screens.add-new.new-public-chat.view :refer [new-public-chat]]
[status-im.ui.screens.contacts.contact-list-modal.views :refer [contact-list-modal]]

View File

@ -11,7 +11,6 @@
[status-im.utils.ethereum.core :as ethereum]
[status-im.utils.ethereum.tokens :as tokens]))
(defn- render-token [{:keys [symbol name icon]} visible-tokens]
[list/item
[list/item-image icon]

View File

@ -92,6 +92,8 @@
(def action
{:background-color colors/white-transparent
:width 40
:height 40
:border-radius 50})
(def action-label