parent
a2467348e3
commit
8fb71e46b7
|
@ -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
|
||||
|
|
|
@ -50,6 +50,7 @@
|
|||
(dispatch [:updated-discoveries]))))
|
||||
|
||||
(defn discovery [{:keys [navigator]}]
|
||||
[]
|
||||
(let [showSearch (r/atom false)]
|
||||
(fn []
|
||||
[view {:style {:flex 1
|
||||
|
@ -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"))
|
||||
)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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))
|
||||
|
@ -27,9 +28,9 @@
|
|||
: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
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
(ns syng-im.contacts.subs
|
||||
(:require-macros [reagent.ratom :refer [reaction]])
|
||||
(:require [re-frame.core :refer [register-sub]]))
|
||||
|
||||
|
||||
|
|
|
@ -7,8 +7,7 @@
|
|||
(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"
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -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))))))
|
|
@ -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,13 +23,12 @@
|
|||
(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)})
|
||||
(defn load-phone-contacts
|
||||
([callback] (.getAll react-native-contacts callback))
|
||||
([]
|
||||
(.getAll react-native-contacts
|
||||
(fn [error raw-contacts]
|
||||
(put! ch
|
||||
(println raw-contacts)
|
||||
{:error error
|
||||
:contacts
|
||||
(when (not error)
|
||||
|
@ -44,25 +40,9 @@
|
|||
:photo-path (:thumbnailPath contact)
|
||||
:phone-numbers (:phoneNumbers contact)}))
|
||||
(js->clj raw-contacts :keywordize-keys true)))}))))
|
||||
ch))
|
||||
|
||||
(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
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -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)
|
||||
)
|
||||
|
|
|
@ -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"))))
|
||||
|
|
Loading…
Reference in New Issue