introduce system-tags

Signed-off-by: yenda <eric@status.im>
This commit is contained in:
yenda 2019-03-21 11:31:47 +01:00
parent dd4d7457a5
commit 26b97ddf43
No known key found for this signature in database
GPG Key ID: 0095623C0069DCE6
31 changed files with 491 additions and 433 deletions

View File

@ -1,5 +1,6 @@
(ns status-im.accounts.update.core
(:require
[status-im.contact.db :as contact.db]
[status-im.contact.device-info :as device-info]
[status-im.data-store.accounts :as accounts-store]
[status-im.data-store.transport :as transport-store]
@ -36,9 +37,8 @@
:success-event success-event})))))
(fx/defn contact-public-keys [{:keys [db]}]
(reduce (fn [acc [_ {:keys [public-key dapp? pending?]}]]
(if (and (not dapp?)
(not pending?))
(reduce (fn [acc [_ {:keys [public-key] :as contact}]]
(if (contact.db/active? contact)
(conj acc public-key)
acc))
#{}

View File

@ -1,32 +1,19 @@
(ns status-im.chat.db
(:require [clojure.set :as clojure.set]
[clojure.string :as string]
[status-im.contact.db :as contact.db]
[status-im.chat.commands.core :as commands]
[status-im.chat.commands.input :as commands.input]
[status-im.group-chats.db :as group-chats.db]
[status-im.utils.gfycat.core :as gfycat]))
(defn chat-name
[{:keys [group-chat
chat-id
public?
name]}
{contact-name :name}]
(cond
public? (str "#" name)
group-chat name
:else (or contact-name
(gfycat/generate-gfy chat-id))))
(defn group-chat-name
[{:keys [public? name]}]
(str (when public? "#") name))
(defn enrich-active-chat
[contacts {:keys [chat-id] :as chat} current-public-key]
(if-let [contact (get contacts chat-id)]
(-> chat
(assoc :contact contact
:chat-name (chat-name chat contact)
:name (:name contact)
:random-name (gfycat/generate-gfy (:public-key contact)))
(update :tags clojure.set/union (:tags contact)))
[contacts {:keys [chat-id public? group-chat name] :as chat} current-public-key]
(if group-chat
(let [pending-invite-inviter-name
(group-chats.db/get-pending-invite-inviter-name contacts
chat
@ -35,8 +22,17 @@
pending-invite-inviter-name
(assoc :pending-invite-inviter-name pending-invite-inviter-name)
:always
(assoc :chat-name
(chat-name chat nil))))))
(assoc :chat-name (group-chat-name chat))))
(let [{contact-name :name :as contact}
(get contacts chat-id
(contact.db/public-key->new-contact chat-id))
random-name (gfycat/generate-gfy chat-id)]
(-> chat
(assoc :contact contact
:chat-name (or contact-name random-name)
:name contact-name
:random-name random-name)
(update :tags clojure.set/union (:tags contact))))))
(defn active-chats
[contacts chats {:keys [public-key]}]

View File

@ -94,17 +94,21 @@
(assoc message :outgoing (and (= from current-public-key)
(not (system-message? message)))))
(defn build-desktop-notification [{:keys [db] :as cofx} {:keys [chat-id timestamp content from] :as message}]
(let [chat-name' (chat.db/chat-name (get-in db [:chats chat-id]) from)
contact-name' (if-let [contact-name (get-in db [:contacts/contacts from :name])]
contact-name
(defn build-desktop-notification
[{:keys [db] :as cofx} {:keys [chat-id timestamp content from] :as message}]
(let [{:keys [group-chat] :as chat} (get-in db [:chats chat-id])
contact-name (get-in db [:contacts/contacts from :name]
(:name (contact.db/public-key->new-contact from)))
shown-chat-name (when-not (= chat-name' contact-name') chat-name') ; No point in repeating contact name if the chat name already contains the same name
chat-name (if group-chat
(chat.db/group-chat-name chat)
contact-name)
;; contact name and chat-name are the same in 1-1 chats
shown-chat-name (when group-chat chat-name)
timestamp' (when-not (< (time/seconds-ago (time/to-date timestamp)) 15)
(str " @ " (time/to-short-str timestamp)))
body-first-line (when (or shown-chat-name timestamp')
(str shown-chat-name timestamp' ":\n"))]
{:title contact-name'
{:title contact-name
:body (str body-first-line (:text content))
:prioritary? (not (chat-model/multi-user-chat? cofx chat-id))}))

View File

@ -31,20 +31,15 @@
(update :contacts/contacts #(merge contacts %))
(assoc :contacts/blocked (contact.db/get-blocked-contacts all-contacts)))}))
(defn can-add-to-contacts? [{:keys [pending? dapp?]}]
(and (not dapp?)
(or pending?
;; it's not in the contact list at all
(nil? pending?))))
(defn build-contact [{{:keys [chats] :account/keys [account]
(defn build-contact
[{{:keys [chats] :account/keys [account]
:contacts/keys [contacts]} :db} public-key]
(cond-> (assoc (contact.db/public-key->contact contacts public-key)
:address (contact.db/public-key->address public-key))
(cond-> (contact.db/public-key->contact contacts public-key)
(= public-key (:public-key account))
(assoc :name (:name account))))
(defn- own-info [db]
(defn- own-info
[db]
(let [{:keys [name photo-path address]} (:account/account db)
fcm-token (get-in db [:notifications :fcm-token])]
{:name name
@ -53,29 +48,29 @@
:device-info (device-info/all {:db db})
:fcm-token fcm-token}))
(fx/defn upsert-contact [{:keys [db] :as cofx}
{:keys [pending?
public-key] :as contact}]
(fx/defn upsert-contact
[{:keys [db] :as cofx}
{:keys [public-key] :as contact}]
(fx/merge cofx
{:db (-> db
(update-in [:contacts/contacts public-key] merge contact))
:data-store/tx [(contacts-store/save-contact-tx contact)]}
#(when-not pending?
#(when (contact.db/added? contact)
(contact-code/listen-to-chat % public-key))))
(fx/defn send-contact-request
[{:keys [db] :as cofx} {:keys [public-key pending? dapp?] :as contact}]
(when-not dapp?
(if pending?
(protocol/send (message.contact/map->ContactRequestConfirmed (own-info db)) public-key cofx)
(protocol/send (message.contact/map->ContactRequest (own-info db)) public-key cofx))))
[{:keys [db] :as cofx} {:keys [public-key] :as contact}]
(if (contact.db/pending? contact)
(protocol/send (message.contact/map->ContactRequest (own-info db)) public-key cofx)
(protocol/send (message.contact/map->ContactRequestConfirmed (own-info db)) public-key cofx)))
(fx/defn add-contact
"Add a contact and set pending to false"
[{:keys [db] :as cofx} public-key]
(when (not= (get-in db [:account/account :public-key]) public-key)
(let [contact (-> (build-contact cofx public-key)
(assoc :pending? false))]
(update :system-tags
(fnil #(conj % :contact/added) #{})))]
(fx/merge cofx
{:db (assoc-in db [:contacts/new-identity] "")}
(upsert-contact contact)
@ -99,21 +94,23 @@
:minPow 1
:callback (constantly nil)}]}})))
(fx/defn add-tag
"add a tag to the contact"
[{:keys [db] :as cofx}]
(let [tag (get-in db [:ui/contact :contact/new-tag])
public-key (get-in db [:current-chat-id])
tags (conj (get-in db [:contacts/contacts public-key :tags] #{}) tag)]
{:db (assoc-in db [:contacts/contacts public-key :tags] tags)
:data-store/tx [(contacts-store/add-contact-tag-tx public-key tag)]}))
(fx/defn change-system-tag
"remove a system tag from the contact"
[{:keys [db] :as cofx} public-key tag change-fn]
(let [contact (update (get-in db [:contacts/contacts public-key])
:system-tags (fnil #(change-fn % tag) #{}))]
{:db (assoc-in db [:contacts/contacts public-key] contact)
:data-store/tx [(contacts-store/save-contact-tx contact)]}))
(fx/defn remove-tag
"remove a tag from the contact"
(fx/defn add-system-tag
"add a system tag to the contact"
[{:keys [db] :as cofx} public-key tag]
(let [tags (disj (get-in db [:contacts/contacts public-key :tags] #{}) tag)]
{:db (assoc-in db [:contacts/contacts public-key :tags] tags)
:data-store/tx [(contacts-store/remove-contact-tag-tx public-key tag)]}))
(change-system-tag cofx public-key tag conj))
(fx/defn remove-system-tag
"remove a system tag from the contact"
[{:keys [db] :as cofx} public-key tag]
(change-system-tag cofx public-key tag disj))
(fx/defn block-contact-confirmation
[cofx public-key]
@ -178,11 +175,12 @@
(navigation/navigate-to-clean :home {})))
(fx/defn block-contact
[{:keys [db get-user-messages] :as cofx} public-key]
(let [contact (assoc (contact.db/public-key->contact
[{:keys [db get-user-messages now] :as cofx} public-key]
(let [contact (-> (contact.db/public-key->contact
(:contacts/contacts db)
public-key)
:blocked? true)
(assoc :last-updated now)
(update :system-tags conj :contact/blocked))
user-messages (get-user-messages public-key)
user-messages-ids (map :message-id user-messages)
;; we make sure to remove the 1-1 chat which we delete entirely
@ -209,9 +207,10 @@
(navigation/navigate-back)))))
(fx/defn unblock-contact
[{:keys [db]} public-key]
(let [contact (assoc (get-in db [:contacts/contacts public-key])
:blocked? false)]
[{:keys [db now]} public-key]
(let [contact (-> (get-in db [:contacts/contacts public-key])
(assoc :last-updated now)
(update :system-tags disj :contact/blocked))]
{:db (-> db
(update :contacts/blocked disj public-key)
(assoc-in [:contacts/contacts public-key] contact))
@ -238,8 +237,8 @@
;; Backward compatibility with <= 0.9.21, as they don't send
;; fcm-token & address in contact updates
contact-props (cond->
{:public-key public-key
contact-props
(cond-> {:public-key public-key
:photo-path profile-image
:name name
:address (or address
@ -250,8 +249,8 @@
(:device-info contact)
device-info)
:last-updated timestamp-ms
;;NOTE (yenda) in case of concurrent contact request
:pending? (get contact :pending? true)}
:system-tags (conj (get contact :system-tags #{})
:contact/request-received)}
fcm-token (assoc :fcm-token fcm-token))]
(upsert-contact cofx contact-props)))))

View File

@ -10,31 +10,28 @@
;;Contact
;;we can't validate public key, because for dapps public-key is just string
(spec/def :contact/public-key :global/not-empty-string)
(spec/def :contact/name :global/not-empty-string)
(spec/def :contact/address (spec/nilable :global/address))
(spec/def :contact/photo-path (spec/nilable string?))
(spec/def :contact/status (spec/nilable string?))
(spec/def :contact/fcm-token (spec/nilable string?))
(spec/def :contact/description (spec/nilable string?))
(spec/def :contact/last-updated (spec/nilable int?))
(spec/def :contact/last-online (spec/nilable int?))
(spec/def :contact/pending? boolean?)
(spec/def :contact/tags (spec/coll-of string? :kind set?))
(spec/def :contact/blocked? boolean?)
(spec/def :contact/contact (spec/keys :req-un [:contact/name]
:opt-un [:contact/public-key
(spec/def :contact/tags (spec/coll-of string? :kind set?))
;; contact/blocked: the user is blocked
;; contact/added: the user was added to the contacts and a contact request was sent
;; contact/request-received: the user sent a contact request
(spec/def :contact/system-tags (spec/coll-of keyword? :kind set?))
(spec/def :contact/contact (spec/keys :req-un [:contact/public-key
:contact/name
:contact/address
:contact/photo-path
:contact/status
:contact/last-updated
:contact/system-tags]
:opt-un [:contact/last-updated
:contact/last-online
:contact/pending?
:contact/fcm-token
:contact/description
:contact/blocked?
:contact/tags]))
;;Contact list ui props
@ -75,7 +72,8 @@
{:name (gfycat/generate-gfy public-key)
:address (public-key->address public-key)
:photo-path (identicon/identicon public-key)
:public-key public-key})
:public-key public-key
:system-tags #{}})
(defn public-key->contact
[contacts public-key]
@ -99,13 +97,6 @@
(clojure.string/lower-case name2))))
(vals contacts)))
(defn active
[contacts]
(->> contacts
(remove (fn [[_ {:keys [pending? blocked?]}]]
(or pending? blocked?)))
sort-contacts))
(defn filter-dapps
[v dev-mode?]
(remove #(when-not dev-mode? (true? (:developer? %))) v))
@ -122,22 +113,76 @@
(defn get-all-contacts-in-group-chat
[members admins contacts current-account]
(let [current-account-contact (-> current-account
(select-keys [:name :photo-path :public-key]))
all-contacts (assoc contacts (:public-key current-account-contact) current-account-contact)]
(let [{:keys [public-key] :as current-account-contact}
(select-keys current-account [:name :photo-path :public-key])
all-contacts (assoc contacts public-key current-account-contact)]
(->> members
(map #(or (get all-contacts %)
(public-key->new-contact %)))
(remove :dapp?)
(sort-by (comp clojure.string/lower-case :name))
(map #(if (admins (:public-key %))
(assoc % :admin? true)
%)))))
(defn get-blocked-contacts
[contacts]
(into #{} (map :public-key (filter :blocked? contacts))))
(defn added?
([{:keys [system-tags]}]
(contains? system-tags :contact/added))
([db public-key]
(added? (get-in db [:contacts/contacts public-key]))))
(defn blocked?
[db contact]
(get-in db [:contacts/contacts contact :blocked?]))
([{:keys [system-tags]}]
(contains? system-tags :contact/blocked))
([db public-key]
(blocked? (get-in db [:contacts/contacts public-key]))))
(defn pending?
"Check if this is a pending? contact, meaning one side sent a contact request
but the other didn't respond to it yet"
([{:keys [system-tags] :as contact}]
(let [request-received? (contains? system-tags :contact/request-received)
added? (added? contact)]
(and (or request-received?
added?)
(not (and request-received? added?)))))
([db public-key]
(pending? (get-in db [:contacts/contacts public-key]))))
(defn active?
"Checks that the user is added to the contact and not blocked"
([contact]
(and (added? contact)
(not (blocked? contact))))
([db public-key]
(active? (get-in db [:contacts/contacts public-key]))))
(defn enrich-contact
[{:keys [system-tags] :as contact}]
(assoc contact
:pending? (pending? contact)
:blocked? (blocked? contact)
:active? (active? contact)
:added? (contains? system-tags :contact/added)))
(defn enrich-contacts
[contacts]
(reduce (fn [acc [public-key contact]]
(assoc acc public-key (enrich-contact contact)))
{}
contacts))
(defn get-blocked-contacts
[contacts]
(reduce (fn [acc {:keys [public-key] :as contact}]
(if (blocked? contact)
(conj acc public-key)
acc))
#{}
contacts))
(defn get-active-contacts
[contacts]
(->> contacts
(filter (fn [[_ contact]]
(active? contact)))
sort-contacts))

View File

@ -8,6 +8,11 @@
(fn [db]
(:contacts/dapps db)))
(re-frame/reg-sub
::contacts
(fn [db]
(get db :contacts/contacts)))
(re-frame/reg-sub
::query-current-chat-contacts
:<- [:chats/current-chat]
@ -17,14 +22,15 @@
(re-frame/reg-sub
:contacts/contacts
(fn [db]
(get db :contacts/contacts)))
:<- [::contacts]
(fn [contacts]
(contact.db/enrich-contacts contacts)))
(re-frame/reg-sub
:contacts/active
:<- [:contacts/contacts]
(fn [contacts]
(contact.db/active contacts)))
(contact.db/get-active-contacts contacts)))
(re-frame/reg-sub
:contacts/active-count
@ -37,8 +43,8 @@
:<- [:contacts/contacts]
(fn [contacts]
(->> contacts
(filter (fn [[_ {:keys [blocked?]}]]
blocked?))
(filter (fn [[_ contact]]
(contact.db/blocked? contact)))
(contact.db/sort-contacts))))
(re-frame/reg-sub
@ -57,7 +63,10 @@
:<- [:contacts/contacts]
:<- [:contacts/current-contact-identity]
(fn [[contacts identity]]
(contacts identity)))
(or (contacts identity)
(-> identity
contact.db/public-key->new-contact
contact.db/enrich-contact))))
(re-frame/reg-sub
:contacts/all-dapps
@ -95,9 +104,7 @@
:contacts/all-contacts-not-in-current-chat
:<- [::query-current-chat-contacts remove]
(fn [contacts]
(->> contacts
(remove :dapp?)
(sort-by (comp clojure.string/lower-case :name)))))
(sort-by (comp clojure.string/lower-case :name) contacts)))
(re-frame/reg-sub
:contacts/current-chat-contacts

View File

@ -4,6 +4,7 @@
in our contacts."
(:require
[taoensso.timbre :as log]
[status-im.contact.db :as contact.db]
[status-im.utils.fx :as fx]
[status-im.transport.shh :as shh]
[status-im.transport.message.public-chat :as transport.public-chat]
@ -46,7 +47,7 @@
(contains? members-joined my-public-key)
(contains? members their-public-key)))
(vals (:chats db)))]
(when (and (not= false (get-in db [:contacts/contacts their-public-key :pending?]))
(when (and (not (contact.db/active? db their-public-key))
(not= my-public-key their-public-key)
(not (get-in db [:chats their-public-key :is-active]))
(empty? active-group-chats))

View File

@ -4,17 +4,24 @@
[status-im.data-store.realm.core :as core]
[clojure.set :as clojure.set]))
(defn- normalize-contact [contact]
(defn- deserialize-contact [contact]
(-> contact
(update :tags #(into #{} %))))
(update :tags #(into #{} %))
(update :system-tags
#(reduce (fn [acc s]
(conj acc (keyword (subs s 1))))
#{}
%))))
(defn- serialize-contact [contact]
(update contact :device-info #(or (vals %) [])))
(-> contact
(update :device-info #(or (vals %) []))
(update :system-tags #(mapv str %))))
(re-frame/reg-cofx
:data-store/get-all-contacts
(fn [coeffects _]
(assoc coeffects :all-contacts (map normalize-contact
(assoc coeffects :all-contacts (map deserialize-contact
(-> @core/account-realm
(core/get-all :contact)
(core/all-clj :contact))))))
@ -90,23 +97,3 @@
[public-key]
(fn [realm]
(core/delete realm (get-contact-by-id public-key realm))))
(defn add-contact-tag-tx
"Returns tx function for adding chat contacts"
[public-key tag]
(fn [realm]
(let [contact (get-contact-by-id public-key realm)
existing-tags (object/get contact "tags")]
(aset contact "tags"
(clj->js (into #{} (concat tag
(core/list->clj existing-tags))))))))
(defn remove-contact-tag-tx
"Returns tx function for removing chat contacts"
[public-key tag]
(fn [realm]
(let [contact (get-contact-by-id public-key realm)
existing-tags (object/get contact "tags")]
(aset contact "tags"
(clj->js (remove (into #{} tag)
(core/list->clj existing-tags)))))))

View File

@ -114,3 +114,17 @@
:tags {:type "string[]"}
:device-info {:type :list
:objectType :contact-device-info}}})
(def v7 {:name :contact
:primaryKey :public-key
:properties {:address {:type :string :optional true}
:name {:type :string :optional true}
:photo-path {:type :string :optional true}
:last-updated {:type :int :default 0}
:last-online {:type :int :default 0}
:fcm-token {:type :string :optional true}
:public-key :string
:tags {:type "string[]"}
:system-tags {:type "string[]"}
:device-info {:type :list
:objectType :contact-device-info}}})

View File

@ -446,6 +446,21 @@
contact-device-info/v1
contact-recovery/v1])
(def v40 [chat/v14
transport/v8
contact/v7
message/v9
mailserver/v11
mailserver-topic/v1
user-status/v2
membership-update/v1
installation/v3
local-storage/v1
browser/v8
dapp-permissions/v9
contact-device-info/v1
contact-recovery/v1])
;; put schemas ordered by version
(def schemas [{:schema v1
:schemaVersion 1
@ -563,4 +578,7 @@
:migration migrations/v38}
{:schema v39
:schemaVersion 39
:migration (constantly nil)}])
:migration (constantly nil)}
{:schema v40
:schemaVersion 40
:migration migrations/v40}])

View File

@ -369,3 +369,23 @@
chat-id (aget chat "chat-id")]
(when-let [last-clock-value (get-last-clock-value new-realm chat-id)]
(aset chat "last-clock-value" last-clock-value))))))
(defn v40
"the pending? and blocked? boolean fields are removed and turned
into system tags equivalents in the system-tags field"
[old-realm new-realm]
(log/debug "migrating v40 account database")
(let [old-contacts (.objects old-realm "contact")
new-contacts (.objects new-realm "contact")]
(dotimes [i (.-length old-contacts)]
(let [old-contact (aget old-contacts i)
new-contact (aget new-contacts i)
blocked? (aget old-contact "blocked?")
pending? (aget old-contact "pending?")
last-updated (aget old-contact "last-updated")
system-tags (cond-> #{}
blocked? (conj ":contact/blocked")
(false? pending?) (conj ":contact/added")
(or (true? pending?)
(zero? last-updated)) (conj ":contact/request-received"))]
(aset new-contact "system-tags" (clj->js system-tags))))))

View File

@ -1620,16 +1620,6 @@
(fn [cofx _]
(contact/add-new-identity-to-contacts cofx)))
(handlers/register-handler-fx
:contact.ui/add-tag
(fn [cofx _]
(contact/add-tag cofx)))
(handlers/register-handler-fx
:contact.ui/set-tag-input-field
(fn [cofx [_ text]]
{:db (assoc-in (:db cofx) [:ui/contact :contact/new-tag] text)}))
;; search module
(handlers/register-handler-fx

View File

@ -104,7 +104,7 @@
;; but we might want to build a map of hashed pubkeys to pubkeys
;; for this purpose
(hash->pubkey contact-pubkey-or-hash
(contact.db/active (:contacts/contacts db)))
(contact.db/get-active-contacts (:contacts/contacts db)))
(do
(log/warn "failed to lookup contact from hash, not logged in")
contact-pubkey-or-hash)))
@ -212,7 +212,7 @@
(and (valid-notification-payload? rehydrated-payload)
(accounts.db/logged-in? cofx)
(some #(= (:public-key %) from)
(contact.db/active (:contacts/contacts db)))
(contact.db/get-active-contacts (:contacts/contacts db)))
(some #(= (:chat-id %) from)
(vals (:chats db)))))

View File

@ -4,6 +4,7 @@
[status-im.i18n :as i18n]
[status-im.utils.fx :as fx]
[status-im.contact.device-info :as device-info]
[status-im.contact.db :as contact.db]
[status-im.ui.screens.navigation :as navigation]
[status-im.utils.config :as config]
[status-im.utils.platform :as utils.platform]
@ -54,15 +55,16 @@
:payload payload}}))
(defn merge-contact [local remote]
(let [[old-contact new-contact] (sort-by :last-updated [remote local])]
;;TODO we don't sync contact/blocked for now, it requires more complex handling
(let [remove (update remote :system-tags disj :contact/blocked)
[old-contact new-contact] (sort-by :last-updated [remote local])]
(-> local
(merge new-contact)
(assoc :device-info (device-info/merge-info (:last-updated new-contact)
(:device-info old-contact)
(vals (:device-info new-contact))))
(assoc :pending? (boolean
(and (:pending? local true)
(:pending? remote true)))))))
(vals (:device-info new-contact)))
;; we only take system tags from the newest contact version
:system-tags (:system-tags new-contact)))))
(def merge-contacts (partial merge-with merge-contact))
@ -123,8 +125,16 @@
(transport.pairing/SyncInstallation. {} account {})))
(defn- contact-batch->sync-installation-message [batch]
(let [contacts-to-sync (reduce (fn [acc {:keys [public-key] :as contact}]
(assoc acc public-key (dissoc contact :photo-path)))
(let [contacts-to-sync
(reduce (fn [acc {:keys [public-key system-tags] :as contact}]
(assoc acc
public-key
(cond-> (-> contact
(dissoc :photo-path)
(update :system-tags disj :contact/blocked))
;; for compatibility with version < contact.v7
(contact.db/added? contact) (assoc :pending? false)
(contact.db/pending? contact) (assoc :pending? true))))
{}
batch)]
(transport.pairing/SyncInstallation. contacts-to-sync {} {})))
@ -140,9 +150,7 @@
(defn sync-installation-messages [{:keys [db] :as cofx}]
(let [contacts (:contacts/contacts db)
contact-batches (partition-all contact-batch-n (->> contacts
vals
(remove :dapp?)))]
contact-batches (partition-all contact-batch-n (vals contacts))]
(concat (mapv contact-batch->sync-installation-message contact-batches)
[(sync-installation-account-message cofx)]
@ -221,6 +229,16 @@
:chat-id chat-id})]
(send-installation-message-fx cofx sync-message)))
(fx/defn sync-contact
[cofx {:keys [public-key] :as contact}]
(let [sync-message (transport.pairing/SyncInstallation.
{public-key (cond-> contact
;; for compatibility with version < contact.v7
(contact.db/added? contact) (assoc :pending? false)
(contact.db/pending? contact) (assoc :pending? true))}
{} {})]
(send-installation-message-fx cofx sync-message)))
(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)

View File

@ -25,6 +25,17 @@
:chat-id :global/not-empty-string))
(spec/def :transport/filter any?)
(spec/def :pairing/pending? boolean?)
(spec/def :pairing/contact (spec/keys :req-un [:contact/public-key
:contact/name
:contact/address
:contact/system-tags]
:opt-un [:contact/last-updated
:contact/last-online
:contact/fcm-token
:pairing/pending?
:contact/tags]))
(spec/def :pairing/contacts (spec/nilable (spec/map-of :global/not-empty-string :pairing/contact)))
(spec/def :pairing/installation-id :global/not-empty-string)
(spec/def :pairing/device-type :global/not-empty-string)
@ -93,7 +104,7 @@
(spec/def :message/message-seen (spec/keys :req-un [:message/ids]))
(spec/def :message/group-membership-update (spec/keys :req-un [:group-chat/membership-updates :group-chat/chat-id]))
(spec/def :message/sync-installation (spec/keys :req-un [:contacts/contacts]))
(spec/def :message/sync-installation (spec/keys :req-un [:pairing/contacts]))
(spec/def :message/pair-installation (spec/keys :req-un [:pairing/installation-id
:pairing/device-type]))

View File

@ -32,28 +32,24 @@
[react/view online-dot-left]
[react/view online-dot-right]]]])
(defview pending-contact-badge
[chat-id {:keys [pending-wrapper pending-outer-circle pending-inner-circle]}]
(letsubs [pending-contact? [:get-in [:contacts/contacts chat-id :pending?]]]
(when pending-contact?
(defn pending-contact-badge
[{:keys [pending-wrapper pending-outer-circle pending-inner-circle]}]
[react/view pending-wrapper
[react/view pending-outer-circle
[react/view pending-inner-circle]]])))
[react/view pending-inner-circle]]])
(defn chat-icon-view
[chat-id _group-chat name _online styles & [hide-dapp?]]
(let [photo-path (re-frame.core/subscribe [:contacts/chat-photo chat-id])
dapp? (re-frame.core/subscribe [:get-in [:contacts/contacts chat-id :dapp?]])]
[{:keys [photo-path added?] :as contact} _group-chat name _online styles & [hide-dapp?]]
[react/view (:container styles)
(if-not (string/blank? @photo-path)
[photos/photo @photo-path styles]
(if-not (string/blank? photo-path)
[photos/photo photo-path styles]
[default-chat-icon name styles])
(when (and @dapp? (not hide-dapp?))
[dapp-badge styles])
[pending-contact-badge chat-id styles]]))
(when (and contact (not added?))
[pending-contact-badge styles])])
(defn chat-icon-view-toolbar [chat-id group-chat name color online]
[chat-icon-view chat-id group-chat name online
(defn chat-icon-view-toolbar
[contact group-chat name color online]
[chat-icon-view contact group-chat name online
{:container styles/container-chat-toolbar
:online-view-wrapper styles/online-view-wrapper
:online-view styles/online-view
@ -67,8 +63,9 @@
:default-chat-icon (styles/default-chat-icon-chat-toolbar color)
:default-chat-icon-text styles/default-chat-icon-text}])
(defn chat-icon-view-chat-list [chat-id group-chat name color online & [hide-dapp?]]
[chat-icon-view chat-id group-chat name online
(defn chat-icon-view-chat-list
[contact group-chat name color online & [hide-dapp?]]
[chat-icon-view contact group-chat name online
{:container styles/container-chat-list
:online-view-wrapper styles/online-view-wrapper
:online-view styles/online-view
@ -83,14 +80,14 @@
:default-chat-icon-text styles/default-chat-icon-text}
hide-dapp?])
(defn contact-icon-view [{:keys [photo-path name dapp?]} {:keys [container] :as styles}]
(let [photo-path photo-path]
(defn contact-icon-view
[{:keys [photo-path name dapp?]} {:keys [container] :as styles}]
[react/view container
(if-not (string/blank? photo-path)
[photos/photo photo-path styles]
[default-chat-icon name styles])
(when dapp?
[dapp-badge styles])]))
[dapp-badge styles])])
(defn contact-icon-contacts-tab [contact]
[contact-icon-view contact

View File

@ -51,7 +51,7 @@
(i18n/label-pluralize cnt :t/members-active))))]]))
(defview toolbar-content-view []
(letsubs [{:keys [group-chat color online contacts chat-name
(letsubs [{:keys [group-chat color online contacts chat-name contact
public? chat-id] :as chat} [:chats/current-chat]
show-actions? [:chats/current-chat-ui-prop :show-actions?]
accounts [:accounts/accounts]
@ -59,7 +59,7 @@
(let [has-subtitle? (or group-chat (not= :done sync-state))]
[react/view {:style st/toolbar-container}
[react/view {:margin-right 8}
[chat-icon.screen/chat-icon-view-toolbar chat-id group-chat chat-name color online]]
[chat-icon.screen/chat-icon-view-toolbar contact group-chat chat-name color online]]
[react/view {:style st/chat-name-view}
[react/text {:style st/chat-name-text
:number-of-lines 1

View File

@ -1,7 +1,7 @@
(ns status-im.ui.screens.chat.views
(:require [re-frame.core :as re-frame]
[status-im.chat.models :as models.chat]
[status-im.contact.core :as models.contact]
[status-im.contact.db :as contact.db]
[status-im.group-chats.db :as group-chats.db]
[status-im.i18n :as i18n]
[status-im.ui.components.animation :as animation]
@ -62,7 +62,7 @@
:handler #(on-options chat-id chat-name group-chat public?)}]])]
[connectivity/connectivity-view]
(when (and (not group-chat)
(models.contact/can-add-to-contacts? contact))
(not (contact.db/added? contact)))
[add-contact-bar chat-id])]))
(defmulti message-row (fn [{{:keys [type]} :row}] type))

View File

@ -209,9 +209,9 @@
:margin-top 10
:margin-bottom 5})
(defn chat-title-and-type [pending?]
(defn chat-title-and-type [added?]
{:flex 1
:justify-content (if pending? :flex-start :center)})
:justify-content (if added? :center :flex-start)})
(def chat-title
{:margin-bottom 4

View File

@ -31,7 +31,7 @@
(defn toolbar-chat-view
[{:keys [chat-id chat-name contact color public-key public? group-chat]
:as current-chat}]
(let [{:keys [pending? public-key photo-path]} contact]
(let [{:keys [added? public-key photo-path]} contact]
[react/view {:style styles/toolbar-chat-view}
[react/view {:style {:flex-direction :row
:flex 1}}
@ -41,10 +41,11 @@
(string/capitalize (second chat-name))]]
[react/image {:style styles/chat-icon
:source {:uri photo-path}}])
[react/view {:style (styles/chat-title-and-type pending?)}
[react/view {:style (styles/chat-title-and-type added?)}
[react/text {:style styles/chat-title}
chat-name]
(cond pending?
(cond
(and (not group-chat) (not added?))
[react/text {:style styles/add-contact-text
:on-press #(re-frame/dispatch [:contact.ui/add-to-contact-pressed public-key])}
(i18n/label :t/add-to-contacts)]
@ -375,23 +376,23 @@
(views/letsubs [identity [:contacts/current-contact-identity]
maybe-contact [:contacts/current-contact]]
(let [contact (or maybe-contact (contact.db/public-key->new-contact identity))
{:keys [pending? public-key]} contact]
{:keys [added? public-key]} contact]
[react/view {:style styles/chat-profile-body}
[profile.views/profile-badge contact]
;; for private chat, public key will be chat-id
[react/view
(if (or (nil? pending?) pending?)
(if added?
[react/view {:style styles/chat-profile-row}
[react/view {:style styles/chat-profile-icon-container
:accessibility-label :add-contact-link}
[vector-icons/icon :main-icons/add {:style (styles/chat-profile-icon colors/gray)}]]
[react/text {:style (styles/contact-card-text colors/gray)} (i18n/label :t/in-contacts)]]
[react/touchable-highlight {:on-press #(re-frame/dispatch [:contact.ui/add-to-contact-pressed public-key])}
[react/view {:style styles/chat-profile-row}
[react/view {:style styles/chat-profile-icon-container
:accessibility-label :add-contact-link}
[vector-icons/icon :main-icons/add {:style (styles/chat-profile-icon colors/blue)}]]
[react/text {:style (styles/contact-card-text colors/blue)} (i18n/label :t/add-to-contacts)]]]
[react/view {:style styles/chat-profile-row}
[react/view {:style styles/chat-profile-icon-container
:accessibility-label :add-contact-link}
[vector-icons/icon :main-icons/add {:style (styles/chat-profile-icon colors/gray)}]]
[react/text {:style (styles/contact-card-text colors/gray)} (i18n/label :t/in-contacts)]])
[react/text {:style (styles/contact-card-text colors/blue)} (i18n/label :t/add-to-contacts)]]])
[react/touchable-highlight
{:on-press #(re-frame/dispatch
[:contact.ui/send-message-pressed {:public-key public-key}])}

View File

@ -1,5 +1,6 @@
(ns status-im.ui.screens.group.subs
(:require [re-frame.core :refer [reg-sub]]
[status-im.contact.db :as contact.db]
[status-im.utils.subs :as utils]))
(reg-sub
@ -10,8 +11,9 @@
:is-participant-selected?
(utils/contains-sub :selected-participants))
(defn filter-selected-contacts [selected-contacts contacts]
(remove #(true? (:pending? (contacts %))) selected-contacts))
(defn filter-selected-contacts
[selected-contacts contacts]
(filter #(contact.db/added? (contacts %)) selected-contacts))
(reg-sub
:selected-contacts-count

View File

@ -94,7 +94,7 @@
[{:keys [chat-id chat-name
name color online
group-chat public?
public-key
public-key contact
timestamp
last-message-content
last-message-content-type]}]
@ -102,7 +102,7 @@
[react/touchable-highlight {:on-press #(re-frame/dispatch [:chat.ui/navigate-to-chat chat-id])}
[react/view styles/chat-container
[react/view styles/chat-icon-container
[chat-icon.screen/chat-icon-view-chat-list chat-id group-chat truncated-chat-name color online false]]
[chat-icon.screen/chat-icon-view-chat-list contact group-chat truncated-chat-name color online false]]
[react/view styles/chat-info-container
[react/view styles/item-upper-container
[chat-list-item-name truncated-chat-name group-chat public? public-key]
@ -127,7 +127,8 @@
(defn home-list-browser-item-inner-view [{:keys [dapp url name browser-id]}]
(let [photo-path (:photo-path dapp)]
[list-item/list-item (merge
[list-item/list-item
(merge
{:title name
:subtitle (or url (i18n/label :t/dapp))
:on-press #(re-frame/dispatch [:browser.ui/browser-item-selected browser-id])}

View File

@ -1,6 +1,5 @@
(ns status-im.ui.screens.profile.contact.views
(:require [re-frame.core :as re-frame]
[status-im.contact.db :as contact.db]
[status-im.i18n :as i18n]
[status-im.ui.components.list.views :as list]
[status-im.ui.components.react :as react]
@ -16,16 +15,16 @@
toolbar/default-nav-back
[toolbar/content-title ""]])
(defn actions [{:keys [pending? public-key]}]
(concat (if (or (nil? pending?) pending?)
[{:label (i18n/label :t/add-to-contacts)
:icon :main-icons/add-contact
:action #(re-frame/dispatch [:contact.ui/add-to-contact-pressed public-key])
:accessibility-label :add-to-contacts-button}]
(defn actions [{:keys [public-key added?]}]
(concat (if added?
[{:label (i18n/label :t/in-contacts)
:icon :main-icons/in-contacts
:disabled? true
:accessibility-label :in-contacts-button}])
:accessibility-label :in-contacts-button}]
[{:label (i18n/label :t/add-to-contacts)
:icon :main-icons/add-contact
:action #(re-frame/dispatch [:contact.ui/add-to-contact-pressed public-key])
:accessibility-label :add-to-contacts-button}])
[{:label (i18n/label :t/send-message)
:icon :main-icons/message
:action #(re-frame/dispatch [:contact.ui/send-message-pressed {:public-key public-key}])
@ -77,9 +76,7 @@
:icon-opts styles/block-action-icon-opts}])
(defview profile []
(letsubs [identity [:contacts/current-contact-identity]
maybe-contact [:contacts/current-contact]]
(let [contact (or maybe-contact (contact.db/public-key->new-contact identity))]
(letsubs [contact [:contacts/current-contact]]
[react/view profile.components.styles/profile
[status-bar/status-bar]
[profile-contact-toolbar]
@ -98,4 +95,4 @@
[react/view {:style {:height 16}}]
[block-contact-action contact]
[react/view styles/contact-profile-info-container
[profile-info contact]]]])))
[profile-info contact]]]]))

View File

@ -5,7 +5,6 @@
(deftest on-account-created
(let [result (models/on-account-created {:random-guid-generator (constantly "")
:signing-phrase ""
:status ""
:db {}}
{:pubkey "04de2e21f1642ebee03b9aa4bf1936066124cc89967eaf269544c9b90c539fd5c980166a897d06dd4d3732b38116239f63c89395a8d73eac72881fab802010cb56"
:address "7e92236392a850980d00d0cd2a4b92886bd7fe7b"
@ -22,7 +21,6 @@
:rinkeby #{:MOKSHA :KDO}, :xdai #{}, :poa #{}}}},
:networks nil,
:photo-path "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAMAAAC7IEhfAAADAFBMVEX////YjowAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACRS2MAAABAHRSTlP//wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKmfXxgAABnNJREFUeNoBaAaX+QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAQEBAQEBAQEBAQEBAQEBAQEBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAEBAQEBAQEBAQEBAQEBAQEBAQEBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQEBAQEBAQEBAQEBAQEBAQEBAQEAAAAAAAAAAAAAAAAAAAAAAAAAAAABAQEBAQEBAQEBAQEBAQEBAQEBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAEBAQEBAQEBAQEBAQEBAQEBAQEBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQEBAQEBAQEBAQEBAQEBAQEBAQEAAAAAAAAAAAAAAAAAAAAAAAAAAAABAQEBAQEBAQEBAQEBAQEBAQEBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAEBAQEBAQEBAQEBAQEBAQEBAQEBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQEBAQAAAAAAAAAAAAAAAAEBAQEAAAAAAAAAAAAAAAAAAAAAAAAAAAABAQEBAAAAAAAAAAAAAAAAAQEBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAEBAQEAAAAAAAAAAAAAAAABAQEBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQEBAQAAAAAAAAAAAAAAAAEBAQEAAAAAAAAAAAAAAAAAAAAAAAAAAAABAQEBAAAAAAEBAQEAAAAAAQEBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAEBAQEAAAAAAQEBAQAAAAABAQEBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQEBAQAAAAABAQEBAAAAAAEBAQEAAAAAAAAAAAAAAAAAAAAAAAAAAAABAQEBAAAAAAEBAQEAAAAAAQEBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQEBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAQEBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEBAQEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQEBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAoRUBAbwwC1cAAAAASUVORK5CYII=",
:status "",
:seed-backed-up? true,
:network "mainnet_rpc",
:public-key "04de2e21f1642ebee03b9aa4bf1936066124cc89967eaf269544c9b90c539fd5c980166a897d06dd4d3732b38116239f63c89395a8d73eac72881fab802010cb56",

View File

@ -2,22 +2,16 @@
(:require [cljs.test :refer-macros [deftest is testing]]
[status-im.chat.db :as s]))
(deftest chat-name
(deftest group-chat-name
(testing "it prepends # if it's a public chat"
(is (= "#withhash" (s/chat-name {:group-chat true
(is (= "#withhash" (s/group-chat-name {:group-chat true
:chat-id "1"
:public? true
:name "withhash"} nil))))
:name "withhash"}))))
(testing "it leaves the name unchanged if it's a group chat"
(is (= "unchanged" (s/chat-name {:group-chat true
(is (= "unchanged" (s/group-chat-name {:group-chat true
:chat-id "1"
:name "unchanged"} nil))))
(testing "it pulls the name from contact if it's a one-to-one"
(is (= "this-one" (s/chat-name {:chat-id "1"
:name "not-this-one"} {:name "this-one"}))))
(testing "it generates the name from chat id if no contact"
(is (= "Blond Cooperative Coelacanth" (s/chat-name {:chat-id "1"
:name "not-this-one"} nil)))))
:name "unchanged"})))))
(deftest message-stream-tests
(testing "messages with no interspersed datemarks"
@ -121,11 +115,11 @@
(is (not (:display-username? actual-m1))))))))
(deftest active-chats-test
(let [active-chat-1 {:is-active true :chat-id 1}
active-chat-2 {:is-active true :chat-id 2}
chats {1 active-chat-1
2 active-chat-2
3 {:is-active false :chat-id 3}}]
(let [active-chat-1 {:is-active true :chat-id "1"}
active-chat-2 {:is-active true :chat-id "2"}
chats {"1" active-chat-1
"2" active-chat-2
"3" {:is-active false :chat-id "3"}}]
(testing "it returns only chats with is-active"
(is (= #{1 2}
(is (= #{"1" "2"}
(set (keys (s/active-chats {} chats {}))))))))

View File

@ -37,8 +37,9 @@
(deftest stop-listening
(testing "the user is in our contacts"
(testing "it does not remove transport"
(is (not (contact-code/stop-listening {:db {:contacts/contacts
{chat-id {:pending? false}}}}
(is (not (contact-code/stop-listening
{:db {:contacts/contacts
{chat-id {:system-tags #{:contact/added}}}}}
chat-id)))))
(testing "the user is not in our contacts"
(testing "the user is not in any group chats or 1-to1-"

View File

@ -12,18 +12,15 @@
admins #{"0x04fcf40c526b09ff9fb22f4a5dbd08490ef9b64af700870f8a0ba2133f4251d5607ed83cd9047b8c2796576bc83fa0de23a13a4dced07654b8ff137fe744047917"}
contacts {"0x04985040682b77a32bb4bb58268a0719bd24ca4d07c255153fe1eb2ccd5883669627bd1a092d7cc76e8e4b9104327667b19dcda3ac469f572efabe588c38c1985f"
{:description nil,
:last-updated 0,
{:last-updated 0,
:address "eca8218b5ebeb2c47ba94c1b6e0a779d78fff7bc",
:name "User B",
:fcm-token nil,
:photo-path "photo1",
:status nil,
:blocked? false,
:pending? true,
:last-online 0,
:public-key
"0x04985040682b77a32bb4bb58268a0719bd24ca4d07c255153fe1eb2ccd5883669627bd1a092d7cc76e8e4b9104327667b19dcda3ac469f572efabe588c38c1985f"}}
"0x04985040682b77a32bb4bb58268a0719bd24ca4d07c255153fe1eb2ccd5883669627bd1a092d7cc76e8e4b9104327667b19dcda3ac469f572efabe588c38c1985f"
:system-tags #{}}}
current-account {:last-updated 0,
:address "f23d28f538fd8cd4a90c2d96ca89f5bccca5383f",
:signed-up? true,
@ -40,18 +37,16 @@
:photo-path "generated"
:admin? true
:address "71adb0644e2b590e37dafdfea8bd58f0c7668c7f"
:public-key "0x04fcf40c526b09ff9fb22f4a5dbd08490ef9b64af700870f8a0ba2133f4251d5607ed83cd9047b8c2796576bc83fa0de23a13a4dced07654b8ff137fe744047917"}
:public-key "0x04fcf40c526b09ff9fb22f4a5dbd08490ef9b64af700870f8a0ba2133f4251d5607ed83cd9047b8c2796576bc83fa0de23a13a4dced07654b8ff137fe744047917"
:system-tags #{}}
{:name "User A"
:photo-path "photo2"
:public-key "0x048a2f8b80c60f89a91b4c1316e56f75b087f446e7b8701ceca06a40142d8efe1f5aa36bd0fee9e248060a8d5207b43ae98bef4617c18c71e66f920f324869c09f"}
{:description nil
:last-updated 0
{:last-updated 0
:name "User B"
:fcm-token nil
:photo-path "photo1"
:address "eca8218b5ebeb2c47ba94c1b6e0a779d78fff7bc"
:status nil
:blocked? false
:pending? true
:last-online 0
:public-key "0x04985040682b77a32bb4bb58268a0719bd24ca4d07c255153fe1eb2ccd5883669627bd1a092d7cc76e8e4b9104327667b19dcda3ac469f572efabe588c38c1985f"}]))))))
:public-key "0x04985040682b77a32bb4bb58268a0719bd24ca4d07c255153fe1eb2ccd5883669627bd1a092d7cc76e8e4b9104327667b19dcda3ac469f572efabe588c38c1985f"
:system-tags #{}}]))))))

View File

@ -1,34 +0,0 @@
(ns status-im.test.contacts.events
(:require [cljs.test :refer-macros [deftest is testing]]
reagent.core
[re-frame.core :as rf]
[day8.re-frame.test :refer-macros [run-test-sync]]
status-im.ui.screens.db
status-im.ui.screens.subs
[status-im.ui.screens.events :as events]
[status-im.utils.js-resources :as js-res]))
(def test-contact-group
{:group-id "1501682106404-685e041e-38e7-593e-b42c-fb4cabd7faa4"
:name "Test"
:timestamp 0
:order 0
:pending? false
:contacts (list
{:identity "bchat"}
{:identity "Commiteth"}
{:identity "demo-bot"})})
(def dapps-contact-group
{:group-id "dapps"
:name "ÐApps"
:order 1
:timestamp 0
:contacts [{:identity "oaken-water-meter"}
{:identity "melonport"}
{:identity "bchat"}
{:identity "Dentacoin"}
{:identity "Augur"}
{:identity "Ethlance"}
{:identity "Commiteth"}]
:pending? false})

View File

@ -5,21 +5,6 @@
(def public-key "0x04fcf40c526b09ff9fb22f4a5dbd08490ef9b64af700870f8a0ba2133f4251d5607ed83cd9047b8c2796576bc83fa0de23a13a4dced07654b8ff137fe744047917")
(def address "71adb0644e2b590e37dafdfea8bd58f0c7668c7f")
(deftest can-add-to-contact-test
(testing "a user is already in contacts"
(is (not (model/can-add-to-contacts? {:pending? false}))))
(testing "a user is pending"
(testing "a normal user"
(is (model/can-add-to-contacts? {:pending? true})))
(testing "a dapp"
(is (not (model/can-add-to-contacts? {:pending? true
:dapp? true})))))
(testing "the user is not in the contacts"
(testing "a normal user"
(is (model/can-add-to-contacts? {})))
(testing "a dapp"
(is (not (model/can-add-to-contacts? {:dapp? true}))))))
(deftest handle-contact-update-test
(testing "the contact is not in contacts"
(let [actual (model/handle-contact-update
@ -35,19 +20,20 @@
contact (get-in actual [:db :contacts/contacts public-key])]
(testing "it stores the contact in the database"
(is (:data-store/tx actual)))
(testing "it adds a new contact with pending? true"
(testing "it adds a new contact"
(is (= {:public-key public-key
:photo-path "image"
:name "name"
:last-updated 1000
:pending? true
:system-tags #{:contact/request-received}
:device-info {"1" {:id "1"
:timestamp 1
:fcm-token "token-1"}}
:fcm-token "token"
:address "address"} contact)))))
:address "address"}
contact)))))
(testing "the contact is already in contacts"
(testing "timestamp is greather than last-updated"
(testing "timestamp is greater than last-updated"
(let [actual (model/handle-contact-update
public-key
1
@ -70,13 +56,13 @@
"2" {:id "2"
:timestamp 0
:fcm-token "token-2"}}
:pending? false
:system-tags #{:contact/added}
:fcm-token "old-token"
:address "old-address"}}}})
contact (get-in actual [:db :contacts/contacts public-key])]
(testing "it stores the contact in the database"
(is (:data-store/tx actual)))
(testing "it updates the contact leaving pending unchanged"
(testing "it updates the contact and adds contact/request-received to system tags"
(is (= {:public-key public-key
:photo-path "new-image"
:name "new-name"
@ -90,10 +76,11 @@
"3" {:id "3"
:fcm-token "token-3"
:timestamp 1}}
:pending? false
:system-tags #{:contact/added :contact/request-received}
:fcm-token "new-token"
:address "new-address"} contact)))))
(testing "timestamp is equal than last-updated"
:address "new-address"}
contact)))))
(testing "timestamp is equal to last-updated"
(let [actual (model/handle-contact-update
public-key
1
@ -106,7 +93,7 @@
:photo-path "old-image"
:name "old-name"
:last-updated 1000
:pending? false
:system-tags #{:contact/added}
:fcm-token "old-token"
:address "old-address"}}}})
contact (get-in actual [:db :contacts/contacts public-key])]
@ -125,7 +112,7 @@
:photo-path "old-image"
:name "old-name"
:last-updated 1000
:pending? false
:system-tags #{:contact/added :contact/request-received}
:fcm-token "old-token"
:address "old-address"}}}})
contact (get-in actual [:db :contacts/contacts public-key])]
@ -144,18 +131,18 @@
:fcm-token "token-1"}}
:name "old-name"
:last-updated 0
:pending? false}}}})
:system-tags #{:contact/added}}}}})
contact (get-in actual [:db :contacts/contacts public-key])]
(testing "it stores the contact in the database"
(is (:data-store/tx actual)))
(testing "it updates the contact leaving pending unchanged"
(testing "it updates the contact"
(is (= {:public-key public-key
:photo-path "new-image"
:name "new-name"
:device-info {"1" {:id "1"
:fcm-token "token-1"}}
:last-updated 1000
:pending? false
:system-tags #{:contact/added :contact/request-received}
:address address} contact)))))
(testing "the message is coming from us"
(testing "it does not update contacts"

View File

@ -7,16 +7,16 @@
(deftest merge-contact-test
(testing "vanilla contacts"
(let [contact-1 {:pending? false
(let [contact-1 {:system-tags #{:contact/added}
:this-should-be-kept true
:last-updated 1
:name "name-v1"
:photo-path "photo-v1"}
contact-2 {:pending? false
contact-2 {:system-tags #{:contact/added}
:last-updated 2
:name "name-v2"
:photo-path "photo-v2"}
expected {:pending? false
expected {:system-tags #{:contact/added}
:this-should-be-kept true
:last-updated 2
:device-info nil
@ -24,14 +24,14 @@
:photo-path "photo-v2"}]
(is (= expected (pairing/merge-contact contact-1 contact-2)))))
(testing "without last-updated"
(let [contact-1 {:pending? false
(let [contact-1 {:system-tags #{:contact/added}
:name "name-v1"
:photo-path "photo-v1"}
contact-2 {:pending? false
contact-2 {:system-tags #{:contact/added}
:last-updated 2
:name "name-v2"
:photo-path "photo-v2"}
expected {:pending? false
expected {:system-tags #{:contact/added}
:last-updated 2
:device-info nil
:name "name-v2"
@ -39,26 +39,41 @@
(is (= expected (pairing/merge-contact contact-1 contact-2)))))
(testing "nil contact"
(let [contact-1 nil
contact-2 {:pending? false
contact-2 {:system-tags #{:contact/added}
:last-updated 2
:name "name-v2"
:photo-path "photo-v2"}
expected {:pending? false
expected {:system-tags #{:contact/added}
:last-updated 2
:device-info nil
:name "name-v2"
:photo-path "photo-v2"}]
(is (= expected (pairing/merge-contact contact-1 contact-2)))))
(testing "not pending in one device"
(let [contact-1 {:pending? false
(testing "added in one device but updated less recently"
(let [contact-1 {:system-tags #{:contact/added}
:last-updated 1
:name "name-v1"
:photo-path "photo-v1"}
contact-2 {:pending? true
contact-2 {:system-tags #{}
:last-updated 2
:name "name-v2"
:photo-path "photo-v2"}
expected {:pending? false
expected {:system-tags #{}
:last-updated 2
:device-info nil
:name "name-v2"
:photo-path "photo-v2"}]
(is (= expected (pairing/merge-contact contact-1 contact-2)))))
(testing "added in one device updated more recently"
(let [contact-1 {:system-tags #{}
:last-updated 1
:name "name-v1"
:photo-path "photo-v1"}
contact-2 {:system-tags #{:contact/added}
:last-updated 2
:name "name-v2"
:photo-path "photo-v2"}
expected {:system-tags #{:contact/added}
:last-updated 2
:device-info nil
:name "name-v2"
@ -66,18 +81,18 @@
(is (= expected (pairing/merge-contact contact-1 contact-2)))))
(testing "pending in one device and nil"
(let [contact-1 nil
contact-2 {:pending? true
contact-2 {:system-tags #{}
:last-updated 2
:name "name-v2"
:photo-path "photo-v2"}
expected {:pending? true
expected {:system-tags #{}
:last-updated 2
:device-info nil
:name "name-v2"
:photo-path "photo-v2"}]
(is (= expected (pairing/merge-contact contact-1 contact-2)))))
(testing "device-info"
(let [contact-1 {:pending? false
(let [contact-1 {:system-tags #{:contact/added}
:last-updated 1
:name "name-v1"
:device-info {"1" {:timestamp 1
@ -87,7 +102,7 @@
:fcm-token "token-2"
:id "2"}}
:photo-path "photo-v1"}
contact-2 {:pending? false
contact-2 {:system-tags #{:contact/added}
:last-updated 2
:name "name-v2"
:device-info {"2" {:timestamp 2
@ -97,7 +112,7 @@
:fcm-token "token-3"
:id "3"}}
:photo-path "photo-v2"}
expected {:pending? false
expected {:system-tags #{:contact/added}
:last-updated 2
:device-info {"1" {:timestamp 1
:fcm-token "token-1"
@ -119,38 +134,38 @@
:public-key "contact-1"
:last-updated 0
:photo-path "old-contact-1"
:pending? true}
:system-tags #{}}
new-contact-1 {:name "new-contact-one"
:public-key "contact-1"
:last-updated 1
:photo-path "new-contact-1"
:pending? false}
:system-tags #{:contact/added}}
old-contact-2 {:name "old-contact-2"
:public-key "contact-2"
:last-updated 0
:photo-path "old-contact-2"
:pending? false}
:system-tags #{:contact/added}}
new-contact-2 {:name "new-contact-2"
:public-key "contact-2"
:last-updated 1
:photo-path "new-contact-2"
:pending? false}
:system-tags #{:contact/added}}
contact-3 {:name "contact-3"
:public-key "contact-3"
:photo-path "contact-3"
:pending? false}
:system-tags #{:contact/added}}
contact-4 {:name "contact-4"
:public-key "contact-4"
:pending? true}
:system-tags #{}}
local-contact-5 {:name "contact-5"
:photo-path "local"
:public-key "contact-5"
:pending? true
:system-tags #{}
:last-updated 1}
remote-contact-5 {:name "contact-5"
:public-key "contact-5"
:photo-path "remote"
:pending? true
:system-tags #{}
:last-updated 1}
cofx {:db {:account/account {:public-key "us"}
:contacts/contacts {"contact-1" old-contact-1
@ -236,26 +251,36 @@
:is-active true
:chat-id "status"}}
:contacts/contacts {"contact-1" {:name "contact-1"
:public-key "contact-1"}
:public-key "contact-1"
:system-tags #{}}
"contact-2" {:name "contact-2"
:public-key "contact-2"}
:public-key "contact-2"
:system-tags #{}}
"contact-3" {:name "contact-3"
:public-key "contact-3"}
:public-key "contact-3"
:system-tags #{}}
"contact-4" {:name "contact-4"
:public-key "contact-4"}
:public-key "contact-4"
:system-tags #{}}
"contact-5" {:name "contact-5"
:public-key "contact-5"}}}}
:public-key "contact-5"
:system-tags #{:contact/blocked}}}}}
expected [(transport.pairing/SyncInstallation. {"contact-1" {:name "contact-1"
:public-key "contact-1"}
:public-key "contact-1"
:system-tags #{}}
"contact-2" {:name "contact-2"
:public-key "contact-2"}
:public-key "contact-2"
:system-tags #{}}
"contact-3" {:name "contact-3"
:public-key "contact-3"}
:public-key "contact-3"
:system-tags #{}}
"contact-4" {:name "contact-4"
:public-key "contact-4"}}
:public-key "contact-4"
:system-tags #{}}}
{} {})
(transport.pairing/SyncInstallation. {"contact-5" {:name "contact-5"
:public-key "contact-5"}} {} {})
:public-key "contact-5"
:system-tags #{}}} {} {})
(transport.pairing/SyncInstallation. {} {:photo-path "photo-path"
:name "name"
:last-updated 1} {})

View File

@ -1,37 +1,22 @@
(ns status-im.test.sign-in.data)
(def all-contacts
[{:description nil
:last-updated 1547185503000
[{:last-updated 1547185503000
:tags #{}
:address "2f88d65f3cb52605a54a833ae118fb1363acccd2"
:name "Darkviolet Lightgreen Halcyon"
:fcm-token "cwigXoAk9R4:APA91bFZOy8vsCj9I9t6PYZXropyYEqAhKaVD2GxrURwvxe_Ay3zLrtJxeirp69se_5EOjS5i4T9xQnoPWrFTLfU9U7AUBdjxZtq5cnlP005bOY05p-psxGsQThMKQMeP5DJC9uxN0Ei"
:dapp-url nil
:dapp-hash nil
:photo-path "data:image/png;base64iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAMAAAC7IEhfAAADAFBMVEX///+M2KwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADdPOdBAAABAHRSTlP//wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKmfXxgAABnNJREFUeNoBaAaX+QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAQEBAQEBAQEBAQEBAQEBAQEBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAEBAQEBAQEBAQEBAQEBAQEBAQEBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQEBAQEBAQEBAQEBAQEBAQEBAQEAAAAAAAAAAAAAAAAAAAAAAAAAAAABAQEBAQEBAQEBAQEBAQEBAQEBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAQEBAQEBAQEBAQEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEBAQEBAQEBAQEBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQEBAQEBAQEBAQEBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAQEBAQEBAQEBAQEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAQEBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEBAQEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQEBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAQEBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAQEBAQEBAQEBAQEBAQEBAQEBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAEBAQEBAQEBAQEBAQEBAQEBAQEBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQEBAQEBAQEBAQEBAQEBAQEBAQEAAAAAAAAAAAAAAAAAAAAAAAAAAAABAQEBAQEBAQEBAQEBAQEBAQEBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAQEBAAAAAAEBAQEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEBAQEAAAAAAQEBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQEBAQAAAAABAQEBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAQEBAAAAAAEBAQEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAY5UBARL8TK8AAAAASUVORK5CYII="
:debug? false
:status nil
:bot-url nil
:pending? false
:system-tags #{:contact/added}
:last-online 0
:dapp? false
:public-key "0x04d6e56a475cd35f512d6ce0bf76c2c2af435c85ff48c2b9bdefd129f620e051a436f50961eae5717b2a750e59c3f5b60647d927da46d0b8b11621640b5678fc24"}
{:description nil
:last-updated 1547271764000
:tags #{}
{:last-updated 1547271764000
:address "b267ff8336ac10b3a1986c04a70ff91fb03d0b78"
:name "rv"
:fcm-token "dpVPtMBLuv8:APA91bEU4YuSz9yrc-vsiSl-IjdLSR5UpHm7yffaFlWQs_fvsTiK18ZcdYUbzA8iUoNuMVRNF_ngU7JdQInwNpXdGtNv_qcAFt0jhXHqf7dWY-kGJUBw9Ma8G_2fa40JLJchGVrzUIen"
:dapp-url nil
:dapp-hash nil
:photo-path "data:image/png;base64iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAMAAAC7IEhfAAADAFBMVEX////VjNgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwYzy6AAABAHRSTlP//wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKmfXxgAABnNJREFUeNoBaAaX+QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQEBAQEBAQEBAQEBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAQEBAQEBAQEBAQEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEBAQEBAQEBAQEBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQEBAQEBAQEBAQEBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEBAQEBAQEBAQEBAQEBAQEBAQEBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQEBAQEBAQEBAQEBAQEBAQEBAQEAAAAAAAAAAAAAAAAAAAAAAAAAAAABAQEBAQEBAQEBAQEBAQEBAQEBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAEBAQEBAQEBAQEBAQEBAQEBAQEBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAQEBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEBAQEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQEBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAQEBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEBAQEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQEBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAQEBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEBAQEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEBAQEBAQEBAAAAAAEBAQEBAQEBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQEBAQEBAQEAAAAAAQEBAQEBAQEAAAAAAAAAAAAAAAAAAAAAAAAAAAABAQEBAQEBAQAAAAABAQEBAQEBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAEBAQEBAQEBAAAAAAEBAQEBAQEBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA6IYA4bRtf+EAAAAASUVORK5CYII="
:debug? false
:status nil
:bot-url nil
:pending? false
:system-tags #{:contact/added}
:last-online 0
:dapp? false
:public-key "0x043ae31038ff45a31b096a91d3f8290e079366fbbae76a00fbbd349cd0e5b8d7598965d206772ec4504f68908649a08383cdc51a52cdae5e9ccc744ace4d37020f"}])
(def chats
@ -170,7 +155,6 @@
:rpc-url nil}}
:photo-path "data:image/png;base64iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAMAAAC7IEhfAAADAFBMVEX////YsYwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABPGFwxAAABAHRSTlP//wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKmfXxgAABnNJREFUeNoBaAaX+QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEBAQEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQEBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAQEBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEBAQEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEBAQEAAAAAAQEBAQAAAAABAQEBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQEBAQAAAAABAQEBAAAAAAEBAQEAAAAAAAAAAAAAAAAAAAAAAAAAAAABAQEBAAAAAAEBAQEAAAAAAQEBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAEBAQEAAAAAAQEBAQAAAAABAQEBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQEBAQAAAAABAQEBAAAAAAEBAQEAAAAAAAAAAAAAAAAAAAAAAAAAAAABAQEBAAAAAAEBAQEAAAAAAQEBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAEBAQEAAAAAAQEBAQAAAAABAQEBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQEBAQAAAAABAQEBAAAAAAEBAQEAAAAAAAAAAAAAAAAAAAAAAAAAAAABAQEBAAAAAAEBAQEAAAAAAQEBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAEBAQEAAAAAAQEBAQAAAAABAQEBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQEBAQAAAAABAQEBAAAAAAEBAQEAAAAAAAAAAAAAAAAAAAAAAAAAAAABAQEBAAAAAAEBAQEAAAAAAQEBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAEBAQEBAQEBAAAAAAEBAQEBAQEBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQEBAQEBAQEAAAAAAQEBAQEBAQEAAAAAAAAAAAAAAAAAAAAAAAAAAAABAQEBAQEBAQAAAAABAQEBAQEBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAEBAQEBAQEBAAAAAAEBAQEBAQEBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAloYA4a9rBHIAAAAASUVORK5CYII="
:debug? false
:status "success is simply the wisdom born out of so called failures"
:extensions {}
:mainnet-warning-shown? false
:last-sign-in 1547271706793