diff --git a/resources/icons/desktop.svg b/resources/icons/desktop.svg
new file mode 100644
index 0000000000..865a48abf7
--- /dev/null
+++ b/resources/icons/desktop.svg
@@ -0,0 +1,3 @@
+
diff --git a/resources/icons/mobile.svg b/resources/icons/mobile.svg
new file mode 100644
index 0000000000..af01106d41
--- /dev/null
+++ b/resources/icons/mobile.svg
@@ -0,0 +1,4 @@
+
+
diff --git a/src/status_im/chat/models/message.cljs b/src/status_im/chat/models/message.cljs
index 9f1915498a..54f3ab6a73 100644
--- a/src/status_im/chat/models/message.cljs
+++ b/src/status_im/chat/models/message.cljs
@@ -50,6 +50,9 @@
(and (= constants/content-type-text content-type) (not emoji?))
(update :content message-content/enrich-content))))
+(defn system-message? [{:keys [message-type]}]
+ (= :system-message message-type))
+
(fx/defn re-index-message-groups
"Relative datemarks of message groups can get obsolete with passing time,
this function re-indexes them for given chat"
@@ -83,9 +86,9 @@
status)
:data-store/tx [(user-statuses-store/save-status-tx status)]}))
-(defn add-outgoing-status [{:keys [from message-type] :as message} current-public-key]
+(defn add-outgoing-status [{:keys [from] :as message} current-public-key]
(assoc message :outgoing (and (= from current-public-key)
- (not= :system-message message-type))))
+ (not (system-message? message)))))
(fx/defn add-message
[{:keys [db] :as cofx} batch? {:keys [chat-id message-id clock-value timestamp content from] :as message} current-chat?]
@@ -105,11 +108,12 @@
;; this will increase last-clock-value twice when sending our own messages
(update-in [:chats chat-id :last-clock-value] (partial utils.clocks/receive clock-value)))
(and (not current-chat?)
- (not= from (:current-public-key db)))
+ (not= from current-public-key))
(update-in [:chats chat-id :unviewed-messages] (fnil conj #{}) message-id))
:data-store/tx [(messages-store/save-message-tx prepared-message)]}
(when (and platform/desktop?
- (not batch?))
+ (not batch?)
+ (not (system-message? prepared-message)))
(chat-model/update-dock-badge-label))
(when-not batch?
(re-index-message-groups chat-id))
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 6373a064dd..be72368769 100644
--- a/src/status_im/data_store/realm/schemas/account/core.cljs
+++ b/src/status_im/data_store/realm/schemas/account/core.cljs
@@ -239,6 +239,19 @@
browser/v8
dapp-permissions/v9])
+(def v24 [chat/v8
+ transport/v7
+ contact/v3
+ message/v7
+ mailserver/v11
+ mailserver-topic/v1
+ user-status/v2
+ membership-update/v1
+ installation/v2
+ local-storage/v1
+ browser/v8
+ dapp-permissions/v9])
+
;; put schemas ordered by version
(def schemas [{:schema v1
:schemaVersion 1
@@ -308,4 +321,7 @@
:migration migrations/v22}
{:schema v23
:schemaVersion 23
- :migration migrations/v23}])
+ :migration migrations/v23}
+ {:schema v24
+ :schemaVersion 24
+ :migration migrations/v24}])
diff --git a/src/status_im/data_store/realm/schemas/account/migrations.cljs b/src/status_im/data_store/realm/schemas/account/migrations.cljs
index 595f80e828..e2108649df 100644
--- a/src/status_im/data_store/realm/schemas/account/migrations.cljs
+++ b/src/status_im/data_store/realm/schemas/account/migrations.cljs
@@ -153,3 +153,6 @@
new-user-status (aget new-user-statuses i)
whisper-identity (aget old-user-status "whisper-identity")]
(aset new-user-status "public-key" whisper-identity)))))
+
+(defn v24 [old-realm new-realm]
+ (log/debug "migrating v24 account database"))
diff --git a/src/status_im/events.cljs b/src/status_im/events.cljs
index 7fee3180ae..e4e80ad663 100644
--- a/src/status_im/events.cljs
+++ b/src/status_im/events.cljs
@@ -1115,7 +1115,7 @@
(handlers/register-handler-fx
:pairing.ui/synchronize-installation-pressed
(fn [cofx _]
- (pairing/send-installation-message cofx)))
+ (pairing/send-installation-messages cofx)))
(handlers/register-handler-fx
:pairing.ui/enable-installation-pressed
diff --git a/src/status_im/pairing/core.cljs b/src/status_im/pairing/core.cljs
index 169488f702..eb5d99e57d 100644
--- a/src/status_im/pairing/core.cljs
+++ b/src/status_im/pairing/core.cljs
@@ -1,16 +1,19 @@
(ns status-im.pairing.core
(:require [re-frame.core :as re-frame]
+ [clojure.string :as string]
[status-im.utils.fx :as fx]
[status-im.utils.config :as config]
[status-im.utils.platform :as utils.platform]
+ [status-im.accounts.db :as accounts.db]
[status-im.transport.message.protocol :as protocol]
[status-im.data-store.installations :as data-store.installations]
[status-im.native-module.core :as native-module]
- [status-im.accounts.db :as accounts.db]
[status-im.utils.identicon :as identicon]
[status-im.data-store.contacts :as data-store.contacts]
[status-im.transport.message.pairing :as transport.pairing]))
+(def contact-batch-n 4)
+
(defn- parse-response [response-js]
(-> response-js
js/JSON.parse
@@ -21,6 +24,12 @@
device-type utils.platform/os]
(protocol/send (transport.pairing/PairInstallation. installation-id device-type) nil cofx)))
+(defn has-paired-installations? [cofx]
+ (->>
+ (get-in cofx [:db :pairing/installations])
+ vals
+ (some :enabled?)))
+
(defn send-pair-installation [cofx payload]
(let [{:keys [web3]} (:db cofx)
current-public-key (accounts.db/current-public-key cofx)]
@@ -29,13 +38,9 @@
:payload payload}}))
(defn merge-contact [local remote]
- (let [[old-contact new-contact] (sort-by :last-updated [local remote])]
+ (let [[old-contact new-contact] (sort-by :last-updated [remote local])]
(-> local
(merge new-contact)
- (assoc :photo-path
- (or (:photo-path new-contact)
- (:photo-path old-contact)
- (identicon/identicon (:public-key local))))
(assoc :pending? (boolean
(and (:pending? local true)
(:pending? remote true)))))))
@@ -66,8 +71,15 @@
(defn sync-installation-messages [{:keys [db]}]
(let [contacts (:contacts/contacts db)]
(map
- (fn [[k v]] (transport.pairing/SyncInstallation. {k (dissoc v :photo-path)}))
- contacts)))
+ (fn [batch]
+ (let [contacts-to-sync (reduce (fn [acc {:keys [public-key] :as contact}]
+ (assoc acc public-key (dissoc contact :photo-path)))
+ {}
+ batch)]
+ (transport.pairing/SyncInstallation. contacts-to-sync)))
+ (partition-all contact-batch-n (->> contacts
+ vals
+ (remove :dapp?))))))
(defn enable [{:keys [db]} installation-id]
{:db (assoc-in db
@@ -119,29 +131,54 @@
:pairing/disable-installation
disable-installation!)
-(defn send-installation-message [cofx]
- ;; The message needs to be broken up in chunks as we hit the whisper size limit
+(fx/defn send-sync-installation [cofx payload]
(let [{:keys [web3]} (:db cofx)
- current-public-key (accounts.db/current-public-key cofx)
- sync-messages (sync-installation-messages cofx)]
+ current-public-key (accounts.db/current-public-key cofx)]
+
{:shh/send-direct-message
- (map #(hash-map :web3 web3
- :src current-public-key
- :dst current-public-key
- :payload %) sync-messages)}))
+ [{:web3 web3
+ :src current-public-key
+ :dst current-public-key
+ :payload payload}]}))
+
+(fx/defn send-installation-message-fx [cofx payload]
+ (let [dev-mode? (get-in cofx [:db :account/account :dev-mode?])]
+ (when (and (config/pairing-enabled? dev-mode?)
+ (has-paired-installations? cofx))
+ (protocol/send payload nil cofx))))
+
+(defn send-installation-messages [cofx]
+ ;; The message needs to be broken up in chunks as we hit the whisper size limit
+ (let [sync-messages (sync-installation-messages cofx)
+ sync-messages-fx (map send-installation-message-fx sync-messages)]
+ (apply fx/merge cofx sync-messages-fx)))
+
+(defn ensure-photo-path
+ "Make sure a photo path is there, generate otherwise"
+ [contacts]
+ (reduce-kv (fn [acc k {:keys [public-key photo-path] :as v}]
+ (assoc acc k
+ (assoc
+ v
+ :photo-path
+ (if (string/blank? photo-path)
+ (identicon/identicon public-key)
+ photo-path))))
+ {}
+ contacts))
(defn handle-sync-installation [{:keys [db] :as cofx} {:keys [contacts]} sender]
(let [dev-mode? (get-in db [:account/account :dev-mode?])]
(when (and (config/pairing-enabled? dev-mode?)
(= sender (accounts.db/current-public-key cofx)))
- (let [new-contacts (merge-contacts (:contacts/contacts db) contacts)]
+ (let [new-contacts (merge-contacts (:contacts/contacts db) (ensure-photo-path contacts))]
{:db (assoc db :contacts/contacts new-contacts)
:data-store/tx [(data-store.contacts/save-contacts-tx (vals new-contacts))]}))))
(defn handle-pair-installation [{:keys [db] :as cofx} {:keys [installation-id device-type]} timestamp sender]
(let [dev-mode? (get-in db [:account/account :dev-mode?])]
(when (and (config/pairing-enabled? dev-mode?)
- (= sender (get-in cofx [:db :current-public-key]))
+ (= sender (accounts.db/current-public-key cofx))
(not= (get-in db [:account/account :installation-id]) installation-id))
(let [installation {:installation-id installation-id
:device-type device-type
diff --git a/src/status_im/transport/impl/send.cljs b/src/status_im/transport/impl/send.cljs
index 68cec39c96..0663a8f78e 100644
--- a/src/status_im/transport/impl/send.cljs
+++ b/src/status_im/transport/impl/send.cljs
@@ -1,7 +1,11 @@
(ns status-im.transport.impl.send
(:require [status-im.group-chats.core :as group-chats]
+ [status-im.utils.fx :as fx]
[status-im.pairing.core :as pairing]
+ [status-im.data-store.transport :as transport-store]
+ [status-im.transport.db :as transport.db]
[status-im.transport.message.pairing :as transport.pairing]
+ [status-im.transport.message.contact :as transport.contact]
[status-im.transport.message.group-chat :as transport.group-chat]
[status-im.transport.message.protocol :as protocol]))
@@ -14,3 +18,45 @@
protocol/StatusMessage
(send [this _ cofx]
(pairing/send-pair-installation cofx this)))
+
+(extend-type transport.pairing/SyncInstallation
+ protocol/StatusMessage
+ (send [this _ cofx]
+ (pairing/send-sync-installation cofx this)))
+
+(extend-type transport.contact/ContactRequest
+ protocol/StatusMessage
+ (send [this chat-id cofx]
+ (let [sync-message (transport.pairing/SyncInstallation.
+ (select-keys
+ (get-in cofx [:db :contacts/contacts])
+ [chat-id]))]
+ (fx/merge cofx
+ (protocol/init-chat {:chat-id chat-id
+ :resend? "contact-request"})
+ (protocol/send-with-pubkey {:chat-id chat-id
+ :payload this
+ :success-event [:transport/contact-message-sent chat-id]})
+ (pairing/send-installation-message-fx sync-message)))))
+
+(extend-type transport.contact/ContactRequestConfirmed
+ protocol/StatusMessage
+ (send [this chat-id {:keys [db] :as cofx}]
+ (let [sync-message (transport.pairing/SyncInstallation.
+ (select-keys
+ (get-in cofx [:db :contacts/contacts])
+ [chat-id]))
+ success-event [:transport/contact-message-sent chat-id]
+ chat (get-in db [:transport/chats chat-id])
+ updated-chat (if chat
+ (assoc chat :resend? "contact-request-confirmation")
+ (transport.db/create-chat {:resend? "contact-request-confirmation"}))]
+ (fx/merge cofx
+ {:db (assoc-in db
+ [:transport/chats chat-id] updated-chat)
+ :data-store/tx [(transport-store/save-transport-tx {:chat-id chat-id
+ :chat updated-chat})]}
+ (protocol/send-with-pubkey {:chat-id chat-id
+ :payload this
+ :success-event success-event})
+ (pairing/send-installation-message-fx sync-message)))))
diff --git a/src/status_im/transport/message/contact.cljs b/src/status_im/transport/message/contact.cljs
index bbdb545c5b..17ed3338fa 100644
--- a/src/status_im/transport/message/contact.cljs
+++ b/src/status_im/transport/message/contact.cljs
@@ -8,33 +8,12 @@
(defrecord ContactRequest [name profile-image address fcm-token]
protocol/StatusMessage
- (send [this chat-id {:keys [db random-id-generator] :as cofx}]
- (fx/merge cofx
- (protocol/init-chat {:chat-id chat-id
- :resend? "contact-request"})
- (protocol/send-with-pubkey {:chat-id chat-id
- :payload this
- :success-event [:transport/contact-message-sent chat-id]})))
(validate [this]
(when (spec/valid? :message/contact-request this)
this)))
(defrecord ContactRequestConfirmed [name profile-image address fcm-token]
protocol/StatusMessage
- (send [this chat-id {:keys [db] :as cofx}]
- (let [success-event [:transport/contact-message-sent chat-id]
- chat (get-in db [:transport/chats chat-id])
- updated-chat (if chat
- (assoc chat :resend? "contact-request-confirmation")
- (transport.db/create-chat {:resend? "contact-request-confirmation"}))]
- (fx/merge cofx
- {:db (assoc-in db
- [:transport/chats chat-id] updated-chat)
- :data-store/tx [(transport-store/save-transport-tx {:chat-id chat-id
- :chat updated-chat})]}
- (protocol/send-with-pubkey {:chat-id chat-id
- :payload this
- :success-event success-event}))))
(validate [this]
(when (spec/valid? :message/contact-request-confirmed this)
this)))
@@ -79,15 +58,3 @@
{:shh/remove-filter {:chat-id chat-id
:filter filter}}))
-(defrecord NewContactKey [sym-key topic message]
- protocol/StatusMessage
- (send
- ;; no-op, we don't send NewContactKey anymore
- [this chat-id cofx])
- (receive
- ;;for compatibility with old clients, we only care about the message within
- [this chat-id _ timestamp {:keys [db] :as cofx}]
- (protocol/receive message chat-id chat-id timestamp cofx))
- (validate [this]
- (when (spec/valid? :message/new-contact-key this)
- this)))
diff --git a/src/status_im/transport/message/protocol.cljs b/src/status_im/transport/message/protocol.cljs
index ac47aecc89..018496e7f4 100644
--- a/src/status_im/transport/message/protocol.cljs
+++ b/src/status_im/transport/message/protocol.cljs
@@ -82,7 +82,7 @@
StatusMessage
(send [this chat-id cofx]
(let [dev-mode? (get-in cofx [:db :account/account :dev-mode?])
- current-public-key (get-in cofx [:db :current-public-key])
+ current-public-key (accounts.db/current-public-key cofx)
params {:chat-id chat-id
:payload this
:success-event [:transport/message-sent
diff --git a/src/status_im/transport/message/transit.cljs b/src/status_im/transport/message/transit.cljs
index 88c95a378d..8474502c64 100644
--- a/src/status_im/transport/message/transit.cljs
+++ b/src/status_im/transport/message/transit.cljs
@@ -21,11 +21,6 @@
;; The tag will determine which reader is used to recreate the clojure record
;; When migrating a particular record, it is important to use a different type and still handle the previous
;; gracefully for compatibility
-(deftype NewContactKeyHandler []
- Object
- (tag [this v] "c1")
- (rep [this {:keys [sym-key topic message]}]
- #js [sym-key topic message]))
(deftype ContactRequestHandler []
Object
@@ -102,8 +97,7 @@
(def writer (transit/writer :json
{:handlers
- {contact/NewContactKey (NewContactKeyHandler.)
- contact/ContactRequest (ContactRequestHandler.)
+ {contact/ContactRequest (ContactRequestHandler.)
contact/ContactRequestConfirmed (ContactRequestConfirmedHandler.)
contact/ContactUpdate (ContactUpdateHandler.)
protocol/Message (MessageHandler.)
@@ -145,9 +139,7 @@
;; Here we only need to call the record with the arguments parsed from the clojure datastructures
(def reader (transit/reader :json
{:handlers
- {"c1" (fn [[sym-key topic message]]
- (contact/NewContactKey. sym-key topic message))
- "c2" (fn [[name profile-image address fcm-token]]
+ {"c2" (fn [[name profile-image address fcm-token]]
(contact/ContactRequest. name profile-image address fcm-token))
"c3" (fn [[name profile-image address fcm-token]]
(contact/ContactRequestConfirmed. name profile-image address fcm-token))
diff --git a/src/status_im/ui/components/button/styles.cljs b/src/status_im/ui/components/button/styles.cljs
index 794269c1dc..dac1419e33 100644
--- a/src/status_im/ui/components/button/styles.cljs
+++ b/src/status_im/ui/components/button/styles.cljs
@@ -42,6 +42,9 @@
{:font-weight :normal
:color colors/white
:padding-horizontal 16
+ :desktop {:font-size 14
+ :padding-vertical 10
+ :letter-spacing 0.5}
:android {:font-size 14
:padding-vertical 10
:letter-spacing 0.5}
@@ -99,4 +102,4 @@
:flex 0.1
:padding-right 5
:align-items :center
- :justify-content :center})
\ No newline at end of file
+ :justify-content :center})
diff --git a/src/status_im/ui/components/contact/contact.cljs b/src/status_im/ui/components/contact/contact.cljs
index e9a29dd5b7..616e270e0f 100644
--- a/src/status_im/ui/components/contact/contact.cljs
+++ b/src/status_im/ui/components/contact/contact.cljs
@@ -58,7 +58,7 @@
[react/view styles/more-btn
[vector-icons/icon :icons/options {:accessibility-label :options}]]]]))]])
-(views/defview toogle-contact-view [{:keys [public-key] :as contact} selected-key on-toggle-handler]
+(views/defview toggle-contact-view [{:keys [public-key] :as contact} selected-key on-toggle-handler]
(views/letsubs [checked [selected-key public-key]]
[react/view {:accessibility-label :contact-item}
[list/list-item-with-checkbox
diff --git a/src/status_im/ui/components/icons/vector_icons.cljs b/src/status_im/ui/components/icons/vector_icons.cljs
index a4763ddc95..f5aa3bef2d 100644
--- a/src/status_im/ui/components/icons/vector_icons.cljs
+++ b/src/status_im/ui/components/icons/vector_icons.cljs
@@ -54,6 +54,7 @@
:icons/close (js/require "./resources/icons/close.svg")
:icons/copy-from (js/require "./resources/icons/copy_from.svg")
:icons/delete (js/require "./resources/icons/delete.svg")
+ :icons/desktop (js/require "./resources/icons/desktop.svg")
:icons/dots-horizontal (js/require "./resources/icons/dots_horizontal.svg")
:icons/dots-vertical (js/require "./resources/icons/dots_vertical.svg")
:icons/exclamation-mark (js/require "./resources/icons/exclamation_mark.svg")
@@ -67,6 +68,7 @@
:icons/in-contacts (js/require "./resources/icons/in_contacts.svg")
:icons/lock (js/require "./resources/icons/lock.svg")
:icons/mic (js/require "./resources/icons/mic.svg")
+ :icons/mobile (js/require "./resources/icons/mobile.svg")
:icons/ok (js/require "./resources/icons/ok.svg")
:icons/public (js/require "./resources/icons/public.svg")
:icons/public-chat (js/require "./resources/icons/public_chat.svg")
@@ -125,6 +127,7 @@
:icons/close (components.svg/slurp-svg "./resources/icons/close.svg")
:icons/copy-from (components.svg/slurp-svg "./resources/icons/copy_from.svg")
:icons/delete (components.svg/slurp-svg "./resources/icons/delete.svg")
+ :icons/desktop (components.svg/slurp-svg "./resources/icons/desktop.svg")
:icons/dots-horizontal (components.svg/slurp-svg "./resources/icons/dots_horizontal.svg")
:icons/dots-vertical (components.svg/slurp-svg "./resources/icons/dots_vertical.svg")
:icons/exclamation-mark (components.svg/slurp-svg "./resources/icons/exclamation_mark.svg")
@@ -139,6 +142,7 @@
:icons/lock (components.svg/slurp-svg "./resources/icons/lock.svg")
:icons/lock-opened (components.svg/slurp-svg "./resources/icons/lock_opened.svg")
:icons/mic (components.svg/slurp-svg "./resources/icons/mic.svg")
+ :icons/mobile (components.svg/slurp-svg "./resources/icons/mobile.svg")
:icons/ok (components.svg/slurp-svg "./resources/icons/ok.svg")
:icons/public (components.svg/slurp-svg "./resources/icons/public.svg")
:icons/public-chat (components.svg/slurp-svg "./resources/icons/public_chat.svg")
diff --git a/src/status_im/ui/screens/desktop/main/tabs/profile/views.cljs b/src/status_im/ui/screens/desktop/main/tabs/profile/views.cljs
index 35b7fa1863..bac10be7ab 100644
--- a/src/status_im/ui/screens/desktop/main/tabs/profile/views.cljs
+++ b/src/status_im/ui/screens/desktop/main/tabs/profile/views.cljs
@@ -80,13 +80,9 @@
[react/view
[react/view {:style styles/title-separator}]
[react/text {:style styles/mailserver-title} (i18n/label :devices)]
- [react/touchable-highlight {:style styles/pair-button
- :on-press pairing.views/pair!}
- [react/text (i18n/label :pair)]]
- (for [installation installations]
- ^{:key (:installation-id installation)}
- [react/view {:style {:margin-vertical 8}}
- [pairing.views/render-row installation]])])
+ [pairing.views/pair-this-device]
+ [pairing.views/sync-devices]
+ [pairing.views/installations-list installations]])
(views/defview advanced-settings []
(views/letsubs [installations [:pairing/installations]
diff --git a/src/status_im/ui/screens/group/add_contacts/views.cljs b/src/status_im/ui/screens/group/add_contacts/views.cljs
index db2273a178..cbcd0cb704 100644
--- a/src/status_im/ui/screens/group/add_contacts/views.cljs
+++ b/src/status_im/ui/screens/group/add_contacts/views.cljs
@@ -2,8 +2,11 @@
(:require-macros [status-im.utils.views :refer [defview letsubs]])
(:require [re-frame.core :as re-frame]
[status-im.i18n :as i18n]
- [status-im.ui.components.contact.contact :refer [toogle-contact-view]]
+ [status-im.utils.platform :as utils.platform]
+ [status-im.ui.components.button.view :as buttons]
+ [status-im.ui.components.contact.contact :refer [toggle-contact-view]]
[status-im.ui.components.list.views :as list]
+ [status-im.ui.components.list-selection :as list-selection]
[status-im.ui.components.react :as react]
[status-im.ui.components.status-bar.view :refer [status-bar]]
[status-im.ui.components.toolbar.view :as toolbar]
@@ -18,10 +21,15 @@
(re-frame/dispatch [action public-key])))
(defn- group-toggle-contact [contact]
- [toogle-contact-view contact :is-contact-selected? on-toggle])
+ [toggle-contact-view contact :is-contact-selected? on-toggle])
(defn- group-toggle-participant [contact]
- [toogle-contact-view contact :is-participant-selected? on-toggle-participant])
+ [toggle-contact-view contact :is-participant-selected? on-toggle-participant])
+
+(defn- handle-invite-friends-pressed []
+ (if utils.platform/desktop?
+ (re-frame/dispatch [:navigate-to :new-contact])
+ (list-selection/open-share {:message (i18n/label :t/get-status-at)})))
(defn- toggle-list-toolbar [{:keys [handler count label]} title]
[toolbar/toolbar {}
@@ -33,25 +41,39 @@
label])])
(defn toggle-list [contacts render-function]
- [react/view {:flex 1}
- [list/flat-list {:style styles/contacts-list
- :data contacts
- :key-fn :address
- :render-fn render-function
- :keyboardShouldPersistTaps :always}]])
+ [react/scroll-view {:flex 1}
+ (if utils.platform/desktop?
+ (for [contact contacts]
+ ^{:key (:public-key contact)}
+ (render-function contact))
+ [list/flat-list {:style styles/contacts-list
+ :data contacts
+ :key-fn :address
+ :render-fn render-function
+ :keyboardShouldPersistTaps :always}])])
+
+(defn no-contacts []
+ [react/view {:style {:flex 1
+ :justify-content :center
+ :align-items :center}}
+ [react/text
+ {:style styles/no-contact-text}
+ (i18n/label :t/group-chat-no-contacts)]
+ [buttons/secondary-button {:on-press handle-invite-friends-pressed} (i18n/label :t/invite-friends)]])
;; Start group chat
(defview contact-toggle-list []
(letsubs [contacts [:all-added-people-contacts]
selected-contacts-count [:selected-contacts-count]]
- (when (seq contacts)
- [react/keyboard-avoiding-view {:style styles/group-container}
- [status-bar]
- [toggle-list-toolbar {:handler #(re-frame/dispatch [:navigate-to :new-group])
- :label (i18n/label :t/next)
- :count (pos? selected-contacts-count)}
- (i18n/label :t/group-chat)]
- [toggle-list contacts group-toggle-contact]])))
+ [react/keyboard-avoiding-view {:style styles/group-container}
+ [status-bar]
+ [toggle-list-toolbar {:handler #(re-frame/dispatch [:navigate-to :new-group])
+ :label (i18n/label :t/next)
+ :count (pos? selected-contacts-count)}
+ (i18n/label :t/group-chat)]
+ (if (seq contacts)
+ [toggle-list contacts group-toggle-contact]
+ [no-contacts])]))
;; Add participants to existing group chat
(defview add-participants-toggle-list []
diff --git a/src/status_im/ui/screens/group/styles.cljs b/src/status_im/ui/screens/group/styles.cljs
index 1b2fba42ee..2ba14e2a3b 100644
--- a/src/status_im/ui/screens/group/styles.cljs
+++ b/src/status_im/ui/screens/group/styles.cljs
@@ -17,3 +17,9 @@
(def contacts-list
{:background-color colors/white})
+
+(def no-contact-text
+ {:margin-bottom 20
+ :margin-horizontal 50
+ :text-align :center
+ :color colors/gray})
diff --git a/src/status_im/ui/screens/pairing/styles.cljs b/src/status_im/ui/screens/pairing/styles.cljs
index 94430a7a60..a697415aee 100644
--- a/src/status_im/ui/screens/pairing/styles.cljs
+++ b/src/status_im/ui/screens/pairing/styles.cljs
@@ -1,27 +1,78 @@
(ns status-im.ui.screens.pairing.styles
(:require [status-im.ui.components.colors :as colors])
- (:require-macros [status-im.utils.styles :refer [defstyle]]))
+ (:require-macros [status-im.utils.styles :refer [defnstyle defstyle]]))
(def wrapper
{:flex 1
:background-color :white})
(def installation-item-inner
- {:flex 1
- :flex-direction :row
- :padding-horizontal 16})
+ {:flex 1
+ :flex-direction :row})
(defstyle installation-item
- {:flex-direction :row
- :background-color :white
- :align-items :center
- :padding-horizontal 16
- :ios {:height 64}
- :android {:height 56}})
+ {:flex-direction :row
+ :background-color :white
+ :align-items :center
+ :ios {:height 64}
+ :android {:height 56}})
(defstyle installation-item-name-text
- {:color colors/black
- :ios {:font-size 17
- :letter-spacing -0.2
- :line-height 20}
- :android {:font-size 16}})
+ {:color colors/black})
+
+(def installation-list
+ {:background-color :white
+ :padding-horizontal 16
+ :flex 1})
+
+(def footer-content {:justify-content :center
+ :flex 1
+ :align-items :center})
+
+(def footer-text {:color colors/blue
+ :text-align :center})
+
+(def pair-this-device
+ {:height 80
+ :padding-horizontal 16
+ :padding-top 12
+ :background-color :white})
+
+(def pair-this-device-actions
+ {:flex 1
+ :flex-direction :row})
+
+(defn pairing-button [enabled?]
+ {:width 40
+ :height 40
+ :background-color (if enabled?
+ colors/blue-light
+ colors/gray-lighter)
+ :border-radius 28
+ :align-items :center
+ :justify-content :center})
+
+(def installation-status
+ {:color colors/gray})
+
+(def pairing-actions-text
+ {:flex 1
+ :font-size 15
+ :margin-left 16})
+
+(def pair-this-device-title
+ {:color colors/blue
+ :margin-bottom 6
+ :font-size 15})
+
+(defnstyle pairing-button-icon [enabled?]
+ (let [color (if enabled?
+ colors/blue
+ colors/gray)]
+ {:desktop {:tint-color color}
+ :ios {:color color}
+ :android {:color color}}))
+
+(def paired-devices-title
+ {:color colors/gray
+ :margin-vertical 10})
diff --git a/src/status_im/ui/screens/pairing/subs.cljs b/src/status_im/ui/screens/pairing/subs.cljs
index 3726a4c972..16f914614d 100644
--- a/src/status_im/ui/screens/pairing/subs.cljs
+++ b/src/status_im/ui/screens/pairing/subs.cljs
@@ -4,4 +4,7 @@
(re-frame/reg-sub :pairing/installations
:<- [:get :pairing/installations]
- vals)
+ (fn [k]
+ (->> k
+ vals
+ (filter :device-type))))
diff --git a/src/status_im/ui/screens/pairing/views.cljs b/src/status_im/ui/screens/pairing/views.cljs
index fa5a362576..46260f8087 100644
--- a/src/status_im/ui/screens/pairing/views.cljs
+++ b/src/status_im/ui/screens/pairing/views.cljs
@@ -3,10 +3,12 @@
(:require [re-frame.core :as re-frame]
[status-im.i18n :as i18n]
[status-im.utils.config :as config]
+ [status-im.ui.screens.main-tabs.styles :as main-tabs.styles]
+ [status-im.ui.components.icons.vector-icons :as icons]
+ [status-im.ui.screens.home.styles :as home.styles]
+ [status-im.utils.platform :as utils.platform]
[status-im.utils.gfycat.core :as gfycat]
[status-im.ui.components.button.view :as buttons]
- [status-im.ui.components.colors :as colors]
- [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.components.status-bar.view :as status-bar]
@@ -15,8 +17,16 @@
[status-im.ui.screens.profile.components.views :as profile.components]
[status-im.ui.screens.pairing.styles :as styles]))
-(defn synchronize-installation! [id]
- #_(re-frame/dispatch [:pairing.ui/synchronize-installation-pressed id]))
+(defn icon-style [{:keys [width height] :as style}]
+ (if utils.platform/desktop?
+ {:container-style {:width width
+
+ :height height}
+ :style style}
+ style))
+
+(defn synchronize-installations! []
+ (re-frame/dispatch [:pairing.ui/synchronize-installation-pressed]))
(defn pair! []
(re-frame/dispatch [:pairing.ui/pair-devices-pressed]))
@@ -27,39 +37,84 @@
(defn disable-installation! [installation-id _]
(re-frame/dispatch [:pairing.ui/disable-installation-pressed installation-id]))
+(defn footer []
+ [react/touchable-highlight {:on-press synchronize-installations!
+ :style main-tabs.styles/tabs-container}
+ [react/view
+ {:style styles/footer-content}
+ [react/text
+ {:style styles/footer-text}
+ (i18n/label :t/sync-all-devices)]]])
+
+(defn pair-this-device []
+ [react/touchable-highlight {:on-press pair!
+ :style styles/pair-this-device}
+ [react/view {:style styles/pair-this-device-actions}
+ [react/view
+ [react/view (styles/pairing-button true)
+ [icons/icon :icons/add (icon-style (styles/pairing-button-icon true))]]]
+ [react/view {:style styles/pairing-actions-text}
+ [react/view
+ [react/text {:style styles/pair-this-device-title} (i18n/label :t/pair-this-device)]]
+ [react/view
+ [react/text (i18n/label :t/pair-this-device-description)]]]]])
+
+(defn sync-devices []
+ [react/touchable-highlight {:on-press synchronize-installations!
+ :style styles/pair-this-device}
+ [react/view {:style styles/pair-this-device-actions}
+ [react/view
+ [react/view (styles/pairing-button true)
+ [icons/icon :icons/wnode (icon-style (styles/pairing-button-icon true))]]]
+ [react/view {:style styles/pairing-actions-text}
+ [react/view
+ [react/text {:style styles/pair-this-device-title} (i18n/label :t/sync-all-devices)]]]]])
+
(defn render-row [{:keys [device-type enabled? installation-id]}]
[react/touchable-highlight
- {:on-press #(synchronize-installation! installation-id)
+ {:on-press (if enabled?
+ (partial disable-installation! installation-id)
+ (partial enable-installation! installation-id))
:accessibility-label :installation-item}
- [react/view styles/installation-item
- [react/view styles/installation-item-inner
+ [react/view {:style styles/installation-item}
+ [react/view {:style (styles/pairing-button enabled?)}
+ [icons/icon (if (= "desktop"
+ device-type)
+ :icons/desktop
+ :icons/mobile)
+ (icon-style (styles/pairing-button-icon enabled?))]]
+ [react/view {:style styles/pairing-actions-text}
[react/view
[react/text {:style styles/installation-item-name-text}
- (str (gfycat/generate-gfy installation-id) " - " (or device-type
- "unknown"))]]
+ (gfycat/generate-gfy installation-id)]]
[react/view
- (if enabled?
- [buttons/primary-button
- {:on-press (partial disable-installation! installation-id)}
- (i18n/label :t/enabled)]
- [buttons/secondary-button
- {:on-press (partial enable-installation! installation-id)}
- (i18n/label :t/disabled)])]]]])
+ [react/text {:style styles/installation-status}
+ (if enabled?
+ (i18n/label :t/syncing-enabled)
+ (i18n/label :t/syncing-disabled))]]]]])
(defn render-rows [installations]
- [react/view styles/wrapper
+ [react/view {:style styles/wrapper}
[list/flat-list {:data installations
:default-separator? false
:key-fn :installation-id
:render-fn render-row}]])
+(defn installations-list [installations]
+ [react/view {:style styles/installation-list}
+ [react/view {:style styles/paired-devices-title}
+ [react/text (i18n/label :t/paired-devices)]]
+ (render-rows installations)])
+
(views/defview installations []
(views/letsubs [installations [:pairing/installations]]
[react/view {:flex 1}
[status-bar/status-bar]
[toolbar/toolbar {}
toolbar/default-nav-back
- [toolbar/content-title (i18n/label :t/devices)]
- [toolbar/actions
- [(toolbar.actions/add false pair!)]]]
- (render-rows installations)]))
+ [toolbar/content-title (i18n/label :t/devices)]]
+ [react/scroll-view {:style {:background-color :white}}
+ [pair-this-device]
+ (when (seq installations)
+ [installations-list installations])]
+ (when (seq installations) [footer])]))
diff --git a/src/status_im/utils/fx.cljs b/src/status_im/utils/fx.cljs
index 5d709a1f02..2e66f366ac 100644
--- a/src/status_im/utils/fx.cljs
+++ b/src/status_im/utils/fx.cljs
@@ -12,6 +12,7 @@
(def ^:private mergable-keys
#{:data-store/tx :data-store/base-tx :chat-received-message/add-fx
:shh/add-new-sym-keys :shh/get-new-sym-keys :shh/post
+ :shh/send-direct-message
:shh/generate-sym-key-from-password :transport/confirm-messages-processed
:group-chats/extract-membership-signature :utils/dispatch-later})
diff --git a/test/cljs/status_im/test/pairing/core.cljs b/test/cljs/status_im/test/pairing/core.cljs
index 0599d09296..d664d70377 100644
--- a/test/cljs/status_im/test/pairing/core.cljs
+++ b/test/cljs/status_im/test/pairing/core.cljs
@@ -1,6 +1,7 @@
(ns status-im.test.pairing.core
(:require [cljs.test :refer-macros [deftest is testing]]
[status-im.transport.message.pairing :as transport.pairing]
+ [status-im.utils.identicon :as identicon]
[status-im.utils.config :as config]
[status-im.pairing.core :as pairing]))
@@ -72,40 +73,60 @@
(is (= expected (pairing/merge-contact contact-1 contact-2))))))
(deftest handle-sync-installation-test
- (with-redefs [config/pairing-enabled? (constantly true)]
- (let [old-contact-1 {:name "old-contact-one"
- :last-updated 0
- :photo-path "old-contact-1"
- :pending? true}
- new-contact-1 {:name "new-contact-one"
- :last-updated 1
- :photo-path "new-contact-1"
- :pending? false}
- old-contact-2 {:name "old-contact-2"
- :last-updated 0
- :photo-path "old-contact-2"
- :pending? false}
- new-contact-2 {:name "new-contact-2"
- :last-updated 1
- :photo-path "new-contact-2"
- :pending? false}
- contact-3 {:name "contact-3"
- :photo-path "contact-3"
- :pending? false}
- contact-4 {:name "contact-4"
- :photo-path "contact-4"
- :pending? true}
+ (with-redefs [config/pairing-enabled? (constantly true)
+ identicon/identicon (constantly "generated")]
+ (let [old-contact-1 {:name "old-contact-one"
+ :public-key "contact-1"
+ :last-updated 0
+ :photo-path "old-contact-1"
+ :pending? true}
+ new-contact-1 {:name "new-contact-one"
+ :public-key "contact-1"
+ :last-updated 1
+ :photo-path "new-contact-1"
+ :pending? false}
+ old-contact-2 {:name "old-contact-2"
+ :public-key "contact-2"
+ :last-updated 0
+ :photo-path "old-contact-2"
+ :pending? false}
+ new-contact-2 {:name "new-contact-2"
+ :public-key "contact-2"
+ :last-updated 1
+ :photo-path "new-contact-2"
+ :pending? false}
+ contact-3 {:name "contact-3"
+ :public-key "contact-3"
+ :photo-path "contact-3"
+ :pending? false}
+ contact-4 {:name "contact-4"
+ :public-key "contact-4"
+ :pending? true}
+ local-contact-5 {:name "contact-5"
+ :photo-path "local"
+ :public-key "contact-5"
+ :pending? true
+ :last-updated 1}
+ remote-contact-5 {:name "contact-5"
+ :public-key "contact-5"
+ :photo-path "remote"
+ :pending? true
+ :last-updated 1}
cofx {:db {:account/account {:public-key "us"}
:contacts/contacts {"contact-1" old-contact-1
"contact-2" new-contact-2
- "contact-3" contact-3}}}
+ "contact-3" contact-3
+ "contact-5" local-contact-5}}}
sync-message {:contacts {"contact-1" new-contact-1
"contact-2" old-contact-2
- "contact-4" contact-4}}
+ "contact-4" contact-4
+ "contact-5" remote-contact-5}}
expected {"contact-1" new-contact-1
"contact-2" new-contact-2
"contact-3" contact-3
- "contact-4" contact-4}]
+ "contact-4" (assoc contact-4
+ :photo-path "generated")
+ "contact-5" local-contact-5}]
(testing "not coming from us"
(is (not (pairing/handle-sync-installation cofx sync-message "not-us"))))
(testing "coming from us"
@@ -115,7 +136,7 @@
(deftest handle-pair-installation-test
(with-redefs [config/pairing-enabled? (constantly true)]
- (let [cofx {:db {:current-public-key "us"
+ (let [cofx {:db {:account/account {:public-key "us"}
:pairing/installations {"1" {:has-bundle? true
:installation-id "1"}
"2" {:has-bundle? false
@@ -138,10 +159,26 @@
(deftest sync-installation-messages-test
(testing "it creates a sync installation message"
(let [cofx {:db {:account/account {:public-key "us"}
- :contacts/contacts {"contact-1" {:name "contact-1"}
- "contact-2" {:name "contact-2"}}}}
- expected [(transport.pairing/SyncInstallation. {"contact-1" {:name "contact-1"}})
- (transport.pairing/SyncInstallation. {"contact-2" {:name "contact-2"}})]]
+ :contacts/contacts {"contact-1" {:name "contact-1"
+ :public-key "contact-1"}
+ "contact-2" {:name "contact-2"
+ :public-key "contact-2"}
+ "contact-3" {:name "contact-3"
+ :public-key "contact-3"}
+ "contact-4" {:name "contact-4"
+ :public-key "contact-4"}
+ "contact-5" {:name "contact-5"
+ :public-key "contact-5"}}}}
+ expected [(transport.pairing/SyncInstallation. {"contact-1" {:name "contact-1"
+ :public-key "contact-1"}
+ "contact-2" {:name "contact-2"
+ :public-key "contact-2"}
+ "contact-3" {:name "contact-3"
+ :public-key "contact-3"}
+ "contact-4" {:name "contact-4"
+ :public-key "contact-4"}})
+ (transport.pairing/SyncInstallation. {"contact-5" {:name "contact-5"
+ :public-key "contact-5"}})]]
(is (= expected (pairing/sync-installation-messages cofx))))))
(deftest handle-bundles-added-test
@@ -162,3 +199,11 @@
(testing "not from us"
(let [new-installation {:identity "not-us" :installationID "does-not-matter"}]
(is (not (pairing/handle-bundles-added cofx new-installation))))))))
+
+(deftest has-paired-installations-test
+ (testing "no paired devices"
+ (is (not (pairing/has-paired-installations? {:db {:pairing/installations {"1" {}
+ "2" {}}}}))))
+ (testing "has paired devices"
+ (is (pairing/has-paired-installations? {:db {:pairing/installations {"1" {}
+ "2" {:enabled? true}}}}))))
diff --git a/translations/en.json b/translations/en.json
index f5c4cdcc4a..6e49d97bdd 100644
--- a/translations/en.json
+++ b/translations/en.json
@@ -6,8 +6,12 @@
"description": "Description",
"devices": "Devices",
"pair": "Pair devices",
- "enabled": "enabled",
- "disabled": "disabled",
+ "pair-this-device": "Pair this device",
+ "pair-this-device-description": "Pair your devices to sync contacts and chats between them",
+ "syncing-enabled": "Syncing enabled",
+ "syncing-disabled": "Syncing disabled",
+ "sync-all-devices": "Sync all devices",
+ "paired-devices": "Paired devices",
"currency-display-name-tzs": "Tanzanian Shilling",
"currency-display-name-brl": "Brazil Real",
"mainnet-network": "Main network",
@@ -25,6 +29,7 @@
"group-chat-name-changed": "*{{member}}* changed the group's name to *{{name}}*",
"group-chat-member-added": "*{{member}}* joined the group",
"group-chat-member-removed": "*{{member}}* left the group",
+ "group-chat-no-contacts": "You don't have any contacts yet.\nInvite your friends to start chatting",
"agree-by-continuing": "By continuing you agree\n to our ",
"wallet-advanced": "Advanced",
"currency-display-name-sos": "Somalia Shilling",