contacts handlers

This commit is contained in:
Roman Volosovskyi 2016-05-12 12:56:47 +03:00
parent c5c71691c3
commit 73d3fdb3e6
12 changed files with 143 additions and 171 deletions

View File

@ -64,9 +64,9 @@
(defn on-action-selected [position]
(case position
0 (dispatch [:show-add-participants #_navigator])
1 (dispatch [:show-remove-participants #_navigator])
2 (dispatch [:leave-group-chat #_navigator])))
0 (dispatch [:show-add-participants])
1 (dispatch [:show-remove-participants])
2 (dispatch [:leave-group-chat])))
(defn overlay [{:keys [on-click-outside]} items]
[view st/actions-overlay
@ -97,21 +97,18 @@
:icon :menu_group
:icon-style {:width 25
:height 19}
:handler nil #_#(dispatch [:show-add-participants
navigator])}
:handler #(dispatch [:show-add-participants])}
{:title "Remove Contact from chat"
:subtitle "Alex, John"
:icon :search_gray_copy
:icon-style {:width 17
:height 17}
:handler nil #_#(dispatch
[:show-remove-participants navigator])}
:handler #(dispatch [:show-remove-participants])}
{:title "Leave Chat"
:icon :muted
:icon-style {:width 18
:height 21}
:handler nil #_#(dispatch [:leave-group-chat
navigator])}
:handler #(dispatch [:leave-group-chat])}
{:title "Settings"
:subtitle "Not implemented"
:icon :settings

View File

@ -31,7 +31,7 @@
:autoFocus true
:placeholder "Type your search tags here"
:onSubmitEditing (fn [e]
(let [search (aget e "nativeEvent" "text")
(let [search (aget e "nativeEvent" "text")
hashtags (get-hashtags search)]
(dispatch [:broadcast-status search hashtags])))}]
[view
@ -50,25 +50,26 @@
(dispatch [:updated-discoveries]))))
(defn discovery [{:keys [navigator]}]
[]
(let [showSearch (r/atom false)]
(fn []
[view {:style {:flex 1
:backgroundColor "#eef2f5"}}
[toolbar {:style st/discovery-toolbar
:navigator navigator
:nav-action {:image {:source {:uri "icon_hamburger"}
:style {:width 16
:height 12}}
[toolbar {:style st/discovery-toolbar
:navigator navigator
:nav-action {:image {:source {:uri "icon_hamburger"}
:style {:width 16
:height 12}}
:handler create-fake-discovery}
:title "Add Participants"
:content (title-content @showSearch)
:action {:image {:source {:uri "icon_search"}
:style {:width 17
:height 17}}
:handler (fn []
(if @showSearch
(reset! showSearch false)
(reset! showSearch true)))}}]
:title "Add Participants"
:content (title-content @showSearch)
:action {:image {:source {:uri "icon_search"}
:style {:width 17
:height 17}}
:handler (fn []
(if @showSearch
(reset! showSearch false)
(reset! showSearch true)))}}]
[scroll-view {:style {}}
[view {:style st/section-spacing}
[text {:style st/discovery-subtitle} "Popular tags"]]
@ -76,7 +77,3 @@
[view {:style st/section-spacing}
[text {:style st/discovery-subtitle} "Recent"]]
[discovery-recent]]])))
(comment
(def page-width (aget (natal-shell.dimensions/get "window") "width"))
(def page-height (aget (natal-shell.dimensions/get "window") "height"))
)

View File

@ -1,6 +1,15 @@
(ns syng-im.contacts.handlers
(:require [re-frame.core :refer [register-handler after]]
[syng-im.models.contacts :as contacts]))
(:require [re-frame.core :refer [register-handler after dispatch]]
[syng-im.models.contacts :as contacts]
[syng-im.utils.crypt :refer [encrypt]]
[clojure.string :as s]
[syng-im.utils.utils :refer [http-post]]
[syng-im.utils.phone-number :refer [format-phone-number]]))
(defn side-effect! [handler]
(fn [db params]
(handler db params)
db))
(defn save-contact
[_ [_ contact]]
@ -11,4 +20,82 @@
(update db :contacts conj contact))
((after save-contact))))
(defn load-contacts! [db _]
(let [contacts (contacts/get-contacts)]
(assoc db :contacts contacts)))
(register-handler :load-syng-contacts load-contacts!)
(def react-native-contacts (js/require "react-native-contacts"))
(defn contact-name [contact]
(->> contact
((juxt :givenName :middleName :familyName))
(remove s/blank?)
(s/join " ")))
(defn normalize-phone-contacts [contacts]
(let [contacts' (js->clj contacts :keywordize-keys true)]
(map (fn [{:keys [thumbnailPath phoneNumbers] :as contact}]
{:name (contact-name contact)
:photo-path thumbnailPath
:phone-numbers phoneNumbers}) contacts')))
(defn fetch-contacts-from-phone!
[_ _]
(.getAll react-native-contacts
(fn [error contacts]
(if error
(dispatch [:error-on-fetching-loading error])
(let [contacts' (normalize-phone-contacts contacts)]
(dispatch [:get-contacts-identities contacts']))))))
(register-handler :sync-contacts
(side-effect! fetch-contacts-from-phone!))
(defn get-contacts-by-hash [contacts]
(->> contacts
(mapcat (fn [{:keys [phone-numbers] :as contact}]
(map (fn [{:keys [number]}]
(let [number' (format-phone-number number)]
[(encrypt number')
(-> contact
(assoc :phone-number number')
(dissoc :phone-numbers))]))
phone-numbers)))
(into {})))
(defn add-identity [contacts-by-hash contacts]
(map (fn [{:keys [phone-number-hash whisper-identity]}]
(let [contact (contacts-by-hash phone-number-hash)]
(assoc contact :whisper-identity whisper-identity)))
(js->clj contacts)))
(defn request-stored-contacts [contacts]
(let [contacts-by-hash (get-contacts-by-hash contacts)
data (keys contacts-by-hash)]
(http-post "get-contacts" {:phone-number-hashes data}
(fn [{:keys [contacts]}]
(let [contacts' (add-identity contacts-by-hash contacts)]
(dispatch [:add-contacts contacts']))))))
(defn get-identities-by-contacts! [_ [_ contacts]]
(request-stored-contacts contacts))
(register-handler :get-contacts-identities
(side-effect! get-identities-by-contacts!))
(defn save-contacts! [{:keys [new-contacts]} _]
(contacts/save-syng-contacts new-contacts))
(defn add-new-contacts
[{:keys [contacts] :as db} [_ new-contacts]]
(let [identities (set (map :whisper-identity contacts))
new-contacts' (remove #(identities (:whisper-identity %)) new-contacts)]
(-> db
(update :contacts concat new-contacts')
(assoc :new-contacts new-contacts'))))
(register-handler :add-contacts
(after save-contacts!)
add-new-contacts)

View File

@ -11,10 +11,11 @@
[syng-im.contacts.views.contact :refer [contact-view]]
[syng-im.components.styles :refer [toolbar-background2]]
[syng-im.components.toolbar :refer [toolbar]]
[syng-im.contacts.styles :as st]))
[syng-im.contacts.styles :as st]
[syng-im.utils.listview :as lw]))
(defn render-row [row _ _]
(list-item [contact-view (js->clj row :keywordize-keys true)]))
(list-item [contact-view row]))
(defn get-data-source [contacts]
(clone-with-rows (data-source {:rowHasChanged not=}) contacts))
@ -22,14 +23,14 @@
(defn contact-list-toolbar []
[toolbar {:title "Contacts"
:background-color toolbar-background2
:action {:image {:source {:uri :icon_search}
:style st/search-icon}
:action {:image {:source {:uri :icon_search}
:style st/search-icon}
:handler (fn [])}}])
(defn contact-list []
(let [contacts (subscribe [:get-contacts])]
(let [contacts (subscribe [:get :contacts])]
(fn []
(let [contacts-ds (get-data-source @contacts)]
(let [contacts-ds (lw/to-datasource2 @contacts)]
[view st/contacts-list-container
[contact-list-toolbar]
(when contacts-ds

View File

@ -1,5 +1,3 @@
(ns syng-im.contacts.subs
(:require-macros [reagent.ratom :refer [reaction]])
(:require [re-frame.core :refer [register-sub]]))

View File

@ -7,13 +7,12 @@
(def default-view :discovery)
;; initial state of app-db
(def app-db {:greeting "Hello Clojure in iOS and Android!"
:identity-password "replace-me-with-user-entered-password"
(def app-db {:identity-password "replace-me-with-user-entered-password"
:identity "me"
:contacts []
:current-chat-id "console"
:chat {:command nil
:last-message nil}
:chat {:command nil
:last-message nil}
:chats {}
:chats-updated-signal 0
:show-actions false

View File

@ -14,7 +14,6 @@
message-by-id]]
[syng-im.models.commands :refer [set-commands]]
[syng-im.handlers.server :as server]
[syng-im.handlers.contacts :as contacts-service]
[syng-im.handlers.suggestions :refer [get-command
handle-command
get-command-handler
@ -229,19 +228,6 @@
(user-data/load-phone-number)
db))
;; -- Sign up --------------------------------------------------------------
(register-handler :sync-contacts
(fn [db [_ handler]]
(contacts-service/sync-contacts handler)
db))
;; -- Contacts --------------------------------------------------------------
(register-handler :load-syng-contacts
(fn [db [_ value]]
(contacts/load-syng-contacts db)))
;; -- Chats --------------------------------------------------------------
(defn update-new-participants-selection [db identity add?]
(update db :new-participants (fn [new-participants]

View File

@ -1,64 +0,0 @@
(ns syng-im.handlers.contacts
(:require-macros [cljs.core.async.macros :refer [go]])
(:require [clojure.string :as cstr]
[cljs.core.async :as async :refer [chan put! <!]]
[re-frame.core :refer [subscribe dispatch dispatch-sync]]
[syng-im.utils.utils :refer [log on-error http-post toast]]
[syng-im.utils.crypt :refer [encrypt]]
[syng-im.utils.phone-number :refer [format-phone-number]]
[syng-im.models.contacts :as contacts-model]
[syng-im.utils.logging :as log]))
(defn- get-contact-name [phone-contact]
(cstr/join " "
(remove cstr/blank?
[(:givenName phone-contact)
(:middleName phone-contact)
(:familyName phone-contact)])))
(defn- to-syng-contacts [contacts-by-hash data]
(map (fn [server-contact]
(let [number-info (get contacts-by-hash
(:phone-number-hash server-contact))
phone-contact (:contact number-info)]
{:phone-number (:number number-info)
:whisper-identity (:whisper-identity server-contact)
:name (get-contact-name phone-contact)
:photo-path (:photo-path phone-contact)}))
(js->clj (:contacts data))))
(defn- get-contacts-by-hash [contacts]
(let [numbers-info (reduce (fn [numbers contact]
(into numbers
(map (fn [c]
{:number (format-phone-number (:number c))
:contact contact})
(:phone-numbers contact))))
'()
contacts)]
(reduce (fn [m number-info]
(let [number (:number number-info)
hash (encrypt number)]
(assoc m hash number-info)))
{}
numbers-info)))
(defn- request-syng-contacts [contacts]
(let [contacts-by-hash (get-contacts-by-hash contacts)
data (keys contacts-by-hash)
ch (chan)]
(http-post "get-contacts" {:phone-number-hashes data}
(fn [data]
(put! ch
(to-syng-contacts contacts-by-hash data))))
ch))
(defn sync-contacts [handler]
(go
(let [result (<! (contacts-model/load-phone-contacts))]
(if-let [error (:error result)]
(on-error error)
(let [syng-contacts (<! (request-syng-contacts (:contacts result)))]
(contacts-model/save-syng-contacts syng-contacts)
(dispatch [:load-syng-contacts])
(handler))))))

View File

@ -5,12 +5,9 @@
[syng-im.persistence.realm :as realm]
[syng-im.persistence.realm :as r]
[syng-im.persistence.realm-queries :refer [include-query
exclude-query]]
[clojure.string :as s]))
exclude-query]]))
;; TODO see https://github.com/rt2zz/react-native-contacts/issues/45
(def fake-phone-contacts? false)
(def fake-contacts? false)
(def react-native-contacts (js/require "react-native-contacts"))
@ -26,43 +23,26 @@
(defn- generate-contacts [n]
(map generate-contact (range 1 (inc n))))
(defn load-phone-contacts []
(let [ch (chan)]
(if fake-phone-contacts?
(put! ch {:error nil, :contacts (generate-contacts 10)})
(.getAll react-native-contacts
(fn [error raw-contacts]
(put! ch
{:error error
:contacts
(when (not error)
(log raw-contacts)
(map (fn [contact]
(merge contact
(generate-contact 1)
{:name (:givenName contact)
:photo-path (:thumbnailPath contact)
:phone-numbers (:phoneNumbers contact)}))
(js->clj raw-contacts :keywordize-keys true)))}))))
ch))
(defn load-phone-contacts
([callback] (.getAll react-native-contacts callback))
([]
(.getAll react-native-contacts
(fn [error raw-contacts]
(println raw-contacts)
{:error error
:contacts
(when (not error)
(log raw-contacts)
(map (fn [contact]
(merge contact
(generate-contact 1)
{:name (:givenName contact)
:photo-path (:thumbnailPath contact)
:phone-numbers (:phoneNumbers contact)}))
(js->clj raw-contacts :keywordize-keys true)))}))))
(defn- get-contacts []
(if fake-contacts?
[{:phone-number "123"
:whisper-identity "abc"
:name "fake"
:photo-path ""}]
(realm/get-list :contacts)))
(defn load-syng-contacts [db]
(let [contacts (map (fn [contact]
(merge contact
{:delivery-status (if (< (rand) 0.5) :delivered :seen)
:datetime "15:30"
:new-messages-count (rand-int 3)
:online (< (rand) 0.5)}))
(get-contacts))]
(assoc db :contacts contacts)))
(defn get-contacts []
(realm/collection->map (realm/get-all :contacts)))
(defn- create-contact [{:keys [phone-number whisper-identity name photo-path]}]
(realm/create :contacts

View File

@ -16,10 +16,6 @@
[s]
(keywordize-keys (apply hash-map (split s #"[;=]"))))
(defn select-chat-last-message [chat]
(when-let [last-msg-id (:last-msg-id chat)]
(r/single-cljs (r/get-by-field :msgs :msg-id last-msg-id))))
(defn save-message
[chat-id {:keys [from to msg-id content content-type outgoing
same-author same-direction]

View File

@ -6,8 +6,7 @@
[syng-im.utils.types :refer [to-edn-string]]
[re-frame.db :refer [app-db]]
[syng-im.db :as db]
[syng-im.persistence.simple-kv-store :as kv]
[syng-im.utils.logging :as log]))
[syng-im.persistence.simple-kv-store :as kv]))
(defn set-initialized [db initialized?]
(assoc-in db db/protocol-initialized-path initialized?))
@ -23,8 +22,3 @@
password (:identity-password db)]
(when encrypted
(read-string (password-decrypt password encrypted)))))
(comment
(stored-identity @re-frame.db/app-db)
)

View File

@ -5,5 +5,6 @@
(def country-code (subs locale 3 5))
(set! js/PhoneNumber (js/require "awesome-phonenumber"))
;; todo check wrong numbers, .getNumber returns empty string
(defn format-phone-number [number]
(str (.getNumber (js/PhoneNumber. number country-code "international"))))