Show fake contacts

This commit is contained in:
virvar 2016-03-27 17:59:03 +03:00
parent eca6c9695b
commit 2d8119be64
20 changed files with 397 additions and 7 deletions

BIN
syng-im/images/att.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 651 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 316 B

BIN
syng-im/images/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 526 B

BIN
syng-im/images/mic.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 529 B

BIN
syng-im/images/nav-back.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 985 B

BIN
syng-im/images/no-photo.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

BIN
syng-im/images/online.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 307 B

BIN
syng-im/images/play.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 898 B

BIN
syng-im/images/seen.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 284 B

BIN
syng-im/images/smile.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 584 B

View File

@ -7,10 +7,10 @@
[syng-im.handlers]
[syng-im.subs]
[syng-im.components.react :refer [navigator app-registry]]
[syng-im.components.contact-list.contact-list :refer [contact-list]]
[syng-im.components.chat :refer [chat]]
[syng-im.utils.logging :as log]))
(def ^{:dynamic true :private true} *nav-render*
"Flag to suppress navigator re-renders from outside om when pushing/popping."
true)
@ -18,6 +18,7 @@
(def back-button-handler (cljs/atom {:nav nil
:handler nil}))
(defn init-back-button-handler! [nav]
(let [handler @back-button-handler]
(when-not (= nav (:nav handler))
@ -32,7 +33,7 @@
(add-event-listener "hardwareBackPress" new-listener)))))
(defn app-root []
[navigator {:initial-route (clj->js {:view-id :chat})
[navigator {:initial-route (clj->js {:view-id :contact-list})
:render-scene (fn [route nav]
(log/debug "route" route)
(when *nav-render*
@ -40,9 +41,12 @@
view-id (keyword view-id)]
(init-back-button-handler! nav)
(case view-id
:contact-list (r/as-element [contact-list
{:navigator nav}])
:chat (r/as-element [chat {:navigator nav}])))))}])
(defn init []
(dispatch-sync [:initialize-db])
(dispatch [:initialize-protocol])
(dispatch [:load-syng-contacts])
(.registerComponent app-registry "SyngIm" #(r/reactify-component app-root)))

View File

@ -0,0 +1,104 @@
(ns syng-im.components.contact-list.contact
(:require [syng-im.components.react :refer [view text image touchable-highlight]]
[syng-im.components.resources :as res]
;; [messenger.comm.intercom :as intercom :refer [show-chat]]
;; [messenger.components.chat.chat :refer [chat]]
))
(defn contact-view [contact]
(let [{:keys [name photo-path delivery-status datetime new-messages-count
online whisper-identity]} contact]
[touchable-highlight {:onPress (fn []
;; TODO
;; (show-chat nav whisper-identity)
)}
[view {:style {:flexDirection "row"
:marginTop 5
:marginBottom 5
:paddingLeft 15
:paddingRight 15
:height 75}}
[view {:width 54
:height 54}
;;; photo
[view {:width 54
:height 54
:borderRadius 50
:backgroundColor "#FFFFFF"
:elevation 6}
[image {:source (if (< 0 (count photo-path))
{:uri photo-path}
res/user-no-photo)
:style {:borderWidth 2
:borderColor "#FFFFFF"
:borderRadius 50
:width 54
:height 54
:position "absolute"}}]]
;;; online
(when online
[view {:position "absolute"
:top 41
:left 36
:width 12
:height 12
:borderRadius 50
:backgroundColor "#FFFFFF"
:elevation 6}
[image {:source res/online-icon
:style {:width 12
:height 12}}]])]
[view {:style {:flexDirection "column"
:marginLeft 7
:marginRight 10
:flex 1
:position "relative"}}
;;; name
[text {:style {:fontSize 15
:fontFamily "Avenir-Roman"}} name]
;;; last message
[text {:style {:color "#AAB2B2"
:fontFamily "Avenir-Roman"
:fontSize 14
:marginTop 2
:paddingRight 10}}
(str "Hi, I'm " name)]]
[view {:style {:flexDirection "column"}}
;;; delivery status
[view {:style {:flexDirection "row"
:position "absolute"
:top 0
:right 0}}
(when delivery-status
[image {:source (if (= (keyword delivery-status) :seen)
res/seen-icon
res/delivered-icon)
:style {:marginTop 5}}])
;;; datetime
[text {:style {:fontFamily "Avenir-Roman"
:fontSize 11
:color "#AAB2B2"
:letterSpacing 1
:lineHeight 15
:marginLeft 5}}
datetime]]
;;; new messages count
(when (< 0 new-messages-count)
[view {:style {:position "absolute"
:right 0
:bottom 24
:width 18
:height 18
:backgroundColor "#6BC6C8"
:borderColor "#FFFFFF"
:borderRadius 50
:alignSelf "flex-end"}}
[text {:style {:width 18
:height 17
:fontFamily "Avenir-Roman"
:fontSize 10
:color "#FFFFFF"
:lineHeight 19
:textAlign "center"
:top 1}}
new-messages-count]])]]]))

View File

@ -0,0 +1,96 @@
(ns syng-im.components.contact-list.contact-list
(:require-macros
[natal-shell.data-source :refer [data-source clone-with-rows]]
[natal-shell.core :refer [with-error-view]])
(:require [re-frame.core :refer [subscribe dispatch dispatch-sync]]
[syng-im.components.react :refer [view text image touchable-highlight
navigator list-view toolbar-android
list-item]]
[syng-im.components.resources :as res]
[syng-im.components.contact-list.contact :refer [contact-view]]
[syng-im.utils.logging :as log]
;; [messenger.comm.intercom :as intercom]
;; [messenger.components.contact-list.contact :refer [contact]]
;; [messenger.components.iname :as in]
))
(defn render-row [row section-id row-id]
(list-item (contact-view (js->clj row :keywordize-keys true))))
;; (defn load-contacts []
;; (intercom/load-syng-contacts))
;; (defui ContactList
;; static in/IName
;; (get-name [this]
;; :contacts/contacts)
;; static om/IQuery
;; (query [this]
;; '[:contacts-ds])
;; Object
;; (componentDidMount [this]
;; (load-contacts))
;; (render [this]
;; (let [{{contacts-ds :contacts-ds} :contacts/contacts} (om/props this)
;; {:keys [nav]} (om/get-computed this)]
;; (view {:style {:flex 1
;; :backgroundColor "white"}}
;; (toolbar-android {:logo res/logo-icon
;; :title "Chats"
;; :titleColor "#4A5258"
;; :style {:backgroundColor "white"
;; :height 56
;; :elevation 2}})
;; (when contacts-ds
;; (list-view {:dataSource contacts-ds
;; :renderRow (partial render-row nav)
;; :style {:backgroundColor "white"}}))))))
;; (def contact-list (om/factory ContactList))
(defn get-data-source [contacts]
(clone-with-rows (data-source {:rowHasChanged (fn [row1 row2]
(not= row1 row2))})
contacts))
(defn contacts-list-re-frame [contacts]
(let [contacts-ds (get-data-source contacts)]
[view {:style {:flex 1
:backgroundColor "white"}}
[toolbar-android {:logo res/logo-icon
:title "Chats"
:titleColor "#4A5258"
:style {:backgroundColor "white"
:height 56
:elevation 2}}]
(when contacts-ds
[list-view {:dataSource contacts-ds
:renderRow render-row
:style {:backgroundColor "white"}}]
)
]))
(def logo-img (js/require "./images/cljs.png"))
(defn alert [title]
(.alert (.-Alert js/React) title))
(defn contact-list [{:keys [navigator]}]
(let [greeting (subscribe [:get-greeting])
contacts (subscribe [:get-contacts])]
(fn []
(contacts-list-re-frame @contacts)
;; [view {:style {:flex-direction "column" :margin 40 :align-items "center"}}
;; [text {:style {:font-size 30 :font-weight "100" :margin-bottom 20 :text-align "center"}} (str @greeting " " (count @contacts))]
;; [image {:source logo-img
;; :style {:width 80 :height 80 :margin-bottom 30}}]
;; [touchable-highlight {:style {:background-color "#999" :padding 10 :border-radius 5}
;; :on-press #(alert "HELLO!")}
;; [text {:style {:color "white" :text-align "center" :font-weight "bold"}} "press me"]]]
)))

View File

@ -8,4 +8,43 @@
(def text (r/adapt-react-class (.-Text js/React)))
(def view (r/adapt-react-class (.-View js/React)))
(def image (r/adapt-react-class (.-Image js/React)))
(def touchable-highlight (r/adapt-react-class (.-TouchableHighlight js/React)))
(def touchable-highlight (r/adapt-react-class (.-TouchableHighlight js/React)))
(def toolbar-android (r/adapt-react-class (.-ToolbarAndroid js/React)))
(def list-view (r/adapt-react-class (.-ListView js/React)))
(defn list-item [component]
(r/as-element component))
;; (do
;; (def activity-indicator-ios (r/adapt-react-class (.-ActivityIndicatorIOS js/React)))
;; (def animated-image (r/adapt-react-class (.-Animated.Image js/React)))
;; (def animated-text (r/adapt-react-class (.-Animated.Text js/React)))
;; (def animated-view (r/adapt-react-class (.-Animated.View js/React)))
;; (def date-picker-ios (r/adapt-react-class (.-DatePickerIOS js/React)))
;; (def drawer-layout-android (r/adapt-react-class (.-DrawerLayoutAndroid js/React)))
;; (def image (r/adapt-react-class (.-Image js/React)))
;; (def list-view (r/adapt-react-class (.-ListView js/React)))
;; (def map-view (r/adapt-react-class (.-MapView js/React)))
;; (def modal (r/adapt-react-class (.-Modal js/React)))
;; (def navigator (r/adapt-react-class (.-Navigator js/React)))
;; (def navigator-ios (r/adapt-react-class (.-NavigatorIOS js/React)))
;; (def picker-ios (r/adapt-react-class (.-PickerIOS js/React)))
;; (def progress-bar-android (r/adapt-react-class (.-ProgressBarAndroid js/React)))
;; (def progress-view-ios (r/adapt-react-class (.-ProgressViewIOS js/React)))
;; (def pull-to-refresh-view-android (r/adapt-react-class (.-PullToRefreshViewAndroid js/React)))
;; (def scroll-view (r/adapt-react-class (.-ScrollView js/React)))
;; (def segmented-control-ios (r/adapt-react-class (.-SegmentedControlIOS js/React)))
;; (def slider-ios (r/adapt-react-class (.-SliderIOS js/React)))
;; (def switch (r/adapt-react-class (.-Switch js/React)))
;; (def tab-bar-ios (r/adapt-react-class (.-TabBarIOS js/React)))
;; (def tab-bar-ios-item (r/adapt-react-class (.-TabBarIOS.Item js/React)))
;; (def text (r/adapt-react-class (.-Text js/React)))
;; (def text-input (r/adapt-react-class (.-TextInput js/React)))
;; (def toolbar-android (r/adapt-react-class (.-ToolbarAndroid js/React)))
;; (def touchable-highlight (r/adapt-react-class (.-TouchableHighlight js/React)))
;; (def touchable-native-feedback (r/adapt-react-class (.-TouchableNativeFeedback js/React)))
;; (def touchable-opacity (r/adapt-react-class (.-TouchableOpacity js/React)))
;; (def touchable-without-feedback (r/adapt-react-class (.-TouchableWithoutFeedback js/React)))
;; (def view (r/adapt-react-class (.-View js/React)))
;; (def view-pager-android (r/adapt-react-class (.-ViewPagerAndroid js/React)))
;; (def web-view (r/adapt-react-class (.-WebView js/React))))

View File

@ -0,0 +1,12 @@
(ns syng-im.components.resources)
(def logo-icon (js/require "./images/logo.png"))
(def nav-back-icon (js/require "./images/nav-back.png"))
(def user-no-photo (js/require "./images/no-photo.png"))
(def online-icon (js/require "./images/online.png"))
(def seen-icon (js/require "./images/seen.png"))
(def delivered-icon (js/require "./images/delivered.png"))
(def play (js/require "./images/play.png"))
(def mic (js/require "./images/mic.png"))
(def smile (js/require "./images/smile.png"))
(def att (js/require "./images/att.png"))

View File

@ -6,7 +6,8 @@
;; initial state of app-db
(def app-db {:greeting "Hello Clojure in iOS and Android!"
:identity-password "replace-me-with-user-entered-password"})
:identity-password "replace-me-with-user-entered-password"
:contacts []})
(def protocol-initialized-path [:protocol-initialized])
@ -14,4 +15,4 @@
(def identity-password-path [:identity-password])
(def current-chat-id-path [:chat :current-chat-id])
(defn arrived-message-path [chat-id]
[:chat chat-id :arrived-message-id])
[:chat chat-id :arrived-message-id])

View File

@ -7,6 +7,7 @@
[syng-im.protocol.protocol-handler :refer [make-handler]]
[syng-im.models.protocol :refer [update-identity
set-initialized]]
[syng-im.models.contacts :as contacts]
[syng-im.models.messages :refer [save-message
new-message-arrived]]
[syng-im.utils.logging :as log]))
@ -49,8 +50,14 @@
(save-message chat-id msg)
(new-message-arrived db chat-id msg-id)))
;; -- Contacts --------------------------------------------------------------
(register-handler :load-syng-contacts
(fn [db [_ value]]
(contacts/load-syng-contacts db)))
;; -- Something --------------------------------------------------------------
(register-handler :set-greeting
(fn [db [_ value]]
(assoc db :greeting value)))
(assoc db :greeting value)))

View File

@ -0,0 +1,83 @@
(ns syng-im.models.contacts
(:require [cljs.core.async :as async :refer [chan put! <! >!]]
[re-frame.core :refer [subscribe dispatch dispatch-sync]]
[syng-im.utils.utils :refer [log toast]]
[syng-im.persistence.realm :as realm]))
(def fake-contacts? true)
(def react-native-contacts (js/require "react-native-contacts"))
(defn- generate-contact [n]
{:name (str "Contact " n)
:photo-path ""
:phone-numbers [{:label "mobile" :number (apply str (repeat 7 n))}]
:delivery-status (if (< (rand) 0.5) :delivered :seen)
:datetime "15:30"
:new-messages-count (rand-int 3)
:online (< (rand) 0.5)})
(defn- generate-contacts [n]
(map generate-contact (range 1 (inc n))))
(defn load-phone-contacts []
(let [ch (chan)]
(if fake-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]
;; (toast (str 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- get-contacts []
(if fake-contacts?
[{:phone-number "123"
:whisper-identity "abc"
:name "fake"
:photo-path ""}]
(realm/get-list "Contact")))
(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- create-contact [{:keys [phone-number whisper-identity name photo-path]}]
(realm/create "Contact"
{:phone-number phone-number
:whisper-identity whisper-identity
:name (or name "")
:photo-path (or photo-path "")}))
(defn- contact-exist? [contacts contact]
(some #(= (:phone-number contact) (:phone-number %)) contacts))
(defn- add-contacts [contacts]
(realm/write (fn []
(let [db-contacts (get-contacts)]
(dorun (map (fn [contact]
(if (not (contact-exist? db-contacts contact))
(create-contact contact)
;; TODO else override?
))
contacts))))))
(defn save-syng-contacts [syng-contacts]
(add-contacts syng-contacts))

View File

@ -6,4 +6,10 @@
:get-greeting
(fn [db _]
(reaction
(get @db :greeting))))
(get @db :greeting))))
(register-sub
:get-contacts
(fn [db _]
(reaction
(get @db :contacts))))

View File

@ -0,0 +1,38 @@
(ns syng-im.utils.utils
(:require-macros
[natal-shell.async-storage :refer [get-item set-item]]
[natal-shell.alert :refer [alert]]
[natal-shell.toast-android :as toast]))
;; (def server-address "http://rpc0.syng.im:20000/")
(def server-address "http://10.0.3.2:3000/")
(defn log [obj]
(.log js/console obj))
(defn toast [s]
(toast/show s (toast/long)))
(defn on-error [error]
(toast (str "error: " error)))
(defn http-post
([action data on-success]
(http-post action data on-success nil))
([action data on-success on-error]
(-> (.fetch js/window
(str server-address action)
(clj->js {:method "POST"
:headers {:accept "application/json"
:content-type "application/json"}
:body (.stringify js/JSON (clj->js data))}))
(.then (fn [response]
(log response)
(.text response)))
(.then (fn [text]
(let [json (.parse js/JSON text)
obj (js->clj json :keywordize-keys true)]
(on-success obj))))
(.catch (or on-error
(fn [error]
(toast (str error))))))))