Navigation

Former-commit-id: 50beafc5a2
This commit is contained in:
virvar 2016-02-25 19:21:08 +03:00
parent 4adbc18dab
commit 0ed10176b2
6 changed files with 236 additions and 115 deletions

BIN
images/nav-back.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 530 B

View File

@ -0,0 +1,78 @@
(ns messenger.android.chat
(:require-macros
[natal-shell.components :refer [view text image touchable-highlight list-view
toolbar-android]]
[natal-shell.data-source :refer [data-source clone-with-rows]]
[natal-shell.core :refer [with-error-view]]
[natal-shell.alert :refer [alert]])
(:require [om.next :as om :refer-macros [defui]]
[re-natal.support :as sup]
[messenger.state :as state]
[messenger.android.resources :as res]))
(defn nav-pop [nav]
(binding [state/*nav-render* false]
(.pop nav)))
(defui Message
static om/Ident
(ident [this {:keys [id]}]
[:message/by-id id])
static om/IQuery
(query [this]
'[:id :body :delivery-status :datetime])
Object
(render [this]
(let [{:keys [id body delivery-status datetime]}
(om/props this)]
(text {:style {:color "#AAB2B2"
:fontFamily "Avenir-Roman"
:fontSize 14
:marginTop 2
:paddingRight 10}}
body))))
(def message (om/factory Message {:keyfn :id}))
(defn render-row [row section-id row-id]
(message (js->clj row :keywordize-keys true)))
(defn generate-message [n]
{:id n
:body "Hi"
:delivery-status (if (< (rand) 0.5) :delivered :seen)
:datetime "15:30"})
(defn generate-messages [n]
(map generate-message (range 1 (inc n))))
(defn load-message []
(swap! state/app-state update :contacts-ds
#(clone-with-rows %
(vec (generate-messages 10)))))
(defui Chat
;; static om/IQuery
;; (query [this]
;; '[:contacts-ds])
Object
;; (componentDidMount [this]
;; (load-contacts))
(render [this]
(let [{:keys [contacts-ds]} (om/props this)
{:keys [nav]} (om/get-computed this)]
(view {:style {:flex 1}}
(toolbar-android {:logo res/logo-icon
:title "Chat name"
:style {:backgroundColor "#e9eaed"
:height 56}
:navIcon res/nav-back-icon
:onIconClicked (fn []
(nav-pop nav))})
(text {} "Hello")
;; (list-view {:dataSource contacts-ds
;; :renderRow render-row
;; :style {}})
))))
(def chat (om/factory Chat))

View File

@ -7,18 +7,21 @@
[natal-shell.alert :refer [alert]]) [natal-shell.alert :refer [alert]])
(:require [om.next :as om :refer-macros [defui]] (:require [om.next :as om :refer-macros [defui]]
[re-natal.support :as sup] [re-natal.support :as sup]
[messenger.state :as state])) [messenger.state :as state]
[messenger.android.resources :as res]
[messenger.android.chat :refer [chat]]))
(def fake-contacts? true) (def fake-contacts? true)
(set! js/React (js/require "react-native"))
(def react-native-contacts (js/require "react-native-contacts")) (def react-native-contacts (js/require "react-native-contacts"))
(def logo-icon (js/require "./images/logo.png")) (defn nav-push [nav route]
(def user-no-photo (js/require "./images/no-photo.png")) (binding [state/*nav-render* false]
(def online-icon (js/require "./images/online.png")) (.push nav (clj->js route))))
(def seen-icon (js/require "./images/seen.png"))
(def delivered-icon (js/require "./images/delivered.png")) (defn show-chat [nav]
(nav-push nav {:component chat
:name "chat"}))
(defui Contact (defui Contact
static om/Ident static om/Ident
@ -30,107 +33,111 @@
Object Object
(render [this] (render [this]
(let [{:keys [name photo delivery-status datetime new-messages-count online]} (let [{:keys [name photo delivery-status datetime new-messages-count online]}
(om/props this)] (dissoc (om/props this) :om.next/computed)
(view {:style {:flexDirection "row" {:keys [nav]} (om/get-computed this)]
:marginTop 5 (touchable-highlight
:marginBottom 5 {:onPress (fn [] (show-chat nav))}
:paddingLeft 15 (view {:style {:flexDirection "row"
:paddingRight 15 :marginTop 5
:height 75 :marginBottom 5
:transform [{:translateX 0} :paddingLeft 15
{:translateY 0}]}} :paddingRight 15
(view {:width 54 :height 75
:height 54} :transform [{:translateX 0}
;;; photo {:translateY 0}]}}
(view {:width 54 (view {:width 54
:height 54 :height 54}
:borderRadius 50 ;;; photo
:backgroundColor "#FFFFFF" (view {:width 54
:elevation 6} :height 54
(image {:source (if (< 0 (count photo)) :borderRadius 50
{:uri photo} :backgroundColor "#FFFFFF"
user-no-photo) :elevation 6}
:style {:borderWidth 2 (image {:source (if (< 0 (count photo))
:borderColor "#FFFFFF" {:uri photo}
:borderRadius 50 res/user-no-photo)
:width 54 :style {:borderWidth 2
:height 54 :borderColor "#FFFFFF"
:position "absolute" :borderRadius 50
;; :top 2 :width 54
;; :right 2 :height 54
}})) :position "absolute"
;;; online ;; :top 2
(when online ;; :right 2
(view {:position "absolute" }}))
:top 41 ;;; online
:left 36 (when online
:width 12 (view {:position "absolute"
:height 12 :top 41
:borderRadius 50 :left 36
:backgroundColor "#FFFFFF" :width 12
:elevation 6} :height 12
(image {:source online-icon :borderRadius 50
:style {:width 12 :backgroundColor "#FFFFFF"
:height 12}})))) :elevation 6}
(view {:style {:flexDirection "column" (image {:source res/online-icon
:marginLeft 7 :style {:width 12
:marginRight 10 :height 12}}))))
:flex 1 (view {:style {:flexDirection "column"
:position "relative"}} :marginLeft 7
;;; name :marginRight 10
(text {:style {:fontSize 15 :flex 1
:fontFamily "Avenir-Roman"}} name) :position "relative"}}
;;; last message ;;; name
(text {:style {:color "#AAB2B2" (text {:style {:fontSize 15
:fontFamily "Avenir-Roman" :fontFamily "Avenir-Roman"}} name)
:fontSize 14 ;;; last message
:marginTop 2 (text {:style {:color "#AAB2B2"
:paddingRight 10}} :fontFamily "Avenir-Roman"
(str "Hi, I'm " name))) :fontSize 14
(view {:style {:flexDirection "column"}} :marginTop 2
;;; delivery status :paddingRight 10}}
(view {:style {:flexDirection "row" (str "Hi, I'm " name)))
:position "absolute" (view {:style {:flexDirection "column"}}
:top 0 ;;; delivery status
:right 0}} (view {:style {:flexDirection "row"
(when delivery-status :position "absolute"
(image {:source (if (= (keyword delivery-status) :seen) :top 0
seen-icon :right 0}}
delivered-icon) (when delivery-status
:style {:marginTop 5}})) (image {:source (if (= (keyword delivery-status) :seen)
;;; datetime res/seen-icon
(text {:style {:fontFamily "Avenir-Roman" res/delivered-icon)
:fontSize 11 :style {:marginTop 5}}))
:color "#AAB2B2" ;;; datetime
:letterSpacing 1 (text {:style {:fontFamily "Avenir-Roman"
:lineHeight 15 :fontSize 11
:marginLeft 5}} :color "#AAB2B2"
datetime)) :letterSpacing 1
;;; new messages count :lineHeight 15
(when (< 0 new-messages-count) :marginLeft 5}}
(view {:style {:position "absolute" datetime))
:right 0 ;;; new messages count
:bottom 24 (when (< 0 new-messages-count)
:width 18 (view {:style {:position "absolute"
:height 18 :right 0
:backgroundColor "#6BC6C8" :bottom 24
:borderColor "#FFFFFF" :width 18
:borderRadius 50 :height 18
:alignSelf "flex-end"}} :backgroundColor "#6BC6C8"
(text {:style {:width 18 :borderColor "#FFFFFF"
:height 17 :borderRadius 50
:fontFamily "Avenir-Roman" :alignSelf "flex-end"}}
:fontSize 10 (text {:style {:width 18
:color "#FFFFFF" :height 17
:lineHeight 19 :fontFamily "Avenir-Roman"
:textAlign "center" :fontSize 10
:top 1}} :color "#FFFFFF"
new-messages-count)))))))) :lineHeight 19
:textAlign "center"
:top 1}}
new-messages-count)))))))))
(def contact (om/factory Contact {:keyfn :name})) (def contact (om/factory Contact {:keyfn :name}))
(defn render-row [row section-id row-id] (defn render-row [nav row section-id row-id]
(contact (js->clj row :keywordize-keys true))) (contact (om/computed (js->clj row :keywordize-keys true)
{:nav nav})))
(defn generate-contact [n] (defn generate-contact [n]
{:name (str "Contact " n) {:name (str "Contact " n)
@ -167,14 +174,15 @@
(componentDidMount [this] (componentDidMount [this]
(load-contacts)) (load-contacts))
(render [this] (render [this]
(let [{:keys [contacts-ds]} (om/props this)] (let [{:keys [contacts-ds]} (om/props this)
{:keys [nav]} (om/get-computed this)]
(view {:style {:flex 1}} (view {:style {:flex 1}}
(toolbar-android {:logo logo-icon (toolbar-android {:logo res/logo-icon
:title "Chats" :title "Chats"
:style {:backgroundColor "#e9eaed" :style {:backgroundColor "#e9eaed"
:height 56}}) :height 56}})
(list-view {:dataSource contacts-ds (list-view {:dataSource contacts-ds
:renderRow render-row :renderRow (partial render-row nav)
:style {}}))))) :style {}})))))
(def contacts-list (om/factory ContactsList)) (def contacts-list (om/factory ContactsList))

View File

@ -1,30 +1,52 @@
(ns messenger.android.core (ns messenger.android.core
(:require-macros (:require-macros
[natal-shell.components :refer [view text image touchable-highlight list-view [natal-shell.components :refer [navigator view text image touchable-highlight list-view
toolbar-android]] toolbar-android]]
[natal-shell.data-source :refer [data-source clone-with-rows]] [natal-shell.data-source :refer [data-source clone-with-rows]]
[natal-shell.back-android :refer [add-event-listener]]
[natal-shell.core :refer [with-error-view]] [natal-shell.core :refer [with-error-view]]
[natal-shell.alert :refer [alert]]) [natal-shell.alert :refer [alert]])
(:require [om.next :as om :refer-macros [defui]] (:require [om.next :as om :refer-macros [defui]]
[re-natal.support :as sup] [re-natal.support :as sup]
[messenger.state :as state] [messenger.state :as state]
[messenger.android.contacts-list :as contacts-list])) [messenger.android.contacts-list :refer [contacts-list]]
[messenger.android.chat :refer [chat]]))
(set! js/React (js/require "react-native"))
(def app-registry (.-AppRegistry js/React)) (def app-registry (.-AppRegistry js/React))
(def initialized-atom (atom false))
(defn init-back-button-handler! [nav]
(if (not @initialized-atom)
(swap! initialized-atom
(fn [initialized]
(when (not initialized)
(add-event-listener "hardwareBackPress"
(fn []
(binding [state/*nav-render* false]
(when (< 1 (.-length (.getCurrentRoutes nav)))
(.pop nav)
true)))))
true))))
(defui AppRoot (defui AppRoot
static om/IQuery static om/IQuery
(query [this] (query [this]
'[:contacts-ds]) '[:page :contacts-ds])
Object Object
(render [this] (render [this]
(let [{:keys [contacts-ds]} (om/props this)] (let [{:keys [page contacts-ds]} (om/props this)]
(contacts-list/contacts-list (om/props this))))) (navigator
{:initialRoute {:component contacts-list}
:renderScene (fn [route nav]
(when state/*nav-render*
(init-back-button-handler! nav)
(let [{:keys [component]} (js->clj route :keywordize-keys true)]
(component (om/computed (om/props this) {:nav nav})))))}))))
(swap! state/app-state assoc :contacts-ds (swap! state/app-state assoc :contacts-ds
(data-source {:rowHasChanged (fn [row1 row2] (data-source {:rowHasChanged (fn [row1 row2]
(not= row1 row2))})) (not= row1 row2))}))
(swap! state/app-state assoc :component contacts-list)
(defonce RootNode (sup/root-node! 1)) (defonce RootNode (sup/root-node! 1))
(defonce app-root (om/factory RootNode)) (defonce app-root (om/factory RootNode))

View File

@ -0,0 +1,8 @@
(ns messenger.android.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"))

View File

@ -2,7 +2,12 @@
(:require [om.next :as om] (:require [om.next :as om]
[re-natal.support :as sup])) [re-natal.support :as sup]))
(defonce app-state (atom {})) (set! js/React (js/require "react-native"))
(defonce app-state (atom {:component nil}))
(def ^{:dynamic true :private true} *nav-render*
"Flag to suppress navigator re-renders from outside om when pushing/popping."
true)
(defmulti read om/dispatch) (defmulti read om/dispatch)
(defmethod read :default (defmethod read :default