Correct ordering of messages (#224)

This commit is contained in:
Alexander Pantyuhov 2016-10-14 17:38:02 +03:00
parent 2c72274025
commit ff32691fea
12 changed files with 200 additions and 64 deletions

View File

@ -35,7 +35,8 @@
status-im.chat.handlers.receive-message
[cljs.core.async :as a]
status-im.chat.handlers.webview-bridge
status-im.chat.handlers.wallet-chat))
status-im.chat.handlers.wallet-chat
[taoensso.timbre :as log]))
(register-handler :set-chat-ui-props
(fn [db [_ ui-element value]]
@ -289,20 +290,22 @@
load-messages!
init-chat))))
(defn prepare-chat
[{:keys [contacts] :as db} [_ contact-id options]]
(let [name (get-in contacts [contact-id :name])
chat (merge {:chat-id contact-id
:name (or name (generate-gfy))
:color default-chat-color
:group-chat false
:is-active true
:timestamp (.getTime (js/Date.))
:contacts [{:identity contact-id}]
:dapp-url nil
:dapp-hash nil}
options)]
(assoc db :new-chat chat)))
(defn prepare-chat [{:keys [contacts]} chat-id chat]
(let [name (get-in contacts [chat-id :name])]
(merge {:chat-id chat-id
:name (or name (generate-gfy))
:color default-chat-color
:group-chat false
:is-active true
:timestamp (.getTime (js/Date.))
:contacts [{:identity chat-id}]
:dapp-url nil
:dapp-hash nil}
chat)))
(defn add-new-chat
[db [_ chat-id chat]]
(assoc db :new-chat (prepare-chat db chat-id chat)))
(defn add-chat [{:keys [new-chat] :as db} [_ chat-id]]
(-> db
@ -318,7 +321,7 @@
(dispatch [(or navigation-type :navigate-to) :chat chat-id]))
(register-handler ::start-chat!
(-> prepare-chat
(-> add-new-chat
((enrich add-chat))
((after save-new-chat!))
((after open-chat!))))
@ -331,10 +334,30 @@
(dispatch [::start-chat! contact-id options navigation-type])))))
(register-handler :add-chat
(-> prepare-chat
(-> add-new-chat
((enrich add-chat))
((after save-new-chat!))))
(defn update-chat!
[_ [_ chat]]
(chats/save chat))
(register-handler :update-chat!
(-> (fn [db [_ {:keys [chat-id] :as chat}]]
(if (get-in db [:chats chat-id])
(update-in db [:chats chat-id] merge chat)
db))
((after update-chat!))))
(register-handler :upsert-chat!
(fn [db [_ {:keys [chat-id clock-value] :as opts}]]
(let [chat (if (chats/exists? chat-id)
(let [{old-clock-value :clock-value :as chat} (chats/get-by-id chat-id)]
(assoc chat :clock-value (+ (max old-clock-value clock-value)) 1))
(prepare-chat db chat-id opts))]
(chats/save chat)
(update-in db [:chats chat-id] merge chat))))
(register-handler :switch-command-suggestions!
(u/side-effect!
(fn [db]
@ -453,17 +476,6 @@
(fn [db [_ chat-id mode]]
(assoc-in db [:kb-mode chat-id] mode)))
(defn update-chat!
[_ [_ chat]]
(chats/save chat))
(register-handler :update-chat!
(-> (fn [db [_ {:keys [chat-id] :as chat}]]
(if (get-in db [:chats chat-id])
(update-in db [:chats chat-id] merge chat)
db))
((after update-chat!))))
(register-handler :check-autorun
(u/side-effect!
(fn [{:keys [current-chat-id] :as db}]
@ -474,3 +486,10 @@
(a/<! (a/timeout 100))
(dispatch [:set-chat-command (keyword autorun)])
(dispatch [:animate-command-suggestions])))))))
(register-handler :inc-clock
(u/side-effect!
(fn [_ [_ chat-id]]
(let [chat (-> (chats/get-by-id chat-id)
(update :clock-value inc))]
(dispatch [:update-chat! chat])))))

View File

@ -7,7 +7,8 @@
[status-im.utils.random :as random]
[status-im.constants :refer [content-type-command-request]]
[cljs.reader :refer [read-string]]
[status-im.data-store.chats :as chats]))
[status-im.data-store.chats :as chats]
[taoensso.timbre :as log]))
(defn check-preview [{:keys [content] :as message}]
(if-let [preview (:preview content)]
@ -25,12 +26,17 @@
(:public-key (accounts current-account-id)))
(defn receive-message
[db [_ {:keys [from group-id chat-id message-id timestamp] :as message}]]
(let [same-message (messages/get-by-id message-id)
[db [_ {:keys [from group-id chat-id message-id timestamp clock-value] :as message :or {clock-value 0}}]]
(let [same-message (messages/get-by-id message-id)
current-identity (get-current-identity db)
chat-id' (or group-id chat-id from)
exists? (chats/exists? chat-id')
active? (chats/is-active? chat-id')]
chat-id' (or group-id chat-id from)
exists? (chats/exists? chat-id')
active? (chats/is-active? chat-id')
clock-value (if (= clock-value 0)
(-> (chats/get-by-id chat-id')
(get :clock-value)
(inc))
clock-value)]
(when (and (not same-message)
(not= from current-identity)
(or (not exists?) active?))
@ -40,10 +46,12 @@
(cu/check-author-direction previous-message)
(check-preview))
:chat-id chat-id'
:timestamp (or timestamp (random/timestamp)))]
:timestamp (or timestamp (random/timestamp))
:clock-value clock-value)]
(store-message message')
(when-not exists?
(dispatch [:add-chat chat-id' (when group-chat? {:group-chat true})]))
(dispatch [:upsert-chat! {:chat-id chat-id'
:group-chat group-chat?
:clock-value clock-value}])
(dispatch [::add-message message'])
(when (= (:content-type message') content-type-command-request)
(dispatch [:add-request chat-id' message']))
@ -53,9 +61,9 @@
(u/side-effect!
(fn [_ [_ {:keys [from to payload]}]]
(dispatch [:received-message (merge payload
{:from from
:to to
:chat-id from})]))))
{:from from
:to to
:chat-id from})]))))
(register-handler :received-message
(u/side-effect! receive-message))

View File

@ -13,10 +13,11 @@
default-number-of-messages]]
[status-im.utils.datetime :as datetime]
[status-im.protocol.core :as protocol]
[taoensso.timbre :refer-macros [debug]]))
[taoensso.timbre :refer-macros [debug]]
[taoensso.timbre :as log]))
(defn prepare-command
[identity chat-id
[identity chat-id clock-value
{:keys [id preview preview-string params command to-message handler-data]}]
(let [content {:command (command :name)
:params params}]
@ -32,7 +33,8 @@
:rendered-preview preview
:to-message to-message
:type (:type command)
:has-handler (:has-handler command)}))
:has-handler (:has-handler command)
:clock-value (inc clock-value)}))
(register-handler :send-chat-message
(u/side-effect!
@ -76,8 +78,9 @@
(u/side-effect!
(fn [{:keys [current-public-key] :as db}
[_ {:keys [chat-id staged-command handler-data] :as params}]]
(let [command' (->> (assoc staged-command :handler-data handler-data)
(prepare-command current-public-key chat-id)
(let [{:keys [clock-value]} (get-in db [:chats chat-id])
command' (->> (assoc staged-command :handler-data handler-data)
(prepare-command current-public-key chat-id clock-value)
(cu/check-author-direction db chat-id))]
(dispatch [:clear-command chat-id (:id staged-command)])
(dispatch [::send-command! (assoc params :command command')])))))
@ -144,7 +147,7 @@
(register-handler ::prepare-message
(u/side-effect!
(fn [db [_ {:keys [chat-id identity message] :as params}]]
(let [{:keys [group-chat]} (get-in db [:chats chat-id])
(let [{:keys [group-chat clock-value]} (get-in db [:chats chat-id])
message' (cu/check-author-direction
db chat-id
{:message-id (random/id)
@ -153,7 +156,8 @@
:from identity
:content-type text-content-type
:outgoing true
:timestamp (time/now-ms)})
:timestamp (time/now-ms)
:clock-value (inc clock-value)})
message'' (if group-chat
(assoc message' :group-id chat-id :message-type :group-user-message)
(assoc message' :to chat-id :message-type :user-message))
@ -179,7 +183,7 @@
chat-id :chat-id}]]
(when (and message (cu/not-console? chat-id))
(let [message' (select-keys message [:from :message-id])
payload (select-keys message [:timestamp :content :content-type])
payload (select-keys message [:timestamp :content :content-type :clock-value])
options {:web3 web3
:message (assoc message' :payload payload)}]
(if (= message-type :group-user-message)
@ -189,26 +193,29 @@
:keypair {:public public-key
:private private-key})))
(protocol/send-message! (assoc-in options
[:message :to] (:to message)))))))))
[:message :to] (:to message))))
(dispatch [:inc-clock chat-id]))))))
(register-handler ::send-command-protocol!
(u/side-effect!
(fn [{:keys [web3 current-public-key chats] :as db} [_ {:keys [chat-id command]}]]
(let [{:keys [content message-id]} command]
(let [{:keys [content message-id clock-value]} command]
(when (cu/not-console? chat-id)
(let [{:keys [public-key private-key]} (chats chat-id)
{:keys [group-chat]} (get-in db [:chats chat-id])
payload {:content content
:content-type content-type-command
:timestamp (datetime/now-ms)}
:timestamp (datetime/now-ms)
:clock-value clock-value}
options {:web3 web3
:message {:from current-public-key
:message-id message-id
:payload payload}}]
:message {:from current-public-key
:message-id message-id
:payload payload}}]
(if group-chat
(protocol/send-group-message! (assoc options
:group-id chat-id
:keypair {:public public-key
:private private-key}))
(protocol/send-message! (assoc-in options
[:message :to] chat-id)))))))))
[:message :to] chat-id)))
(dispatch [:inc-clock chat-id])))))))

View File

@ -31,7 +31,8 @@
[status-im.components.sync-state.offline :refer [offline-view]]
[status-im.constants :refer [content-type-status]]
[reagent.core :as r]
[cljs-time.core :as t]))
[cljs-time.core :as t]
[taoensso.timbre :as log]))
(defn contacts-by-identity [contacts]
(->> contacts
@ -120,6 +121,7 @@
(concat all-messages [status-message])
all-messages)
messages (->> all-messages
(sort-by :clock-value >)
(map #(assoc % :datemark (time/day-relative (:timestamp %))))
(group-by :datemark)
(map (fn [[k v]] [v {:type :datemark :value k}]))

View File

@ -13,7 +13,8 @@
[status-im.chat.styles.plain-message :as st-message]
[status-im.chat.styles.response :as st-response]
[reagent.core :as r]
[clojure.string :as str]))
[clojure.string :as str]
[taoensso.timbre :as log]))
(defn send-button [{:keys [on-press accessibility-label]}]
[touchable-highlight {:on-press on-press
@ -57,13 +58,13 @@
:default-value (or input-message "")}
input-options)])
(defview command-input [input-options command]
(defview command-input [input-options {:keys [fullscreen] :as command}]
[input-command [:get-chat-command-content]
icon-width [:command-icon-width]
disable? [:get :disable-input]]
[text-input (merge
(command-input-options command icon-width disable?)
{:auto-focus false
{:auto-focus (not fullscreen)
:blur-on-submit false
:accessibility-label :input
:on-focus #(dispatch [:set :focused true])

View File

@ -70,7 +70,7 @@
(defn chat-list-item-inner-view [{:keys [chat-id name color last-message
online group-chat contacts] :as chat}]
(let [last-message (or (first (:messages chat))
(let [last-message (or (first (sort-by :clock-value > (:messages chat)))
last-message)
name (or name (generate-gfy))]
[view st/chat-container

View File

@ -30,7 +30,7 @@
(defn get-last-message
[chat-id]
(-> (realm/get-by-field @realm/account-realm :message :chat-id chat-id)
(realm/sorted :timestamp :desc)
(realm/sorted :clock-value :desc)
(realm/single-cljs)))
(defn get-unviewed

View File

@ -1,6 +1,7 @@
(ns status-im.data-store.realm.schemas.account.core
(:require [status-im.data-store.realm.schemas.account.v1.core :as v1]
[status-im.data-store.realm.schemas.account.v2.core :as v2]))
[status-im.data-store.realm.schemas.account.v2.core :as v2]
[status-im.data-store.realm.schemas.account.v3.core :as v3]))
; put schemas ordered by version
(def schemas [{:schema v1/schema
@ -8,4 +9,7 @@
:migration v1/migration}
{:schema v2/schema
:schemaVersion 2
:migration v2/migration}])
:migration v2/migration}
{:schema v3/schema
:schemaVersion 3
:migration v3/migration}])

View File

@ -0,0 +1,32 @@
(ns status-im.data-store.realm.schemas.account.v3.chat
(:require [taoensso.timbre :as log]
[status-im.components.styles :refer [default-chat-color]]))
(def schema {: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}
:removed-at {:type :int
:optional true}
:last-message-id "string"
:public-key {:type :string
:optional true}
:private-key {:type :string
:optional true}
:clock-value {:type :int
:default 0}}})
(defn migration [old-realm new-realm]
(log/debug "migrating chat schema"))

View File

@ -0,0 +1,29 @@
(ns status-im.data-store.realm.schemas.account.v3.core
(:require [taoensso.timbre :as log]
[status-im.data-store.realm.schemas.account.v3.chat :as chat]
[status-im.data-store.realm.schemas.account.v3.message :as message]
[status-im.data-store.realm.schemas.account.v2.contact :as contact]
[status-im.data-store.realm.schemas.account.v1.chat-contact :as chat-contact]
[status-im.data-store.realm.schemas.account.v1.command :as command]
[status-im.data-store.realm.schemas.account.v1.discovery :as discovery]
[status-im.data-store.realm.schemas.account.v1.kv-store :as kv-store]
[status-im.data-store.realm.schemas.account.v1.pending-message :as pending-message]
[status-im.data-store.realm.schemas.account.v1.request :as request]
[status-im.data-store.realm.schemas.account.v1.tag :as tag]
[status-im.data-store.realm.schemas.account.v1.user-status :as user-status]))
(def schema [chat/schema
chat-contact/schema
command/schema
contact/schema
discovery/schema
kv-store/schema
message/schema
pending-message/schema
request/schema
tag/schema
user-status/schema])
(defn migration [old-realm new-realm]
(log/debug "migrating v3 account database: " old-realm new-realm)
(contact/migration old-realm new-realm))

View File

@ -0,0 +1,34 @@
(ns status-im.data-store.realm.schemas.account.v3.message
(:require [taoensso.timbre :as log]))
(def schema {:name :message
: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"
:chat-id {:type "string"
:indexed true}
:outgoing "bool"
:retry-count {:type :int
:default 0}
:same-author "bool"
:same-direction "bool"
:preview {:type :string
:optional true}
:message-type {:type :string
:optional true}
:message-status {:type :string
:optional true}
:user-statuses {:type :list
:objectType "user-status"}
:clock-value {:type :int
:default 0}}})
(defn migration [old-realm new-realm]
(log/debug "migrating message schema"))

View File

@ -23,7 +23,7 @@
content)
payload' (-> message
(select-keys [:message-id :requires-ack? :type])
(select-keys [:message-id :requires-ack? :type :clock-value])
(merge payload)
(assoc :content content')
prn-str