Merge pull request #204 from status-im/feature/delivery-of-messages

Improved message delivery
This commit is contained in:
Roman Volosovskyi 2016-08-29 13:47:00 +03:00 committed by GitHub
commit 22332e0781
34 changed files with 383 additions and 257 deletions

View File

@ -9,8 +9,8 @@
[re-frame "0.7.0"]
[prismatic/schema "1.0.4"]
^{:voom {:repo "git@github.com:status-im/status-lib.git"
:branch "master"}}
[status-im/protocol "0.1.3-20160818_172519-g2f734a6"]
:branch "message-delivery"}}
[status-im/protocol "0.2.0-20160829_094621-g83381b2"]
[natal-shell "0.3.0"]
[com.andrewmcveigh/cljs-time "0.4.0"]
[tailrecursion/cljs-priority-map "1.2.0"]

View File

@ -104,9 +104,9 @@
(register-handler :load-accounts load-accounts!)
(defn console-create-account [db _]
(let [msg-id (random/id)]
(let [message-id (random/id)]
(dispatch [:received-message
{:msg-id msg-id
{:message-id message-id
:content {:command (name :keypair)
:content (label :t/keypair-generated)}
:content-type content-type-command-request

View File

@ -1,20 +1,20 @@
(ns status-im.chat.handlers
(:require-macros [cljs.core.async.macros :as am])
(:require [re-frame.core :refer [enrich after debug dispatch]]
[status-im.models.commands :as commands]
[clojure.string :as str]
[status-im.components.styles :refer [default-chat-color]]
[status-im.chat.suggestions :as suggestions]
[status-im.protocol.api :as api]
[status-im.models.chats :as chats]
[status-im.models.messages :as messages]
[status-im.models.pending-messages :as pending-messages]
[status-im.constants :refer [text-content-type
content-type-command
content-type-command-request
default-number-of-messages]]
[status-im.utils.random :as random]
[status-im.chat.sign-up :as sign-up-service]
[status-im.models.chats :as chats]
[status-im.navigation.handlers :as nav]
[status-im.utils.handlers :refer [register-handler] :as u]
[status-im.persistence.realm.core :as r]
@ -149,17 +149,17 @@
(register-handler ::set-text update-text)
(defn set-message-shown
[db chat-id msg-id]
[db chat-id message-id]
(update-in db [:chats chat-id :messages] (fn [messages]
(map (fn [msg]
(if (= msg-id (:msg-id msg))
(assoc msg :new? false)
msg))
(map (fn [message]
(if (= message-id (:message-id message))
(assoc message :new? false)
message))
messages))))
(register-handler :set-message-shown
(fn [db [_ {:keys [chat-id msg-id]}]]
(set-message-shown db chat-id msg-id)))
(fn [db [_ {:keys [chat-id message-id]}]]
(set-message-shown db chat-id message-id)))
(defn init-console-chat
[{:keys [chats] :as db} existing-account?]
@ -253,6 +253,11 @@
(after #(dispatch [:load-unviewed-messages!]))
((enrich initialize-chats) load-chats!))
(register-handler :initialize-pending-messages
(u/side-effect!
(fn [_ _]
(api/init-pending-messages (pending-messages/get-pending-messages)))))
(defmethod nav/preload-data! :chat
[{:keys [current-chat-id] :as db} [_ _ id]]
(let [chat-id (or id current-chat-id)
@ -332,7 +337,7 @@
(messages/save-message
current-chat-id
{:from "system"
:msg-id (random/id)
:message-id (random/id)
:content "You left this chat"
:content-type text-content-type}))
@ -392,7 +397,7 @@
(register-handler :send-seen!
(after (fn [_ [_ chat-id message-id]]
(when-not (console? chat-id))
(dispatch [:msg-seen chat-id message-id])))
(dispatch [:message-seen chat-id message-id])))
(u/side-effect!
(fn [_ [_ chat-id message-id]]
(when-not (console? chat-id)

View File

@ -128,11 +128,11 @@
(after invoke-command-preview!)]
(fn [db [_ chat-id]]
(let [db (assoc-in db [:chats chat-id :input-text] nil)
{:keys [command content to-msg-id]} (command-input db)
{:keys [command content to-message-id]} (command-input db)
content' (content-by-command command content)
command-info {:command command
:content content'
:to-message to-msg-id
:to-message to-message-id
:created-at (time/now-ms)
:id (random/id)}]
(-> db
@ -157,9 +157,9 @@
(after #(dispatch [:command-edit-mode]))]
set-chat-command)
(defn set-response-command [db [_ to-msg-id command-key]]
(defn set-response-command [db [_ to-message-id command-key]]
(-> db
(commands/set-command-input :responses to-msg-id command-key)
(commands/set-command-input :responses to-message-id command-key)
(assoc :canceled-command false)))
(register-handler :set-response-chat-command

View File

@ -8,7 +8,7 @@
[cljs.reader :refer [read-string]]
[status-im.models.chats :as c]))
(defn check-previev [{:keys [content] :as message}]
(defn check-preview [{:keys [content] :as message}]
(if-let [preview (:preview content)]
(let [rendered-preview (generate-hiccup (read-string preview))]
(assoc message
@ -24,8 +24,8 @@
(:public-key (accounts current-account-id)))
(defn receive-message
[db [_ {:keys [from group-id chat-id msg-id] :as message}]]
(let [same-message (messages/get-message msg-id)
[db [_ {:keys [from group-id chat-id message-id] :as message}]]
(let [same-message (messages/get-message message-id)
current-identity (get-current-identity db)]
(when-not (or same-message (= from current-identity))
(let [group-chat? (not (nil? group-id))
@ -33,8 +33,8 @@
previous-message (messages/get-last-message chat-id')
message' (assoc (->> message
(cu/check-author-direction previous-message)
(check-previev))
:delivery-status :pending
(check-preview))
:delivery-status :sending
:chat-id chat-id')]
(store-message message')
(when-not (c/chat-exists? chat-id')
@ -42,7 +42,7 @@
(dispatch [::add-message message'])
(when (= (:content-type message') content-type-command-request)
(dispatch [:add-request chat-id' message']))
(dispatch [:add-unviewed-message chat-id' msg-id])))))
(dispatch [:add-unviewed-message chat-id' message-id])))))
(register-handler :received-message
(u/side-effect! receive-message))

View File

@ -10,9 +10,9 @@
(requests/save-request new-request))
(defn add-request
[db [_ chat-id {:keys [msg-id content]}]]
[db [_ chat-id {:keys [message-id content]}]]
(let [request {:chat-id chat-id
:message-id msg-id
:message-id message-id
:type (:command content)
:added (js/Date.)}
request' (update request :type keyword)]
@ -24,9 +24,8 @@
[{:keys [current-chat-id] :as db} [_ chat-id]]
(let [chat-id' (or chat-id current-chat-id)
requests (-> ;; todo maybe limit is needed
(realm/get-by-fields :account :request
{:chat-id chat-id'
:status "open"})
(realm/get-by-fields :account :request :and [[:chat-id chat-id']
[:status "open"]])
(realm/sorted :added :desc)
(realm/collection->map))
requests' (map #(update % :type keyword) requests)]
@ -36,9 +35,8 @@
[_ [_ chat-id message-id]]
(realm/write :account
(fn []
(-> (realm/get-by-fields :account :request
{:chat-id chat-id
:message-id message-id})
(-> (realm/get-by-fields :account :request :and [[:chat-id chat-id]
[:message-id message-id]])
(realm/single)
(.-status)
(set! "answered")))))

View File

@ -13,18 +13,38 @@
content-type-command
content-type-command-request
default-number-of-messages]]
[status-im.protocol.api :as api]))
[status-im.protocol.api :as api]
[status-im.utils.logging :as log]))
(defn default-delivery-status [chat-id]
(if (cu/console? chat-id)
:seen
:pending))
:sending))
(defn prepare-message
[{:keys [identity current-chat-id] :as db} _]
(let [text (get-in db [:chats current-chat-id :input-text])
[command] (suggestions/check-suggestion db (str text " "))
message (cu/check-author-direction
db current-chat-id
{:message-id (random/id)
:chat-id current-chat-id
:content text
:to current-chat-id
:from identity
:content-type text-content-type
:delivery-status (default-delivery-status current-chat-id)
:outgoing true
:timestamp (time/now-ms)})]
(if command
(commands/set-command-input db :commands command)
(assoc db :new-message (when-not (s/blank? text) message)))))
(defn prepare-command
[identity chat-id {:keys [preview preview-string content command to-message]}]
(let [content {:command (command :name)
:content content}]
{:msg-id (random/id)
{:message-id (random/id)
:from identity
:to chat-id
:content (assoc content :preview preview-string)
@ -37,7 +57,7 @@
:type (:type command)
:has-handler (:has-handler command)}))
(register-handler :send-chat-msg
(register-handler :send-chat-message
(u/side-effect!
(fn [{:keys [current-chat-id identity current-account-id] :as db}]
(let [staged-commands (vals (get-in db [:chats current-chat-id :staged-commands]))
@ -60,7 +80,7 @@
(fn [_ [_ {:keys [commands message] :as params}]]
(doseq [{:keys [command] :as message} commands]
(let [params' (assoc params :command message)]
(if (:pending message)
(if (:sending message)
(dispatch [:navigate-to :confirm])
(if (:has-handler command)
(dispatch [::invoke-command-handlers! params'])
@ -134,18 +154,21 @@
(register-handler ::prepare-message
(u/side-effect!
(fn [db [_ {:keys [chat-id identity message] :as params}]]
(let [message' (cu/check-author-direction
(let [{:keys [group-chat]} (get-in db [:chats chat-id])
message' (cu/check-author-direction
db chat-id
{:msg-id (random/id)
{:message-id (random/id)
:chat-id chat-id
:content message
:to chat-id
:from identity
:content-type text-content-type
:delivery-status (default-delivery-status chat-id)
:outgoing true
:timestamp (time/now-ms)})
params' (assoc params :message message')]
message'' (if group-chat
(assoc message' :group-id chat-id :message-type :group-user-message)
(assoc message' :to chat-id :message-type :user-message))
params' (assoc params :message message'')]
(dispatch [::add-message params'])
(dispatch [::save-message! params'])))))
@ -162,13 +185,13 @@
(register-handler ::send-message!
(u/side-effect!
(fn [db [_ {:keys [message chat-id]}]]
(fn [_ [_ {{:keys [message-type]
:as message} :message
chat-id :chat-id}]]
(when (and message (cu/not-console? chat-id))
(let [{:keys [group-chat]} (get-in db [:chats chat-id])
message' (select-keys message [:content :msg-id])]
(if group-chat
(api/send-group-user-msg (assoc message' :group-id chat-id))
(api/send-user-msg (assoc message' :to chat-id))))))))
(if (= message-type :group-user-message)
(api/send-group-user-message message)
(api/send-user-message message))))))
(register-handler ::send-command-protocol!
(u/side-effect!
@ -179,5 +202,5 @@
message {:content content
:content-type content-type-command}]
(if group-chat
(api/send-group-user-msg (assoc message :group-id chat-id))
(api/send-user-msg (assoc message :to chat-id)))))))))
(api/send-group-user-message (assoc message :group-id chat-id))
(api/send-user-message (assoc message :to chat-id)))))))))

View File

@ -4,17 +4,15 @@
[status-im.persistence.realm.core :as realm]))
(defn delivered-messages []
(-> (realm/get-by-fields
:account :message
{:delivery-status :delivered
:outgoing false})
(-> (realm/get-by-fields :account :message :and [[:delivery-status :delivered]
[:outgoing false]])
(realm/collection->map)))
(defn set-unviewed-messages [db]
(let [messages (->> (::raw-unviewed-messages db)
(group-by :chat-id)
(map (fn [[id messages]]
[id {:messages-ids (map :msg-id messages)
[id {:messages-ids (map :message-id messages)
:count (count messages)}]))
(into {}))]
(-> db

View File

@ -34,13 +34,13 @@
[identity contact]))
(into {})))
(defn add-msg-color [{:keys [from] :as msg} contact-by-identity]
(defn add-message-color [{:keys [from] :as message} contact-by-identity]
(if (= "system" from)
(assoc msg :text-color :#4A5258
:background-color :#D3EEEF)
(assoc message :text-color :#4A5258
:background-color :#D3EEEF)
(let [{:keys [text-color background-color]} (get contact-by-identity from)]
(assoc msg :text-color text-color
:background-color background-color))))
(assoc message :text-color text-color
:background-color background-color))))
(defview chat-icon []
[chat-id [:chat :chat-id]
@ -66,11 +66,11 @@
(defn message-row [{:keys [contact-by-identity platform-specific group-chat messages-count]}]
(fn [row _ idx]
(let [msg (-> row
(add-msg-color contact-by-identity)
(assoc :group-chat group-chat)
(assoc :last-msg (= (js/parseInt idx) (dec messages-count))))]
(list-item [chat-message msg platform-specific]))))
(let [message (-> row
(add-message-color contact-by-identity)
(assoc :group-chat group-chat)
(assoc :last-message (= (js/parseInt idx) (dec messages-count))))]
(list-item [chat-message message platform-specific]))))
(defn on-action-selected [position]
(case position

View File

@ -16,8 +16,8 @@
content-type-status]]
[status-im.i18n :refer [label]]))
(defn send-console-msg [text]
{:msg-id (random/id)
(defn send-console-message [text]
{:message-id (random/id)
:from "me"
:to "console"
:content text
@ -36,9 +36,9 @@
;; -- Send phone number ----------------------------------------
(defn on-sign-up-response [& [message]]
(let [msg-id (random/id)]
(let [message-id (random/id)]
(dispatch [:received-message
{:msg-id msg-id
{:message-id message-id
:content (command-content
:confirmation-code
(or message (label :t/confirmation-code)))
@ -64,7 +64,7 @@
;; -- Send confirmation code and synchronize contacts---------------------------
(defn on-sync-contacts []
(dispatch [:received-message
{:msg-id (random/id)
{:message-id (random/id)
:content (label :t/contacts-syncronized)
:content-type text-content-type
:outgoing false
@ -78,7 +78,7 @@
(defn on-send-code-response [body]
(dispatch [:received-message
{:msg-id (random/id)
{:message-id (random/id)
:content (:message body)
:content-type text-content-type
:outgoing false
@ -95,9 +95,9 @@
(on-sign-up-response (label :t/incorrect-code)))))
(defn start-signup []
(let [msg-id (random/id)]
(let [message-id (random/id)]
(dispatch [:received-message
{:msg-id msg-id
{:message-id message-id
:content (command-content
:phone
(label :t/phone-number-required))
@ -110,7 +110,7 @@
(defn save-password [password mnemonic]
;; TODO validate and save password
(dispatch [:received-message
{:msg-id (random/id)
{:message-id (random/id)
:content (label :t/password-saved)
:content-type text-content-type
:outgoing false
@ -118,7 +118,7 @@
:to "me"
:new? false}])
(dispatch [:received-message
{:msg-id (random/id)
{:message-id (random/id)
:content (label :t/generate-passphrase)
:content-type text-content-type
:outgoing false
@ -126,7 +126,7 @@
:to "me"
:new? false}])
(dispatch [:received-message
{:msg-id (random/id)
{:message-id (random/id)
:content (label :t/here-is-your-passphrase)
:content-type text-content-type
:outgoing false
@ -134,7 +134,7 @@
:to "me"
:new? false}])
(dispatch [:received-message
{:msg-id (random/id)
{:message-id (random/id)
:content mnemonic
:content-type text-content-type
:outgoing false
@ -142,7 +142,7 @@
:to "me"
:new? false}])
(dispatch [:received-message
{:msg-id "8"
{:message-id "8"
:content (label :t/written-down)
:content-type text-content-type
:outgoing false
@ -155,7 +155,7 @@
(def intro-status
{:msg-id "intro-status"
{:message-id "intro-status"
:content (label :t/intro-status)
:delivery-status "seen"
:from "console"
@ -167,7 +167,7 @@
(defn intro [logged-in?]
(dispatch [:received-message intro-status])
(dispatch [:received-message
{:msg-id "intro-message1"
{:message-id "intro-message1"
:content (label :t/intro-message1)
:content-type text-content-type
:outgoing false
@ -175,7 +175,7 @@
:to "me"}])
(when-not logged-in?
(dispatch [:received-message
{:msg-id "intro-message2"
{:message-id "intro-message2"
:content (label :t/intro-message2)
:content-type text-content-type
:outgoing false
@ -183,7 +183,7 @@
:to "me"}])
(dispatch [:received-message
{:msg-id "intro-message3"
{:message-id "intro-message3"
:content (command-content
:keypair
(label :t/keypair-generated))

View File

@ -31,8 +31,8 @@
:else 10))
(defn last-message-padding
[{:keys [last-msg typing]}]
(when (and last-msg (not typing))
[{:keys [last-message typing]}]
(when (and last-message (not typing))
{:paddingBottom 20}))
(def message-body-base

View File

@ -102,9 +102,9 @@
(fn [db _]
(reaction (commands/get-chat-command-content @db))))
(register-sub :get-chat-command-to-msg-id
(register-sub :get-chat-command-to-message-id
(fn [db _]
(reaction (commands/get-chat-command-to-msg-id @db))))
(reaction (commands/get-chat-command-to-message-id @db))))
(register-sub :chat-command-request
(fn [db _]

View File

@ -3,7 +3,7 @@
[status-im.db :as db]
[status-im.models.commands :refer [get-commands
get-chat-command-request
get-chat-command-to-msg-id]]
get-chat-command-to-message-id]]
[status-im.utils.utils :refer [log on-error http-get]]
[clojure.string :as s]))
@ -28,15 +28,15 @@
(defn handle-command [db command-key content]
(when-let [command-handler (get-chat-command-request db)]
(let [to-msg-id (get-chat-command-to-msg-id db)]
(command-handler to-msg-id command-key content)))
(let [to-message-id (get-chat-command-to-message-id db)]
(command-handler to-message-id command-key content)))
db)
(defn get-command-handler [db command-key content]
(when-let [command-handler (get-chat-command-request db)]
(let [to-msg-id (get-chat-command-to-msg-id db)]
(let [to-message-id (get-chat-command-to-message-id db)]
(fn []
(command-handler to-msg-id command-key content)))))
(command-handler to-message-id command-key content)))))
(defn check-suggestion [db message]
(when-let [suggestion-text (when (string? message)

View File

@ -3,6 +3,7 @@
(:require [clojure.string :as s]
[re-frame.core :refer [subscribe dispatch]]
[reagent.core :as r]
[status-im.i18n :refer [message-status-label]]
[status-im.components.react :refer [view
text
image
@ -11,14 +12,15 @@
[status-im.components.animation :as anim]
[status-im.chat.views.request-message :refer [message-content-command-request]]
[status-im.chat.styles.message :as st]
[status-im.models.commands :refer [parse-command-msg-content
[status-im.models.commands :refer [parse-command-message-content
parse-command-request]]
[status-im.resources :as res]
[status-im.utils.datetime :as time]
[status-im.constants :refer [text-content-type
content-type-status
content-type-command
content-type-command-request]]))
content-type-command-request]]
[status-im.utils.logging :as log]))
(defn message-date [timestamp platform-specific]
[view {}
@ -70,7 +72,7 @@
(defview message-content-command [content preview platform-specific]
[commands [:get-commands-and-responses]]
(let [{:keys [command content]} (parse-command-msg-content commands content)
(let [{:keys [command content]} (parse-command-message-content commands content)
{:keys [name icon type]} command]
[view st/content-command-view
[view st/command-container
@ -90,8 +92,8 @@
:font :default}
(str content)])]))
(defn set-chat-command [msg-id command]
(dispatch [:set-response-chat-command msg-id (keyword (:name command))]))
(defn set-chat-command [message-id command]
(dispatch [:set-response-chat-command message-id (keyword (:name command))]))
(defn message-view
[message content]
@ -139,9 +141,8 @@
:platform-specific platform-specific}]]
platform-specific])
(defview message-delivery-status
[{:keys [delivery-status msg-id to] :as m} platform-specific]
[status [:get-in [:message-status to msg-id]]]
(defview message-delivery-status [{:keys [delivery-status chat-id message-id] :as message} platform-specific]
[status [:get-in [:message-status chat-id message-id]]]
[view st/delivery-view
[image {:source (case (or status delivery-status)
:seen {:uri :icon_ok_small}
@ -152,12 +153,7 @@
[text {:style st/delivery-text
:platform-specific platform-specific
:font :default}
(case (or status delivery-status)
:delivered "Sent"
:seen "Seen"
:seen-by-everyone "Seen by everyone"
:failed "Failed"
"Pending")]])
(message-status-label (or status delivery-status))]])
(defview member-photo [from]
[photo-path [:photo-path from]]
@ -186,7 +182,7 @@
[message-delivery-status {:delivery-status delivery-status} platform-specific])]]]))
(defn message-body
[{:keys [outgoing delivery-status] :as message} content platform-specific]
[{:keys [outgoing] :as message} content platform-specific]
[view (st/message-body message)
content
(when outgoing
@ -226,18 +222,17 @@
children)])}))
(into [view] children)))
(defn chat-message
[{:keys [outgoing delivery-status timestamp new-day group-chat msg-id chat-id]
:as message}
platform-specific]
(let [status (subscribe [:get-in [:message-status chat-id msg-id]])]
(defn chat-message [{:keys [outgoing delivery-status timestamp new-day group-chat message-id chat-id]
:as message}
platform-specific]
(let [status (subscribe [:get-in [:message-status chat-id message-id]])]
(r/create-class
{:component-did-mount
(fn []
(when (and (not outgoing)
(not= :seen delivery-status)
(not= :seen @status))
(dispatch [:send-seen! chat-id msg-id])))
(dispatch [:send-seen! chat-id message-id])))
:reagent-render
(fn [{:keys [outgoing delivery-status timestamp new-day group-chat]
:as message}

View File

@ -15,7 +15,7 @@
(dispatch [:set-chat-input-text message]))
(defn send []
(dispatch [:send-chat-msg]))
(dispatch [:send-chat-message]))
(defn message-valid? [staged-commands message]
(or (and (pos? (count message))

View File

@ -12,8 +12,8 @@
(def request-message-icon-scale-delay 600)
(defn set-chat-command [msg-id command]
(dispatch [:set-response-chat-command msg-id (keyword (:name command))]))
(defn set-chat-command [message-id command]
(dispatch [:set-response-chat-command message-id (keyword (:name command))]))
(defn label [command]
(when command
@ -42,9 +42,9 @@
(anim/start
(button-animation val min-scale loop? answered?)))))
(defn request-button [msg-id command]
(defn request-button [message-id command]
(let [scale-anim-val (anim/create-value min-scale)
answered? (subscribe [:is-request-answered? msg-id])
answered? (subscribe [:is-request-answered? message-id])
loop? (r/atom true)
context {:to-value max-scale
:val scale-anim-val
@ -56,11 +56,11 @@
:component-will-unmount
#(reset! loop? false)
:reagent-render
(fn [msg-id command]
(fn [message-id command]
(if command
[touchable-highlight
{:on-press (when-not @answered?
#(set-chat-command msg-id command))
#(set-chat-command message-id command))
:style st/command-request-image-touchable
:accessibility-label (label command)}
[animated-view {:style (st/command-request-image-view command scale-anim-val)}
@ -68,9 +68,9 @@
:style st/command-request-image}]]]))})))
(defn message-content-command-request
[{:keys [msg-id content from incoming-group]} platform-specific]
[{:keys [message-id content from incoming-group]} platform-specific]
(let [commands-atom (subscribe [:get-responses])]
(fn [{:keys [msg-id content from incoming-group]}]
(fn [{:keys [message-id content from incoming-group]}]
(let [commands @commands-atom
{:keys [command content]} (parse-command-request commands content)]
[view st/comand-request-view
@ -84,7 +84,7 @@
:platform-specific platform-specific
:font :default}
content]]
[request-button msg-id command]
[request-button message-id command]
(when (:request-text command)
[view st/command-request-text-view
[text {:style st/style-sub-text

View File

@ -27,12 +27,13 @@
:discovery-search-tags []
:tags {}
:contacts-ids #{}
:selected-contacts #{}
:current-chat-id "console"
:chats {}
:chat {:command nil
:last-message nil}
:chats {}
:current-chat-id "console"
:contacts-ids #{}
:selected-contacts #{}
:chats-updated-signal 0
:show-actions false
:selected-participants #{}
@ -52,11 +53,11 @@
[:chats chat-id :staged-commands])
(defn chat-command-path [chat-id]
[:chats chat-id :command-input :command])
(defn chat-command-to-msg-id-path [chat-id]
[:chats chat-id :command-input :to-msg-id])
(defn chat-command-to-message-id-path [chat-id]
[:chats chat-id :command-input :to-message-id])
(defn chat-command-content-path [chat-id]
[:chats chat-id :command-input :content])
(defn chat-command-requests-path [chat-id]
[:chats chat-id :command-requests])
(defn chat-command-request-path [chat-id msg-id]
[:chats chat-id :command-requests msg-id])
(defn chat-command-request-path [chat-id message-id]
[:chats chat-id :command-requests message-id])

View File

@ -36,8 +36,8 @@
(register-handler :discovery-response-received
(u/side-effect!
(fn [db [_ from payload]]
(let [{:keys [msg-id name photo-path status hashtags]} payload
discovery {:msg-id msg-id
(let [{:keys [message-id name photo-path status hashtags]} payload
discovery {:message-id message-id
:name name
:photo-path photo-path
:status status
@ -60,8 +60,8 @@
(defn add-discovery
[{db-discoveries :discoveries
:as db} [_ {:keys [msg-id] :as discovery}]]
(let [updated-discoveries (if-let [i (first-index #(= (:msg-id %) msg-id) db-discoveries)]
:as db} [_ {:keys [message-id] :as discovery}]]
(let [updated-discoveries (if-let [i (first-index #(= (:message-id %) message-id) db-discoveries)]
(assoc db-discoveries i discovery)
(conj db-discoveries discovery))]
(-> db

View File

@ -22,15 +22,15 @@
(doseq [tag (distinct tags)]
(update-tag-counter func tag)))
(defn get-tags [msg-id]
(-> (r/get-by-field :account :discovery :msg-id msg-id)
(defn get-tags [message-id]
(-> (r/get-by-field :account :discovery :message-id message-id)
(r/single-cljs)
(:tags)
(vals)))
(defn- upsert-discovery [{:keys [msg-id tags] :as discovery}]
(defn- upsert-discovery [{:keys [message-id tags] :as discovery}]
(log/debug "Creating/updating discovery with tags: " tags)
(let [prev-tags (get-tags msg-id)]
(let [prev-tags (get-tags message-id)]
(if prev-tags
(update-tags-counter dec prev-tags))
(r/create :account :discovery discovery true)

View File

@ -31,6 +31,6 @@
:platform-specific platform-specific
:font :default}
count]]]
(for [{:keys [msg-id] :as discovery} discoveries]
^{:key (str "message-" msg-id)}
(for [{:keys [message-id] :as discovery} discoveries]
^{:key (str "message-" message-id)}
[discovery-list-item discovery platform-specific])])

View File

@ -14,6 +14,6 @@
(defview discovery-recent [{platform-specific :platform-specific}]
[discoveries [:get :discoveries]]
[view st/recent-list
(for [{:keys [msg-id] :as discovery} discoveries]
^{:key (str "message-" msg-id)}
(for [{:keys [message-id] :as discovery} discoveries]
^{:key (str "message-" message-id)}
[discovery-list-item discovery platform-specific])])

View File

@ -95,13 +95,13 @@
(doseq [participant selected-participants]
(api/group-remove-participant current-chat-id participant)))
(defn system-message [msg-id content]
(defn system-message [message-id content]
{:from "system"
:msg-id msg-id
:message-id message-id
:content content
:content-type text-content-type})
(defn removed-participant-msg [chat-id identity]
(defn removed-participant-message [chat-id identity]
(let [contact-name (:name (contacts/contact-by-identity identity))]
(->> (str "You've removed " (or contact-name identity))
(system-message (random/id))
@ -110,7 +110,7 @@
(defn create-removing-messages!
[{:keys [current-chat-id selected-participants]} _]
(doseq [participant selected-participants]
(removed-participant-msg current-chat-id participant)))
(removed-participant-message current-chat-id participant)))
(defn deselect-members [db _]
(assoc db :selected-participants #{}))

View File

@ -74,6 +74,7 @@
(dispatch [:initialize-protocol address])
(dispatch [:initialize-account-db])
(dispatch [:initialize-chats])
(dispatch [:initialize-pending-messages])
(dispatch [:load-contacts])
(dispatch [:init-chat])
(dispatch [:init-discoveries])

View File

@ -16,8 +16,14 @@
(.t i18n (name path) (clj->js options))
(name path))))
(defn label-pluralize [count path & options]
(if (exists? i18n.t)
(.p i18n count (name path) (clj->js options))
(name path)))
(name path)))
(defn message-status-label [status]
(->> status
(name)
(str "t/status-")
(keyword)
(label)))

View File

@ -32,7 +32,7 @@
(save-message chat-id
{:from "Status"
:to nil
:msg-id (random/id)
:message-id (random/id)
:content (str "The brash businessmans braggadocio "
"and public exchange with candidates "
"in the US presidential election")
@ -40,8 +40,8 @@
:outgoing false}))
(defn create-chat
([{:keys [last-msg-id] :as chat}]
(let [chat (assoc chat :last-msg-id (or last-msg-id ""))]
([{:keys [last-message-id] :as chat}]
(let [chat (assoc chat :last-message-id (or last-message-id ""))]
(r/write :account #(r/create :account :chat chat))))
([db chat-id identities group-chat? chat-name]
(when-not (chat-exists? chat-id)
@ -59,7 +59,7 @@
:group-chat group-chat?
:timestamp (timestamp)
:contacts contacts
:last-msg-id ""}))))
:last-message-id ""}))))
(add-status-message chat-id)))))
(defn chat-contacts [chat-id]

View File

@ -27,16 +27,16 @@
(defn set-command-input
([db type command-key]
(set-command-input db type nil command-key))
([{:keys [current-chat-id] :as db} type msg-id command-key]
([{:keys [current-chat-id] :as db} type message-id command-key]
(update-in db [:chats current-chat-id :command-input] merge
{:content nil
:command (get-response-or-command type db command-key)
:parameter-idx 0
:to-msg-id msg-id})))
:to-message-id message-id})))
(defn get-chat-command-to-msg-id
(defn get-chat-command-to-message-id
[{:keys [current-chat-id] :as db}]
(get-in db (db/chat-command-to-msg-id-path current-chat-id)))
(get-in db (db/chat-command-to-message-id-path current-chat-id)))
(defn compare-commands
[{created-at-1 :created-at} {created-at-2 :created-at}]
@ -58,14 +58,14 @@
(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))))
(get-chat-command-to-message-id db))))
(defn set-chat-command-request
[{:keys [current-chat-id] :as db} msg-id handler]
[{:keys [current-chat-id] :as db} message-id handler]
(update-in db (db/chat-command-requests-path current-chat-id)
#(assoc % msg-id handler)))
#(assoc % message-id handler)))
(defn parse-command-msg-content [commands content]
(defn parse-command-message-content [commands content]
(update content :command #((keyword %) commands)))
(defn parse-command-request [commands content]

View File

@ -26,10 +26,10 @@
(defn save-message
;; todo remove chat-id parameter
[chat-id {:keys [delivery-status msg-id content]
:or {delivery-status :pending}
[chat-id {:keys [delivery-status message-id content]
:or {delivery-status :sending}
:as message}]
(when-not (r/exists? :account :message :msg-id msg-id)
(when-not (r/exists? :account :message :message-id message-id)
(r/write :account
(fn []
(let [content' (if (string? content)
@ -66,15 +66,14 @@
(generate-hiccup (read-string preview)))))
message))))))
(defn update-message! [{:keys [msg-id] :as msg}]
(log/debug "update-message!" msg)
(defn update-message! [{:keys [message-id] :as message}]
(r/write :account
(fn []
(when (r/exists? :account :message :msg-id msg-id)
(r/create :account :message msg true)))))
(when (r/exists? :account :message :message-id message-id)
(r/create :account :message message true)))))
(defn get-message [id]
(r/get-one-by-field :account :message :msg-id id))
(r/get-one-by-field :account :message :message-id id))
(defn get-last-message [chat-id]
(-> (r/get-by-field :account :message :chat-id chat-id)

View File

@ -0,0 +1,37 @@
(ns status-im.models.pending-messages
(:require [status-im.persistence.realm.core :as r]
[re-frame.core :refer [dispatch]]
[cljs.reader :refer [read-string]]
[status-im.utils.random :refer [timestamp]]
[clojure.string :refer [join split]]
[clojure.walk :refer [stringify-keys keywordize-keys]]
[status-im.constants :as c]
[status-im.utils.types :refer [clj->json json->clj]]
[status-im.commands.utils :refer [generate-hiccup]]))
(defn get-pending-messages []
(let [collection (-> (r/get-by-fields :account :pending-message :or [[:status :sending]
[:status :sent]])
(r/sorted :timestamp :desc)
(r/collection->map))]
(->> collection
(map (fn [{:keys [message-id] :as message}]
(let [message (-> message
(update :message json->clj)
(update :identities json->clj))]
[message-id message])))
(into {}))))
(defn upsert-pending-message!
[message]
(r/write :account
(fn []
(let [message (-> message
(update :message clj->json)
(update :identities clj->json))]
(r/create :account :pending-message message true)))))
(defn remove-pending-message! [message-id]
(r/write :account
(fn []
(r/delete :account (r/get-by-field :account :pending-message :message-id message-id)))))

View File

@ -100,9 +100,12 @@
[schema schema-name obj]
(write schema (fn [] (create schema schema-name obj true))))
(defn and-q [queries]
(defn and-query [queries]
(str/join " and " queries))
(defn or-query [queries]
(str/join " or " queries))
(defmulti to-query (fn [schema schema-name operator field value]
operator))
@ -122,11 +125,14 @@
(let [q (to-query schema schema-name :eq field value)]
(.filtered (.objects (realm schema) (name schema-name)) q)))
(defn get-by-fields [schema schema-name fields]
(defn get-by-fields [schema schema-name op fields]
(let [queries (map (fn [[k v]]
(to-query schema schema-name :eq k v))
fields)]
(.filtered (.objects (realm schema) (name schema-name)) (and-q queries))))
(.filtered (.objects (realm schema) (name schema-name))
(case op
:and (and-query queries)
:or (or-query queries)))))
(defn get-all [schema schema-name]
(.objects (realm schema) (to-string schema-name)))

View File

@ -37,8 +37,8 @@
:properties {:name "string"
:count {:type "int" :optional true :default 0}}}
{:name :discovery
:primaryKey :msg-id
:properties {:msg-id "string"
:primaryKey :message-id
:properties {:message-id "string"
:name {:type "string" :optional true}
:status "string"
:whisper-id "string"
@ -52,11 +52,13 @@
:properties {:key "string"
:value "string"}}
{:name :message
:primaryKey :msg-id
:properties {:msg-id "string"
:primaryKey :message-id
:properties {:message-id "string"
:from "string"
:to {:type "string"
:optional true}
:group-id {:type "string"
:optional true}
:content "string" ;; TODO make it ArrayBuffer
:content-type "string"
:timestamp "int"
@ -65,31 +67,49 @@
:outgoing "bool"
:delivery-status {:type "string"
:optional true}
:retry-count {:type :int
:default 0}
:same-author "bool"
:same-direction "bool"
:preview {:type :string
:optional true}
:message-type {:type :string
:optional true}}}
{:name :pending-message
:primaryKey :message-id
:properties {:message-id "string"
:chat-id {:type "string"
:optional true}
:message "string"
:timestamp "int"
:status "string"
:retry-count "int"
:send-once "bool"
:identities {:type "string"
:optional true}
:internal? {:type "bool"
:optional true}}}
{:name :chat-contact
:properties {:identity "string"
:is-in-chat {:type "bool"
:default true}}}
{:name :chat
:primaryKey :chat-id
:properties {:chat-id "string"
:name "string"
:color {:type "string"
:default default-chat-color}
:group-chat {:type "bool"
:indexed true}
:is-active "bool"
:timestamp "int"
:contacts {:type "list"
:objectType "chat-contact"}
:dapp-url {:type :string
:optional true}
:dapp-hash {:type :int
:optional true}
:last-msg-id "string"}}
:properties {:chat-id "string"
:name "string"
:color {:type "string"
:default default-chat-color}
:group-chat {:type "bool"
:indexed true}
:is-active "bool"
:timestamp "int"
:contacts {:type "list"
:objectType "chat-contact"}
:dapp-url {:type :string
:optional true}
:dapp-hash {:type :int
:optional true}
:last-message-id "string"}}
{:name :command
:primaryKey :chat-id
:properties {:chat-id "string"

View File

@ -7,13 +7,14 @@
[re-frame.core :refer [dispatch after]]
[status-im.utils.handlers :refer [register-handler]]
[status-im.models.contacts :as contacts]
[status-im.models.messages :as messages]
[status-im.models.pending-messages :as pending-messages]
[status-im.models.chats :as chats]
[status-im.protocol.api :refer [init-protocol]]
[status-im.protocol.protocol-handler :refer [make-handler]]
[status-im.models.protocol :refer [update-identity
set-initialized]]
[status-im.constants :refer [text-content-type]]
[status-im.models.messages :as messages]
[status-im.models.chats :as chats]
[status-im.i18n :refer [label]]))
(register-handler :initialize-protocol
@ -28,107 +29,130 @@
(update-identity identity)
(set-initialized true))))
(defn system-message [msg-id content]
(defn system-message [message-id content]
{:from "system"
:msg-id msg-id
:message-id message-id
:content content
:content-type text-content-type})
(defn joined-chat-msg [chat-id from msg-id]
(defn joined-chat-message [chat-id from message-id]
(let [contact-name (:name (contacts/contact-by-identity from))]
(messages/save-message chat-id {:from "system"
:msg-id (str msg-id "_" from)
:message-id (str message-id "_" from)
:content (str (or contact-name from) " " (label :t/received-invitation))
:content-type text-content-type})))
(defn participant-invited-to-group-msg [chat-id identity from msg-id]
(defn participant-invited-to-group-message [chat-id identity from message-id]
(let [inviter-name (:name (contacts/contact-by-identity from))
invitee-name (if (= identity (api/my-identity))
(label :t/You)
(:name (contacts/contact-by-identity identity)))]
(messages/save-message chat-id {:from "system"
:msg-id msg-id
:message-id message-id
:content (str (or inviter-name from) " " (label :t/invited) " " (or invitee-name identity))
:content-type text-content-type})))
(defn participant-removed-from-group-msg [chat-id identity from msg-id]
(defn participant-removed-from-group-message [chat-id identity from message-id]
(let [remover-name (:name (contacts/contact-by-identity from))
removed-name (:name (contacts/contact-by-identity identity))]
(->> (str (or remover-name from) " " (label :t/removed) " " (or removed-name identity))
(system-message msg-id)
(system-message message-id)
(messages/save-message chat-id))))
(defn you-removed-from-group-msg [chat-id from msg-id]
(defn you-removed-from-group-message [chat-id from message-id]
(let [remover-name (:name (contacts/contact-by-identity from))]
(->> (str (or remover-name from) " " (label :t/removed-from-chat))
(system-message msg-id)
(system-message message-id)
(messages/save-message chat-id))))
(defn participant-left-group-msg [chat-id from msg-id]
(defn participant-left-group-message [chat-id from message-id]
(let [left-name (:name (contacts/contact-by-identity from))]
(->> (str (or left-name from) " " (label :t/left))
(system-message msg-id)
(system-message message-id)
(messages/save-message chat-id))))
(register-handler :group-chat-invite-acked
(u/side-effect!
(fn [_ [action from group-id ack-msg-id]]
(log/debug action from group-id ack-msg-id)
#_(joined-chat-msg group-id from ack-msg-id))))
(fn [_ [action from group-id ack-message-id]]
(log/debug action from group-id ack-message-id)
#_(joined-chat-message group-id from ack-message-id))))
(register-handler :participant-removed-from-group
(u/side-effect!
(fn [_ [action from group-id identity msg-id]]
(log/debug action msg-id from group-id identity)
(fn [_ [action from group-id identity message-id]]
(log/debug action message-id from group-id identity)
(chats/chat-remove-participants group-id [identity])
(participant-removed-from-group-msg group-id identity from msg-id))))
(participant-removed-from-group-message group-id identity from message-id))))
(register-handler :you-removed-from-group
(u/side-effect!
(fn [_ [action from group-id msg-id]]
(log/debug action msg-id from group-id)
(you-removed-from-group-msg group-id from msg-id)
(fn [_ [action from group-id message-id]]
(log/debug action message-id from group-id)
(you-removed-from-group-message group-id from message-id)
(chats/set-chat-active group-id false))))
(register-handler :participant-left-group
(u/side-effect!
(fn [_ [action from group-id msg-id]]
(log/debug action msg-id from group-id)
(fn [_ [action from group-id message-id]]
(log/debug action message-id from group-id)
(when-not (= (api/my-identity) from)
(participant-left-group-msg group-id from msg-id)))))
(participant-left-group-message group-id from message-id)))))
(register-handler :participant-invited-to-group
(u/side-effect!
(fn [_ [action from group-id identity msg-id]]
(log/debug action msg-id from group-id identity)
(participant-invited-to-group-msg group-id identity from msg-id))))
(fn [_ [action from group-id identity message-id]]
(log/debug action message-id from group-id identity)
(participant-invited-to-group-message group-id identity from message-id))))
(defn update-message! [status]
(fn [_ [_ _ msg-id]]
(messages/update-message! {:msg-id msg-id
(fn [_ [_ _ message-id]]
(messages/update-message! {:message-id message-id
:delivery-status status})))
(defn update-message-status [status]
(fn [db [_ from msg-id]]
(let [current-status (get-in db [:message-status from msg-id])]
(fn [db [_ chat-id message-id]]
(let [current-status (get-in db [:message-status chat-id message-id])]
(if-not (= :seen current-status)
(assoc-in db [:message-status from msg-id] status)
(assoc-in db [:message-status chat-id message-id] status)
db))))
(register-handler :acked-msg
(register-handler :message-delivered
(after (update-message! :delivered))
(update-message-status :delivered))
(register-handler :msg-delivery-failed
(register-handler :message-failed
(after (update-message! :failed))
(update-message-status :failed))
(register-handler :msg-seen
(register-handler :message-sent
(after (update-message! :sent))
(update-message-status :sent))
(register-handler :message-seen
[(after (update-message! :seen))
(after (fn [_ [_ chat-id]]
(dispatch [:remove-unviewed-messages chat-id])))]
(update-message-status :seen))
(register-handler :pending-message-upsert
(after
(fn [_ [_ {:keys [message-id status] :as pending-message}]]
(pending-messages/upsert-pending-message! pending-message)
(messages/update-message! {:message-id message-id
:delivery-status status})))
(fn [db [_ {:keys [message-id chat-id status] :as pending-message}]]
(if chat-id
(let [current-status (get-in db [:message-status chat-id message-id])]
(if-not (= :seen current-status)
(assoc-in db [:message-status chat-id message-id] status)
db))
db)))
(register-handler :pending-message-remove
(u/side-effect!
(fn [_ [_ message-id]]
(pending-messages/remove-pending-message! message-id))))
(register-handler :send-transaction!
(u/side-effect!
(fn [_ [_ amount message]]

View File

@ -17,35 +17,40 @@
(case event-type
:initialized (let [{:keys [identity]} event]
(dispatch [:protocol-initialized identity]))
:new-msg (let [{:keys [from to payload]} event]
(dispatch [:received-message (assoc payload
:chat-id from
:from from
:to to)]))
:msg-acked (let [{:keys [msg-id from]} event]
(dispatch [:acked-msg from msg-id]))
:msg-seen (let [{:keys [msg-id from]} event]
(dispatch [:msg-seen from msg-id]))
:delivery-failed (let [{:keys [msg-id from]} event]
(dispatch [:msg-delivery-failed from msg-id]))
:message-received (let [{:keys [from to payload]} event]
(dispatch [:received-message (assoc payload :chat-id from
:from from
:to to)]))
:message-delivered (let [{:keys [message-id from]} event]
(dispatch [:message-delivered from message-id]))
:message-seen (let [{:keys [message-id from]} event]
(dispatch [:message-seen from message-id]))
:message-failed (let [{:keys [message-id chat-id] :as event} event]
(dispatch [:message-failed chat-id message-id]))
:message-sent (let [{:keys [message-id chat-id]} event]
(dispatch [:message-sent chat-id message-id]))
:pending-message-upsert (let [{message :message} event]
(dispatch [:pending-message-upsert message]))
:pending-message-remove (let [{:keys [message-id]} event]
(dispatch [:pending-message-remove message-id]))
:new-group-chat (let [{:keys [from group-id identities group-name]} event]
(dispatch [:group-chat-invite-received from group-id identities group-name]))
:new-group-msg (let [{from :from
group-id :group-id
payload :payload} event]
(dispatch [:received-message (assoc payload
:chat-id group-id
:from from)]))
:group-chat-invite-acked (let [{:keys [from group-id ack-msg-id]} event]
(dispatch [:group-chat-invite-acked from group-id ack-msg-id]))
:group-new-participant (let [{:keys [group-id identity from msg-id]} event]
(dispatch [:participant-invited-to-group from group-id identity msg-id]))
:group-removed-participant (let [{:keys [group-id identity from msg-id]} event]
(dispatch [:participant-removed-from-group from group-id identity msg-id]))
:removed-from-group (let [{:keys [group-id from msg-id]} event]
(dispatch [:you-removed-from-group from group-id msg-id]))
:participant-left-group (let [{:keys [group-id from msg-id]} event]
(dispatch [:participant-left-group from group-id msg-id]))
:new-group-message (let [{from :from
group-id :group-id
payload :payload} event]
(dispatch [:received-message (assoc payload
:chat-id group-id
:from from)]))
:group-chat-invite-acked (let [{:keys [from group-id ack-message-id]} event]
(dispatch [:group-chat-invite-acked from group-id ack-message-id]))
:group-new-participant (let [{:keys [group-id identity from message-id]} event]
(dispatch [:participant-invited-to-group from group-id identity message-id]))
:group-removed-participant (let [{:keys [group-id identity from message-id]} event]
(dispatch [:participant-removed-from-group from group-id identity message-id]))
:removed-from-group (let [{:keys [group-id from message-id]} event]
(dispatch [:you-removed-from-group from group-id message-id]))
:participant-left-group (let [{:keys [group-id from message-id]} event]
(dispatch [:participant-left-group from group-id message-id]))
:discover-response (let [{:keys [from payload]} event]
(dispatch [:discovery-response-received from payload]))
:contact-update (let [{:keys [from payload]} event]

View File

@ -23,6 +23,14 @@
:active-online "online"
:active-unknown "unknown"
;messages
:status-sending "Sending"
:status-sent "Sent"
:status-seen-by-everyone "Seen by everyone"
:status-seen "Seen"
:status-delivered "Delivered"
:status-failed "Failed"
;datetime
:datetime-second {:one "second"
:other "seconds"}

View File

@ -3,7 +3,7 @@
(:require-macros [cljs.core.async.macros :refer [go]]))
(defn handle-channel-events [chan handler]
(go (loop [[msg args] (<! chan)]
(when msg
(handler msg args)
(go (loop [[message args] (<! chan)]
(when message
(handler message args)
(recur (<! chan))))))