diff --git a/syng-im/.re-natal b/syng-im/.re-natal index 7cd8ae4ffa..20f66539ae 100644 --- a/syng-im/.re-natal +++ b/syng-im/.re-natal @@ -1,14 +1,15 @@ { "name": "SyngIm", "interface": "reagent", - "androidHost": "localhost", + "androidHost": "10.0.3.2", "modules": [ "react-native-contacts", "react-native-invertible-scroll-view", "awesome-phonenumber", "realm", "react-native-loading-spinner-overlay", - "react-native-i18n" + "react-native-i18n", + "realm/react-native" ], "imageDirs": [ "images" diff --git a/syng-im/images/deliveryfailed.png b/syng-im/images/deliveryfailed.png new file mode 100644 index 0000000000..921ee8c62b Binary files /dev/null and b/syng-im/images/deliveryfailed.png differ diff --git a/syng-im/package.json b/syng-im/package.json index d344ac3d49..9a81d2f595 100644 --- a/syng-im/package.json +++ b/syng-im/package.json @@ -12,6 +12,6 @@ "react-native-i18n": "0.0.8", "react-native-invertible-scroll-view": "^0.2.0", "react-native-loading-spinner-overlay": "0.0.6", - "realm": "^0.10.0" + "realm": "^0.11.0" } -} \ No newline at end of file +} diff --git a/syng-im/project.clj b/syng-im/project.clj index 5a5e03db46..37ed02bf72 100644 --- a/syng-im/project.clj +++ b/syng-im/project.clj @@ -17,6 +17,7 @@ ["do" "clean" ["with-profile" "prod" "cljsbuild" "once" "ios"] ["with-profile" "prod" "cljsbuild" "once" "android"]]} + :figwheel {:nrepl-port 7888} :profiles {:dev {:dependencies [[figwheel-sidecar "0.5.0-2"] [com.cemerick/piggieback "0.2.1"]] :source-paths ["src" "env/dev"] diff --git a/syng-im/src/syng_im/android/core.cljs b/syng-im/src/syng_im/android/core.cljs index 39b591b92a..2e3718b95b 100644 --- a/syng-im/src/syng_im/android/core.cljs +++ b/syng-im/src/syng_im/android/core.cljs @@ -11,8 +11,8 @@ [syng-im.components.chat :refer [chat]] [syng-im.components.sign-up :refer [sign-up-view]] [syng-im.components.sign-up-confirm :refer [sign-up-confirm-view]] - [syng-im.components.nav :as nav] - [syng-im.utils.logging :as log])) + [syng-im.utils.logging :as log] + [syng-im.navigation :as nav])) (def back-button-handler (cljs/atom {:nav nil :handler nil})) @@ -24,7 +24,7 @@ (let [new-listener (fn [] (binding [nav/*nav-render* false] (when (< 1 (.-length (.getCurrentRoutes nav))) - (.pop nav) + (nav/nav-pop nav) true)))] (reset! back-button-handler {:nav nav :handler new-listener}) diff --git a/syng-im/src/syng_im/components/chat.cljs b/syng-im/src/syng_im/components/chat.cljs index f1f6074a4f..d0b57cfb8e 100644 --- a/syng-im/src/syng_im/components/chat.cljs +++ b/syng-im/src/syng_im/components/chat.cljs @@ -1,21 +1,48 @@ (ns syng-im.components.chat (:require [re-frame.core :refer [subscribe dispatch dispatch-sync]] - [syng-im.components.react :refer [view text image touchable-highlight navigator]] - [syng-im.utils.logging :as log])) - -(def logo-img (js/require "./images/cljs.png")) - -(defn alert [title] - (.alert (.-Alert js/React) title)) + [syng-im.components.react :refer [android? + view + text + image + touchable-highlight + navigator + toolbar-android]] + [syng-im.components.realm :refer [list-view]] + [syng-im.utils.logging :as log] + [syng-im.navigation :refer [nav-pop]] + [syng-im.resources :as res] + [syng-im.utils.listview :refer [to-realm-datasource]] + [syng-im.components.invertible-scroll-view :refer [invertible-scroll-view]] + [reagent.core :as r] + [syng-im.components.chat-message :refer [chat-message]] + [syng-im.components.chat-message-new :refer [chat-message-new]])) (defn chat [{:keys [navigator]}] - (let [greeting (subscribe [:get-greeting])] + (let [messages (subscribe [:get-chat-messages])] (fn [] - [view {:style {:flex-direction "column" :margin 40 :align-items "center"}} - [text {:style {:font-size 30 :font-weight "100" :margin-bottom 20 :text-align "center"}} @greeting] - [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"]]]))) + (let [msgs @messages + _ (log/debug "messages=" msgs) + datasource (to-realm-datasource msgs)] + [view {:style {:flex 1 + :backgroundColor "white"}} + (when android? + ;; TODO add IOS version + [toolbar-android {:logo res/logo-icon + :title "Chat name" + :titleColor "#4A5258" + :subtitle "Last seen just now" + :subtitleColor "#AAB2B2" + :navIcon res/nav-back-icon + :style {:backgroundColor "white" + :height 56 + :elevation 2} + :onIconClicked (fn [] + (nav-pop navigator))}]) + [list-view {:dataSource datasource + :renderScrollComponent (fn [props] + (invertible-scroll-view nil)) + :renderRow (fn [row section-id row-id] + (r/as-element [chat-message (js->clj row :keywordize-keys true)])) + :style {:backgroundColor "black"}}] + [chat-message-new]])))) diff --git a/syng-im/src/syng_im/components/chat_message.cljs b/syng-im/src/syng_im/components/chat_message.cljs new file mode 100644 index 0000000000..e767e96df2 --- /dev/null +++ b/syng-im/src/syng_im/components/chat_message.cljs @@ -0,0 +1,123 @@ +(ns syng-im.components.chat-message + (:require [re-frame.core :refer [subscribe dispatch dispatch-sync]] + [syng-im.components.react :refer [android? + view + text + image + touchable-highlight + navigator + toolbar-android]] + [syng-im.utils.logging :as log] + [syng-im.navigation :refer [nav-pop]] + [syng-im.resources :as res] + [syng-im.constants :refer [text-content-type]])) + + +(defn message-date [{:keys [date]}] + [text {:style {:marginVertical 10 + :fontFamily "Avenir-Roman" + :fontSize 11 + :color "#AAB2B2" + :letterSpacing 1 + :lineHeight 15 + :textAlign "center" + :opacity 0.8}} + date]) + +(defn message-content-audio [{:keys [content-type content-type]}] + [view {:style {:flexDirection "row" + :alignItems "center"}} + [view {:style {:width 33 + :height 33 + :borderRadius 50 + :elevation 1}} + [image {:source res/play + :style {:width 33 + :height 33}}]] + [view {:style {:marginTop 10 + :marginLeft 10 + :width 120 + :height 26 + :elevation 1}} + [view {:style {:position "absolute" + :top 4 + :width 120 + :height 2 + :backgroundColor "#EC7262"}}] + [view {:style {:position "absolute" + :left 0 + :top 0 + :width 2 + :height 10 + :backgroundColor "#4A5258"}}] + [text {:style {:position "absolute" + :left 1 + :top 11 + :fontFamily "Avenir-Roman" + :fontSize 11 + :color "#4A5258" + :letterSpacing 1 + :lineHeight 15}} + "03:39"]]]) + +(defn message-content [{:keys [content-type content outgoing]}] + [view {:style (merge {:borderRadius 6} + (if (= content-type text-content-type) + {:paddingVertical 12 + :paddingHorizontal 16} + {:paddingVertical 14 + :paddingHorizontal 10}) + (if outgoing + {:backgroundColor "#D3EEEF"} + {:backgroundColor "#FBF6E3"}))} + (if (= content-type text-content-type) + [text {:style {:fontSize 14 + :fontFamily "Avenir-Roman" + :color "#4A5258"}} + content] + [message-content-audio {:content content + :content-type content-type}])]) + +(defn message-delivery-status [{:keys [delivery-status]}] + [view {:style {:flexDirection "row" + :marginTop 2}} + [image {:source (case delivery-status + :delivered res/delivered-icon + :seen res/seen-icon + :failed res/delivery-failed-icon) + :style {:marginTop 6 + :opacity 0.6}}] + [text {:style {:fontFamily "Avenir-Roman" + :fontSize 11 + :color "#AAB2B2" + :opacity 0.8 + :marginLeft 5}} + (case delivery-status + :delivered "Delivered" + :seen "Seen" + :failed "Failed")]]) + +(defn message-body [{:keys [msg-id content content-type outgoing delivery-status]}] + [view {:style (merge {:flexDirection "column" + :width 260 + :marginVertical 5} + (if outgoing + {:alignSelf "flex-end" + :alignItems "flex-end"} + {:alignSelf "flex-start" + :alignItems "flex-start"}))} + [message-content {:content-type content-type + :content content + :outgoing outgoing}] + (when (and outgoing delivery-status) + [message-delivery-status {:delivery-status delivery-status}])]) + +(defn chat-message [{:keys [msg-id content content-type outgoing delivery-status date new-day] :as msg}] + [view {:paddingHorizontal 15} + (when new-day + [message-date {:date date}]) + [message-body {:msg-id msg-id + :content content + :content-type content-type + :outgoing outgoing + :delivery-status (keyword delivery-status)}]]) diff --git a/syng-im/src/syng_im/components/chat_message_new.cljs b/syng-im/src/syng_im/components/chat_message_new.cljs new file mode 100644 index 0000000000..9b5f37a404 --- /dev/null +++ b/syng-im/src/syng_im/components/chat_message_new.cljs @@ -0,0 +1,51 @@ +(ns syng-im.components.chat-message-new + (:require [re-frame.core :refer [subscribe dispatch dispatch-sync]] + [syng-im.components.react :refer [android? + view + image + text-input]] + [syng-im.utils.logging :as log] + [syng-im.resources :as res] + [reagent.core :as r])) + + +(defn chat-message-new [] + (let [text (r/atom nil) + chat-id (subscribe [:get-current-chat-id])] + (fn [] + [view {:style {:flexDirection "row" + :margin 10 + :height 40 + :backgroundColor "#E5F5F6" + :borderRadius 5}} + [image {:source res/mic + :style {:marginTop 11 + :marginLeft 14 + :width 13 + :height 20}}] + [text-input {:underlineColorAndroid "#9CBFC0" + :style {:flex 1 + :marginLeft 18 + :lineHeight 42 + :fontSize 14 + :fontFamily "Avenir-Roman" + :color "#9CBFC0"} + :autoFocus true + :placeholder "Enter your message here" + :value @text + :onChangeText (fn [new-text] + (reset! text new-text) + (r/flush)) + :onSubmitEditing (fn [e] + (dispatch [:send-chat-msg @chat-id @text]) + (reset! text nil))}] + [image {:source res/smile + :style {:marginTop 11 + :marginRight 12 + :width 18 + :height 18}}] + [image {:source res/att + :style {:marginTop 14 + :marginRight 16 + :width 17 + :height 14}}]]))) diff --git a/syng-im/src/syng_im/components/contact_list/contact.cljs b/syng-im/src/syng_im/components/contact_list/contact.cljs index 4fdbc52b87..c2bee18212 100644 --- a/syng-im/src/syng_im/components/contact_list/contact.cljs +++ b/syng-im/src/syng_im/components/contact_list/contact.cljs @@ -1,7 +1,7 @@ (ns syng-im.components.contact-list.contact (:require [syng-im.components.react :refer [view text image touchable-highlight]] [syng-im.components.resources :as res] - [syng-im.components.nav :as nav])) + [syng-im.navigation :as nav])) (defn show-chat [navigator whisper-identity] (nav/nav-push navigator {:view-id :chat})) diff --git a/syng-im/src/syng_im/components/invertible-scroll-view.cljs b/syng-im/src/syng_im/components/invertible-scroll-view.cljs deleted file mode 100644 index 6066fd6cc0..0000000000 --- a/syng-im/src/syng_im/components/invertible-scroll-view.cljs +++ /dev/null @@ -1,6 +0,0 @@ -(ns syng-im.components.invertible-scroll-view - (:require [reagent.core :as r])) - -(def react-invertible-scroll-view (js/require "react-native-invertible-scroll-view")) - -(def invertible-scroll-view (r/adapt-react-class react-invertible-scroll-view)) diff --git a/syng-im/src/syng_im/components/invertible_scroll_view.cljs b/syng-im/src/syng_im/components/invertible_scroll_view.cljs new file mode 100644 index 0000000000..fea247863c --- /dev/null +++ b/syng-im/src/syng_im/components/invertible_scroll_view.cljs @@ -0,0 +1,8 @@ +(ns syng-im.components.invertible-scroll-view) + +(set! js/window.InvertibleScrollView (js/require "react-native-invertible-scroll-view")) + +(defn invertible-scroll-view [props] + (js/React.createElement js/InvertibleScrollView + (clj->js (merge {:inverted true} props)))) + diff --git a/syng-im/src/syng_im/components/react.cljs b/syng-im/src/syng_im/components/react.cljs index fca29f0d51..565fdd3ae3 100644 --- a/syng-im/src/syng_im/components/react.cljs +++ b/syng-im/src/syng_im/components/react.cljs @@ -1,7 +1,7 @@ (ns syng-im.components.react (:require [reagent.core :as r])) -(set! js/React (js/require "react-native")) +(set! js/window.React (js/require "react-native")) (def app-registry (.-AppRegistry js/React)) (def navigator (r/adapt-react-class (.-Navigator js/React))) @@ -13,6 +13,10 @@ (def list-view (r/adapt-react-class (.-ListView js/React))) (def text-input (r/adapt-react-class (.-TextInput js/React))) +(def platform (.. js/React -Platform -OS)) + +(def android? (= platform "android")) + (defn list-item [component] (r/as-element component)) diff --git a/syng-im/src/syng_im/components/realm.cljs b/syng-im/src/syng_im/components/realm.cljs new file mode 100644 index 0000000000..524c2f6909 --- /dev/null +++ b/syng-im/src/syng_im/components/realm.cljs @@ -0,0 +1,18 @@ +(ns syng-im.components.realm + (:require [reagent.core :as r])) + +(set! js/window.RealmReactNative (js/require "realm/react-native")) + +(def list-view (r/adapt-react-class (.-ListView js/RealmReactNative))) + +(comment + + + ;(set! js/wat (js/require "realm.react-native.ListView")) + ;(.-Results js/Realm) + ; + ;(r/realm) + ; + ;(require '[syng-im.persistence.realm :as r]) + + ) diff --git a/syng-im/src/syng_im/components/sign_up.cljs b/syng-im/src/syng_im/components/sign_up.cljs index 6b287fd8df..b4983f84eb 100644 --- a/syng-im/src/syng_im/components/sign_up.cljs +++ b/syng-im/src/syng_im/components/sign_up.cljs @@ -4,7 +4,7 @@ toolbar-android text-input]] [syng-im.components.resources :as res] [syng-im.components.spinner :refer [spinner]] - [syng-im.components.nav :as nav] + [syng-im.navigation :as nav] [syng-im.utils.utils :refer [log toast http-post]] [syng-im.utils.phone-number :refer [format-phone-number]])) @@ -25,7 +25,6 @@ user-phone-number (subscribe [:get-user-phone-number]) user-identity (subscribe [:get-user-identity])] (fn [] - ;; (reset! nav-atom navigator) [view {:style {:flex 1}} [view {:style {:flex 1 :backgroundColor "white"}} diff --git a/syng-im/src/syng_im/components/sign_up_confirm.cljs b/syng-im/src/syng_im/components/sign_up_confirm.cljs index a694340c02..220872a6d8 100644 --- a/syng-im/src/syng_im/components/sign_up_confirm.cljs +++ b/syng-im/src/syng_im/components/sign_up_confirm.cljs @@ -6,7 +6,7 @@ toolbar-android text-input]] [syng-im.components.resources :as res] [syng-im.components.spinner :refer [spinner]] - [syng-im.components.nav :as nav] + [syng-im.navigation :as nav] [syng-im.utils.utils :refer [log toast http-post]])) (defn show-home-view [navigator] diff --git a/syng-im/src/syng_im/db.cljs b/syng-im/src/syng_im/db.cljs index 1369ed6fb5..2514a81f3b 100644 --- a/syng-im/src/syng_im/db.cljs +++ b/syng-im/src/syng_im/db.cljs @@ -5,14 +5,15 @@ (def schema {:greeting s/Str}) ;; initial state of app-db -(def app-db {:greeting "Hello Clojure in iOS and Android!" +(def app-db {:greeting "Hello Clojure in iOS and Android!" :identity-password "replace-me-with-user-entered-password" - :contacts []}) + :contacts [] + :chat {:current-chat-id "0x0479a5ed1f38cadfad1db6cd56c4b659b0ebe052bbe9efa950f6660058519fa4ca6be2dda66afa80de96ab00eb97a2605d5267a1e8f4c2a166ab551f6826608cdd"} + :chats {}}) (def protocol-initialized-path [:protocol-initialized]) -(def simple-store-path [:simple-store]) (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]) +(defn latest-msg-id-path [chat-id] + [:chats chat-id :arrived-message-id]) diff --git a/syng-im/src/syng_im/handlers.cljs b/syng-im/src/syng_im/handlers.cljs index ddbacc5797..2d088a88c6 100644 --- a/syng-im/src/syng_im/handlers.cljs +++ b/syng-im/src/syng_im/handlers.cljs @@ -10,10 +10,14 @@ [syng-im.models.user-data :as user-data] [syng-im.models.contacts :as contacts] [syng-im.models.messages :refer [save-message - new-message-arrived]] + update-message! + message-by-id]] + [syng-im.models.chat :refer [signal-chat-updated]] [syng-im.handlers.server :as server] [syng-im.handlers.contacts :as contacts-service] - [syng-im.utils.logging :as log])) + [syng-im.utils.logging :as log] + [syng-im.protocol.api :as api] + [syng-im.constants :refer [text-content-type]])) ;; -- Middleware ------------------------------------------------------------ ;; @@ -57,7 +61,36 @@ (fn [db [_ {chat-id :from msg-id :msg-id :as msg}]] (save-message chat-id msg) - (new-message-arrived db chat-id msg-id))) + (signal-chat-updated db chat-id))) + +(register-handler :acked-msg + (fn [db [_ from msg-id]] + (update-message! {:msg-id msg-id + :delivery-status :delivered}) + (signal-chat-updated db from))) + +(register-handler :msg-delivery-failed + (fn [db [_ msg-id]] + (update-message! {:msg-id msg-id + :delivery-status :failed}) + (let [{:keys [chat-id]} (message-by-id msg-id)] + (signal-chat-updated db chat-id)))) + +(register-handler :send-chat-msg + (fn [db [_ chat-id text]] + (log/debug "chat-id" chat-id "text" text) + (let [{msg-id :msg-id + {from :from + to :to} :msg} (api/send-user-msg {:to chat-id + :content text}) + msg {:msg-id msg-id + :from from + :to to + :content text + :content-type text-content-type + :outgoing true}] + (save-message chat-id msg) + (signal-chat-updated db chat-id)))) ;; -- User data -------------------------------------------------------------- diff --git a/syng-im/src/syng_im/ios/core.cljs b/syng-im/src/syng_im/ios/core.cljs index e3f3ac88e1..02147cce96 100644 --- a/syng-im/src/syng_im/ios/core.cljs +++ b/syng-im/src/syng_im/ios/core.cljs @@ -4,7 +4,7 @@ [syng-im.handlers] [syng-im.subs])) -(set! js/React (js/require "react-native")) +(set! js/window.React (js/require "react-native")) (def app-registry (.-AppRegistry js/React)) (def text (r/adapt-react-class (.-Text js/React))) diff --git a/syng-im/src/syng_im/models/chat.cljs b/syng-im/src/syng_im/models/chat.cljs index e312d3daf5..28a598714b 100644 --- a/syng-im/src/syng_im/models/chat.cljs +++ b/syng-im/src/syng_im/models/chat.cljs @@ -6,3 +6,19 @@ (defn current-chat-id [db] (get-in db db/current-chat-id-path)) + +(defn signal-chat-updated [db chat-id] + (update-in db (db/latest-msg-id-path chat-id) (fn [current] + (if current + (inc current) + 0)))) + +(defn latest-msg-id [db chat-id] + (->> (db/latest-msg-id-path chat-id) + (get-in db))) + +(comment + + (swap! re-frame.db/app-db (fn [db] + (signal-chat-updated db "0x0479a5ed1f38cadfad1db6cd56c4b659b0ebe052bbe9efa950f6660058519fa4ca6be2dda66afa80de96ab00eb97a2605d5267a1e8f4c2a166ab551f6826608cdd"))) + ) \ No newline at end of file diff --git a/syng-im/src/syng_im/models/messages.cljs b/syng-im/src/syng_im/models/messages.cljs index 291d6ded70..36257e47c8 100644 --- a/syng-im/src/syng_im/models/messages.cljs +++ b/syng-im/src/syng_im/models/messages.cljs @@ -2,35 +2,44 @@ (:require [syng-im.persistence.realm :as r] [cljs.reader :refer [read-string]] [syng-im.utils.random :refer [timestamp]] - [syng-im.db :as db])) + [syng-im.db :as db] + [syng-im.utils.logging :as log])) (defn save-message [chat-id {:keys [from to msg-id content content-type outgoing] :or {outgoing false} :as msg}] (when-not (r/exists? :msgs :msg-id msg-id) (r/write (fn [] - (r/create :msgs {:chat-id chat-id - :msg-id msg-id - :from from - :to to - :content content - :content-type content-type - :outgoing outgoing - :timestamp (timestamp)} true))))) - -(defn get-messages* [chat-id] - (-> (r/get-by-field :msgs :chat-id chat-id) - (r/sorted :timestamp :desc) - (r/page 0 10))) + (r/create :msgs {:chat-id chat-id + :msg-id msg-id + :from from + :to to + :content content + :content-type content-type + :outgoing outgoing + :timestamp (timestamp) + :delivery-status nil} true))))) (defn get-messages [chat-id] - (-> (get-messages* chat-id) - (js->clj :keywordize-keys true))) + (-> (r/get-by-field :msgs :chat-id chat-id) + (r/sorted :timestamp :desc))) -(defn new-message-arrived [db chat-id msg-id] - (assoc-in db (db/arrived-message-path chat-id) msg-id)) +(defn message-by-id [msg-id] + (-> (r/get-by-field :msgs :msg-id msg-id) + (r/single-cljs))) + +(defn update-message! [msg] + (r/write + (fn [] + (r/create :msgs msg true)))) (comment + (update-message! {:msg-id "1459175391577-a2185a35-5c49-5a6b-9c08-6eb5b87ceb7f" + :delivery-status "seen2"}) + + (r/get-by-field :msgs :msg-id "1459175391577-a2185a35-5c49-5a6b-9c08-6eb5b87ceb7f") + + (save-message "0x040028c500ff086ecf1cfbb3c1a7240179cde5b86f9802e6799b9bbe9cdd7ad1b05ae8807fa1f9ed19cc8ce930fc2e878738c59f030a6a2f94b3522dc1378ff154" {:msg-id "153" :content "hello!" @@ -38,9 +47,11 @@ (get-messages* "0x040028c500ff086ecf1cfbb3c1a7240179cde5b86f9802e6799b9bbe9cdd7ad1b05ae8807fa1f9ed19cc8ce930fc2e878738c59f030a6a2f94b3522dc1378ff154") - (get-messages "0x043df89d36f6e3d8ade18e55ac3e2e39406ebde152f76f2f82d674681d59319ffd9880eebfb4f5f8d5c222ec485b44d6e30ba3a03c96b1c946144fdeba1caccd43") + (get-messages "0x0479a5ed1f38cadfad1db6cd56c4b659b0ebe052bbe9efa950f6660058519fa4ca6be2dda66afa80de96ab00eb97a2605d5267a1e8f4c2a166ab551f6826608cdd") (doseq [msg (get-messages* "0x043df89d36f6e3d8ade18e55ac3e2e39406ebde152f76f2f82d674681d59319ffd9880eebfb4f5f8d5c222ec485b44d6e30ba3a03c96b1c946144fdeba1caccd43")] (r/delete msg)) + @re-frame.db/app-db + ) \ No newline at end of file diff --git a/syng-im/src/syng_im/models/protocol.cljs b/syng-im/src/syng_im/models/protocol.cljs index 47f164e6a2..bb3bb3ca9f 100644 --- a/syng-im/src/syng_im/models/protocol.cljs +++ b/syng-im/src/syng_im/models/protocol.cljs @@ -25,3 +25,8 @@ (when encrypted (-> (password-decrypt password encrypted) (read-string))))) + +(comment + + (stored-identity @re-frame.db/app-db) + ) \ No newline at end of file diff --git a/syng-im/src/syng_im/components/nav.cljs b/syng-im/src/syng_im/navigation.cljs similarity index 93% rename from syng-im/src/syng_im/components/nav.cljs rename to syng-im/src/syng_im/navigation.cljs index 6440e79de5..1842b87ca6 100644 --- a/syng-im/src/syng_im/components/nav.cljs +++ b/syng-im/src/syng_im/navigation.cljs @@ -1,4 +1,4 @@ -(ns syng-im.components.nav) +(ns syng-im.navigation) (def ^{:dynamic true :private true} *nav-render* "Flag to suppress navigator re-renders from outside om when pushing/popping." diff --git a/syng-im/src/syng_im/persistence/realm.cljs b/syng-im/src/syng_im/persistence/realm.cljs index 1c7201f7ec..6e4c38c7f8 100644 --- a/syng-im/src/syng_im/persistence/realm.cljs +++ b/syng-im/src/syng_im/persistence/realm.cljs @@ -4,7 +4,7 @@ [syng-im.utils.types :refer [to-string]]) (:refer-clojure :exclude [exists?])) -(set! js/Realm (js/require "realm")) +(set! js/window.Realm (js/require "realm")) (def opts {:schema [{:name "Contact" :properties {:phone-number "string" @@ -17,14 +17,17 @@ :value "string"}} {:name :msgs :primaryKey :msg-id - :properties {:msg-id "string" - :from "string" - :to "string" - :content "string" ;; TODO make it ArrayBuffer - :content-type "string" - :timestamp "int" - :chat-id "string" - :outgoing "bool"}}]}) + :properties {:msg-id "string" + :from "string" + :to "string" + :content "string" ;; TODO make it ArrayBuffer + :content-type "string" + :timestamp "int" + :chat-id {:type "string" + :indexed true} + :outgoing "bool" + :delivery-status {:type "string" + :optional true}}}]}) (def realm (js/Realm. (clj->js opts))) @@ -34,7 +37,10 @@ (into {}))) (defn field-type [schema-name field] - (get-in schema-by-name [schema-name :properties field])) + (let [field-def (get-in schema-by-name [schema-name :properties field])] + (if (map? field-def) + (:type field-def) + field-def))) (defn write [f] (.write realm f)) @@ -52,14 +58,13 @@ (let [value (to-string value) query (str (name field) "=" (if (= "string" (field-type schema-name field)) (str "\"" value "\"") - value)) - ;_ (log/debug query) - ] + value))] query)) (defn get-by-field [schema-name field value] - (-> (.objects realm (name schema-name)) - (.filtered (to-query schema-name :eq field value)))) + (let [q (to-query schema-name :eq field value)] + (-> (.objects realm (name schema-name)) + (.filtered q)))) (defn sorted [results field-name order] (.sorted results (to-string field-name) (if (= order :asc) @@ -92,3 +97,13 @@ (defn get-list [schema-name] (vals (js->clj (.objects realm schema-name) :keywordize-keys true))) + + +(comment + + (write #(.create realm "msgs" (clj->js {:msg-id "1459175391577-a2185a35-5c49-5a6b-9c08-6eb5b87ceb7f" + :content "sdfd" + :delivery-status "seen"}) true)) + + + ) \ No newline at end of file diff --git a/syng-im/src/syng_im/protocol/protocol_handler.cljs b/syng-im/src/syng_im/protocol/protocol_handler.cljs index 2e240cf910..69e889a717 100644 --- a/syng-im/src/syng_im/protocol/protocol_handler.cljs +++ b/syng-im/src/syng_im/protocol/protocol_handler.cljs @@ -17,10 +17,10 @@ (dispatch [:protocol-initialized identity])) :new-msg (let [{:keys [from to payload]} event] (dispatch [:received-msg (assoc payload :from from :to to)])) - ;:msg-acked (let [{:keys [msg-id]} event] - ; (add-to-chat "chat" ":" (str "Message " msg-id " was acked"))) - ;:delivery-failed (let [{:keys [msg-id]} event] - ; (add-to-chat "chat" ":" (str "Delivery of message " msg-id " failed"))) + :msg-acked (let [{:keys [msg-id from]} event] + (dispatch [:acked-msg from msg-id])) + :delivery-failed (let [{:keys [msg-id]} event] + (dispatch [:msg-delivery-failed msg-id])) ;:new-group-chat (let [{:keys [from group-id identities]} event] ; (set-group-id! group-id) ; (set-group-identities identities) diff --git a/syng-im/src/syng_im/resources.cljs b/syng-im/src/syng_im/resources.cljs new file mode 100644 index 0000000000..19a0c2ecb7 --- /dev/null +++ b/syng-im/src/syng_im/resources.cljs @@ -0,0 +1,14 @@ +(ns syng-im.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 delivery-failed-icon (js/require "./images/deliveryfailed.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")) + diff --git a/syng-im/src/syng_im/subs.cljs b/syng-im/src/syng_im/subs.cljs index c07e455554..95b53877ae 100644 --- a/syng-im/src/syng_im/subs.cljs +++ b/syng-im/src/syng_im/subs.cljs @@ -1,12 +1,28 @@ (ns syng-im.subs (:require-macros [reagent.ratom :refer [reaction]]) - (:require [re-frame.core :refer [register-sub]])) + (:require [re-frame.core :refer [register-sub]] + [syng-im.models.chat :refer [current-chat-id + latest-msg-id]] + [syng-im.models.messages :refer [get-messages]])) -(register-sub - :get-greeting - (fn [db _] - (reaction - (get @db :greeting)))) +(register-sub :get-greeting (fn [db _] + (reaction + (get @db :greeting)))) + +(register-sub :get-chat-messages + (fn [db _] + (let [chat-id (-> (current-chat-id @db) + (reaction)) + latest-msg (-> (latest-msg-id @db @chat-id) + (reaction))] + ;; latest-msg signals us that a new message has been added + (reaction + (let [_ @latest-msg] + (get-messages @chat-id)))))) + +(register-sub :get-current-chat-id (fn [db _] + (-> (current-chat-id @db) + (reaction)))) ;; -- User data -------------------------------------------------------------- (register-sub diff --git a/syng-im/src/syng_im/utils/listview.cljs b/syng-im/src/syng_im/utils/listview.cljs new file mode 100644 index 0000000000..b6bd9ec080 --- /dev/null +++ b/syng-im/src/syng_im/utils/listview.cljs @@ -0,0 +1,13 @@ +(ns syng-im.utils.listview + (:require-macros [natal-shell.data-source :refer [data-source clone-with-rows]]) + (:require [syng-im.components.realm])) + +(defn to-datasource [msgs] + (-> (data-source {:rowHasChanged (fn [row1 row2] + (not= row1 row2))}) + (clone-with-rows msgs))) + +(defn to-realm-datasource [msgs] + (-> (js/RealmReactNative.ListView.DataSource. (cljs.core/clj->js {:rowHasChanged (fn [row1 row2] + (not= row1 row2))})) + (clone-with-rows msgs))) \ No newline at end of file