Enable syncing of contacts & style pairing section
Everytime a contact request is sent/confirmed a sync message is also sent to other devices so the contact is kept in sync. Signed-off-by: Andrea Maria Piana <andrea.maria.piana@gmail.com>
This commit is contained in:
parent
e8d3e39063
commit
1104becfba
|
@ -0,0 +1,3 @@
|
|||
<svg width="25" height="25" viewBox="0 0 22 14" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M5 0C3.34315 0 2 1.34315 2 3C2 4.02458 2.00001 5.04917 2.00002 6.07375C2.00004 8.04917 2.00007 10.0246 2.00001 12H1C0.447715 12 0 12.4477 0 13C0 13.5523 0.447716 14 1 14H1.99992H20H21C21.5523 14 22 13.5523 22 13C22 12.4477 21.5523 12 21 12H20V3C20 1.34315 18.6569 0 17 0H5ZM18 12V3C18 2.44772 17.5523 2 17 2H5C4.44772 2 4 2.44772 4 3V12H18Z" fill="#4360DF"/>
|
||||
</svg>
|
After Width: | Height: | Size: 511 B |
|
@ -0,0 +1,4 @@
|
|||
|
||||
<svg width="25" height="25" viewBox="0 0 14 22" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M0 3C0 1.34315 1.34315 0 3 0H11C12.6569 0 14 1.34315 14 3V19C14 20.6569 12.6569 22 11 22H3C1.34315 22 0 20.6569 0 19V3ZM3 2C2.44772 2 2 2.44772 2 3V19C2 19.5523 2.44772 20 3 20H11C11.5523 20 12 19.5523 12 19V3C12 2.44772 11.5523 2 11 2H10.4142C10.149 2 9.89464 2.10536 9.70711 2.29289L9.29289 2.70711C9.10536 2.89464 8.851 3 8.58579 3H5.41421C5.149 3 4.89464 2.89464 4.70711 2.70711L4.29289 2.29289C4.10536 2.10536 3.851 2 3.58579 2H3Z" fill="#4360DF"/>
|
||||
</svg>
|
After Width: | Height: | Size: 607 B |
|
@ -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))
|
||||
|
|
|
@ -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}])
|
||||
|
|
|
@ -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"))
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)))))
|
||||
|
|
|
@ -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)))
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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})
|
||||
:justify-content :center})
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -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 []
|
||||
|
|
|
@ -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})
|
||||
|
|
|
@ -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})
|
||||
|
|
|
@ -4,4 +4,7 @@
|
|||
|
||||
(re-frame/reg-sub :pairing/installations
|
||||
:<- [:get :pairing/installations]
|
||||
vals)
|
||||
(fn [k]
|
||||
(->> k
|
||||
vals
|
||||
(filter :device-type))))
|
||||
|
|
|
@ -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])]))
|
||||
|
|
|
@ -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})
|
||||
|
||||
|
|
|
@ -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}}}}))))
|
||||
|
|
|
@ -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",
|
||||
|
|
Loading…
Reference in New Issue