start chat with contact

This commit is contained in:
Roman Volosovskyi 2016-05-16 10:45:59 +03:00
parent 780b12049e
commit af0ed4c3a9
13 changed files with 167 additions and 104 deletions

View File

@ -52,7 +52,7 @@
(dispatch [:initialize-chats])
(dispatch [:initialize-protocol])
(dispatch [:load-user-phone-number])
(dispatch [:load-syng-contacts])
(dispatch [:load-contacts])
;; load commands from remote server (todo: uncomment)
;; (dispatch [:load-commands])
(dispatch [:init-console-chat])

View File

@ -1,5 +1,5 @@
(ns syng-im.chat.handlers
(:require [re-frame.core :refer [register-handler enrich after debug]]
(:require [re-frame.core :refer [register-handler enrich after debug dispatch]]
[syng-im.models.commands :as commands]
[clojure.string :as str]
[syng-im.chat.suggestions :as suggestions]
@ -10,7 +10,9 @@
[syng-im.utils.random :as random]
[syng-im.components.react :as r]
[syng-im.handlers.sign-up :as sign-up-service]
[syng-im.models.chats :as chats]))
[syng-im.models.chats :as chats]
[syng-im.navigation.handlers :as nav]
[syng-im.models.chats :as c]))
(register-handler :set-show-actions
(fn [db [_ show-actions]]
@ -236,15 +238,16 @@
(defn load-messages!
[db _]
db
([db] (load-messages! db nil))
([db _]
(->> (:current-chat-id db)
messages/get-messages
(assoc db :messages)))
(assoc db :messages))))
(defn init-chat
[{:keys [messages current-chat-id] :as db} _]
(assoc-in db [:chats current-chat-id :messages] messages))
([db] (init-chat db nil))
([{:keys [messages current-chat-id] :as db} _]
(assoc-in db [:chats current-chat-id :messages] messages)))
(register-handler :init-chat
(-> load-messages!
@ -256,9 +259,11 @@
(let [chats (->> loaded-chats
(map (fn [{:keys [chat-id] :as chat}]
[chat-id chat]))
(into {}))]
(into {}))
ids (set (keys chats))]
(-> db
(assoc :chats chats)
(assoc :chats-ids ids)
(dissoc :loaded-chats))))
(defn load-chats!
@ -268,7 +273,6 @@
(register-handler :initialize-chats
((enrich initialize-chats) load-chats!))
(defn store-message!
[{:keys [new-message]} [_ {chat-id :from}]]
(messages/save-message chat-id new-message))
@ -288,3 +292,51 @@
(fn [db [_ {chat-id :group-id :as msg}]]
(messages/save-message chat-id msg)
db))
(defn load-chat!
[{:keys [chats current-chat-id] :as db}]
(when-not (chats current-chat-id)
(c/create-chat {}))
db)
(defmethod nav/preload-data! :chat
[{:keys [current-chat-id] :as db} [_ _ id]]
(-> db
(assoc :current-chat-id (or id current-chat-id))
load-messages!
init-chat))
(defn prepare-chat
[{:keys [contacts] :as db} [_ contcat-id]]
(let [name (get-in contacts [contcat-id :name])
chat {:chat-id contcat-id
:name name
:group-chat false
:is-active true
:timestamp (.getTime (js/Date.))
;; todo how to choose color?
;; todo do we need to have some color for not group chat?
:contacts [{:identity contcat-id
:text-color :#FFFFFF
:background-color :#AB7967}]}]
(assoc db :new-chat chat)))
(defn add-chat [{:keys [new-chat] :as db} [_ chat-id]]
(-> db
(update :chats assoc chat-id new-chat)
(update :chats-ids conj chat-id)))
(defn save-chat!
[{:keys [new-chat]} _]
(chats/create-chat new-chat))
(defn open-chat!
[_ [_ chat-id]]
(dispatch [:navigate-to :chat chat-id]))
(register-handler :start-chat
(-> prepare-chat
((enrich add-chat))
((after save-chat!))
((after open-chat!))
debug))

View File

@ -1,4 +1,5 @@
(ns syng-im.chat.screen
(:require-macros [syng-im.utils.views :refer [defview]])
(:require [clojure.string :as s]
[re-frame.core :refer [subscribe dispatch]]
[syng-im.components.react :refer [view
@ -8,7 +9,7 @@
touchable-highlight
list-view
list-item]]
[syng-im.chat.styles.chat :as st]
[syng-im.chat.styles.screen :as st]
[syng-im.resources :as res]
[syng-im.utils.listview :refer [to-datasource]]
[syng-im.components.invertible-scroll-view :refer [invertible-scroll-view]]
@ -91,7 +92,7 @@
(defn actions-list-view []
(let [{:keys [group-chat active]}
(subscribe [:chat-properties [:group-chat :name :contacts :active]])]
(subscribe [:chat-properties [:group-chat :active]])]
(when-let [actions (when (and @group-chat @active)
[{:title "Add Contact to chat"
:icon :menu_group
@ -160,25 +161,23 @@
[chat-photo {}]
[contact-online {:online true}]]])])))
(defn messages-view [group-chat]
(let [messages (subscribe [:chat :messages])
contacts (subscribe [:chat :contacts])]
(fn [group-chat]
(let [contacts' (contacts-by-identity @contacts)]
(defview messages-view [group-chat]
[messages [:chat :messages]
contacts [:chat :contacts]]
(let [contacts' (contacts-by-identity contacts)]
[list-view {:renderRow (message-row contacts' group-chat)
:renderScrollComponent #(invertible-scroll-view (js->clj %))
:onEndReached #(dispatch [:load-more-messages])
:enableEmptySections true
:dataSource (to-datasource @messages)}]))))
:dataSource (to-datasource messages)}]))
(defn chat []
(let [is-active (subscribe [:chat :is-active])
group-chat (subscribe [:chat :group-chat])
show-actions-atom (subscribe [:show-actions])]
(fn []
(defview chat []
[is-active [:chat :is-active]
group-chat [:chat :group-chat]
show-actions-atom [:show-actions]]
[view st/chat-view
[toolbar]
[messages-view @group-chat]
(when @group-chat [typing-all])
[messages-view group-chat]
(when group-chat [typing-all])
(when is-active [chat-message-new])
(when @show-actions-atom [actions-view])])))
(when show-actions-atom [actions-view])])

View File

@ -1,4 +1,4 @@
(ns syng-im.chat.styles.chat
(ns syng-im.chat.styles.screen
(:require [syng-im.components.styles :refer [font
title-font
color-white

View File

@ -76,3 +76,7 @@
(fn [db _]
(let [current-chat-id (:current-chat-id @db)]
(reaction (get-in @db [:chats current-chat-id])))))
(register-sub :get-chat
(fn [db [_ chat-id]]
(reaction (get-in @db [:chats chat-id]))))

View File

@ -12,15 +12,18 @@
(contacts/save-contacts [contact]))
(register-handler :add-contact
(-> (fn [db [_ contact]]
(update db :contacts conj contact))
(-> (fn [db [_ {:keys [whisper-identity] :as contact}]]
(update db :contacts assoc whisper-identity contact))
((after save-contact))))
(defn load-contacts! [db _]
(let [contacts (contacts/get-contacts)]
(let [contacts (->> (contacts/get-contacts)
(map (fn [{:keys [whisper-identity] :as contact}]
[whisper-identity contact]))
(into {}))]
(assoc db :contacts contacts)))
(register-handler :load-syng-contacts load-contacts!)
(register-handler :load-contacts load-contacts!)
;; TODO see https://github.com/rt2zz/react-native-contacts/issues/45
(def react-native-contacts (js/require "react-native-contacts"))

View File

@ -1,7 +1,5 @@
(ns syng-im.contacts.screen
(:require-macros
[natal-shell.data-source :refer [data-source clone-with-rows]]
[natal-shell.core :refer [with-error-view]])
(:require-macros [syng-im.utils.views :refer [defview]])
(:require [re-frame.core :refer [subscribe dispatch dispatch-sync]]
[syng-im.components.react :refer [view text
image
@ -17,9 +15,6 @@
(defn render-row [row _ _]
(list-item [contact-view row]))
(defn get-data-source [contacts]
(clone-with-rows (data-source {:rowHasChanged not=}) contacts))
(defn contact-list-toolbar []
[toolbar {:title "Contacts"
:background-color toolbar-background2
@ -27,14 +22,14 @@
:style st/search-icon}
:handler (fn [])}}])
(defn contact-list []
(let [contacts (subscribe [:get :contacts])]
(fn []
(let [contacts-ds (lw/to-datasource @contacts)]
(defview contact-list []
[contacts [:get-contacts]]
[view st/contacts-list-container
[contact-list-toolbar]
(when contacts-ds
[list-view {:dataSource contacts-ds
;; todo what if there is no contacts, should we show some information
;; about this?
(when contacts
[list-view {:dataSource (lw/to-datasource contacts)
:enableEmptySections true
:renderRow render-row
:style st/contacts-list}])]))))
:style st/contacts-list}])])

View File

@ -1,3 +1,8 @@
(ns syng-im.contacts.subs
(:require-macros [reagent.ratom :refer [reaction]])
(:require [re-frame.core :refer [register-sub]]))
(register-sub :get-contacts
(fn [db _]
(let [contacts (reaction (:contacts @db))]
(reaction (vals @contacts)))))

View File

@ -1,8 +1,16 @@
(ns syng-im.contacts.views.contact
(:require-macros [syng-im.utils.views :refer [defview]])
(:require [syng-im.components.react :refer [view touchable-highlight]]
[re-frame.core :refer [dispatch]]
[re-frame.core :refer [dispatch subscribe]]
[syng-im.contacts.views.contact-inner :refer [contact-inner-view]]))
(defn contact-view [contact]
[touchable-highlight {:onPress #(dispatch [:navigate-to :chat])}
(defn on-press [chat whisper-identity]
(if chat
#(dispatch [:navigate-to :chat whisper-identity])
#(dispatch [:start-chat whisper-identity])))
(defview contact-view [{:keys [whisper-identity] :as contact}]
[chat [:get-chat whisper-identity]]
[touchable-highlight
{:onPress (on-press chat whisper-identity)}
[view {} [contact-inner-view contact]]])

View File

@ -10,6 +10,7 @@
(def app-db {:identity-password "replace-me-with-user-entered-password"
:identity "me"
:contacts []
:contacts-ids #{}
:current-chat-id "console"
:chat {:command nil
:last-message nil}
@ -39,4 +40,3 @@
[:chats chat-id :command-requests])
(defn chat-command-request-path [chat-id msg-id]
[:chats chat-id :command-requests msg-id])
(def updated-current-tag-signal-path [:current-tag-updated-signal])

View File

@ -75,52 +75,56 @@
(defn find-command [commands command-key]
(first (filter #(= command-key (:command %)) commands)))
(defn get-chat-command-content [db]
(get-in db (db/chat-command-content-path (:current-chat-id db))))
(defn get-chat-command-content
[{:keys [current-chat-id] :as db}]
(get-in db (db/chat-command-content-path current-chat-id)))
(defn set-chat-command-content [db content]
(assoc-in db
[:chats (:current-chat-id db) :command-input :content]
content))
(defn set-chat-command-content
[{:keys [current-chat-id] :as db} content]
(assoc-in db [:chats current-chat-id :command-input :content] content))
(defn get-chat-command [db]
(get-in db (db/chat-command-path (:current-chat-id db))))
(defn get-chat-command
[{:keys [current-chat-id] :as db}]
(get-in db (db/chat-command-path current-chat-id)))
(defn set-response-chat-command [db msg-id command-key]
(let [chat-id (:current-chat-id db)]
(-> db
(assoc-in [:chats chat-id :command-input :content] nil)
(assoc-in [:chats chat-id :command-input :command]
(get-command db command-key))
(assoc-in [:chats chat-id :command-input :to-msg-id] msg-id))))
(defn set-response-chat-command
[{:keys [current-chat-id] :as db} msg-id command-key]
(update-in db [:chats current-chat-id :command-input] merge
{:content nil
:command (get-command db command-key)
:to-msg-id msg-id}))
(defn set-chat-command [db command-key]
(set-response-chat-command db nil command-key))
(defn get-chat-command-to-msg-id [db]
(get-in db (db/chat-command-to-msg-id-path (:current-chat-id db))))
(defn get-chat-command-to-msg-id
[{:keys [current-chat-id] :as db}]
(get-in db (db/chat-command-to-msg-id-path current-chat-id)))
(defn stage-command [db command-info]
(update-in db (db/chat-staged-commands-path (:current-chat-id db))
(fn [staged-commands]
(if staged-commands
(conj staged-commands command-info)
[command-info]))))
(defn stage-command
[{:keys [current-chat-id] :as db} command-info]
(update-in db (db/chat-staged-commands-path current-chat-id)
#(if %
(conj % command-info)
[command-info])))
(defn unstage-command [db staged-command]
(update-in db (db/chat-staged-commands-path (:current-chat-id db))
(fn [staged-commands]
(filterv #(not= % staged-command) staged-commands))))
(defn clear-staged-commands [db]
(assoc-in db (db/chat-staged-commands-path (:current-chat-id db)) []))
(defn clear-staged-commands
[{:keys [current-chat-id] :as db}]
(assoc-in db (db/chat-staged-commands-path current-chat-id) []))
(defn get-chat-command-request [db]
(get-in db (db/chat-command-request-path (:current-chat-id db)
(defn get-chat-command-request
[{:keys [current-chat-id] :as db}]
(get-in db (db/chat-command-request-path current-chat-id
(get-chat-command-to-msg-id db))))
(defn set-chat-command-request [db msg-id handler]
(update-in db (db/chat-command-requests-path (:current-chat-id db))
(defn set-chat-command-request
[{:keys [current-chat-id] :as db} msg-id handler]
(update-in db (db/chat-command-requests-path current-chat-id)
#(assoc % msg-id handler)))
(defn parse-command-msg-content [commands content]

View File

@ -4,29 +4,27 @@
[syng-im.components.react :refer [view list-view list-item]]
[syng-im.components.toolbar :refer [toolbar]]
[syng-im.utils.listview :refer [to-datasource]]
[syng-im.participants.views.contact
:refer [participant-contact]]
[syng-im.participants.views.contact :refer [participant-contact]]
[reagent.core :as r]
[syng-im.participants.styles :as st]))
(defn new-participants-toolbar [navigator]
(defn new-participants-toolbar []
[toolbar
{:navigator navigator
:title "Add Participants"
{:title "Add Participants"
:action {:image {:source res/v ;; {:uri "icon_search"}
:style st/new-participant-image}
:handler #(dispatch [:add-new-participants navigator])}}])
:handler #(dispatch [:add-new-participants])}}])
(defn new-participants-row
[row _ _]
(list-item [participant-contact row]))
(defn new-participants [{:keys [navigator]}]
(defn new-participants []
(let [contacts (subscribe [:all-new-contacts])]
(fn []
(let [contacts-ds (to-datasource @contacts)]
[view st/participants-container
[new-participants-toolbar navigator]
[new-participants-toolbar]
[list-view {:dataSource contacts-ds
:renderRow new-participants-row
:style st/participants-list}]]))))

View File

@ -23,11 +23,6 @@
(fn [db _]
(reaction (:signed-up @db))))
(register-sub
:get-contacts
(fn [db _]
(reaction (:contacts @db))))
(register-sub :all-contacts
(fn [db _]
(let [contacts (reaction (:contacts @db))]
@ -44,7 +39,7 @@
(map :identity)
set)]
(fn #(current-participants (:whisper-identity %))
@contacts))))))
(vals @contacts)))))))
(register-sub :all-new-contacts
(fn [db _]