Refactored statuses

This commit is contained in:
janherich 2017-12-23 21:27:04 +01:00
parent e66b5c435b
commit 52ddccca96
No known key found for this signature in database
GPG Key ID: C23B473AFBE94D13
19 changed files with 287 additions and 320 deletions

View File

@ -4,7 +4,6 @@
[status-im.utils.handlers :as handlers]
[status-im.utils.gfycat.core :as gfycat]
[status-im.chat.models :as model]
[status-im.chat.models.unviewed-messages :as unviewed-messages-model]
[status-im.chat.console :as console-chat]
[status-im.chat.constants :as chat-const]
[status-im.data-store.messages :as msg-store]
@ -31,7 +30,8 @@
(re-frame/reg-cofx
:stored-unviewed-messages
(fn [cofx _]
(assoc cofx :stored-unviewed-messages (messages-store/get-unviewed))))
(assoc cofx :stored-unviewed-messages
(msg-store/get-unviewed (-> cofx :db :current-public-key)))))
(re-frame/reg-cofx
:get-stored-message
@ -170,16 +170,16 @@
(if account-creation?
{:db db
:dispatch load-default-contacts-event}
(let [chat->unviewed-messages (unviewed-messages-model/index-unviewed-messages stored-unviewed-messages)
chat->message-id->request (reduce (fn [acc {:keys [chat-id message-id] :as request}]
(let [chat->message-id->request (reduce (fn [acc {:keys [chat-id message-id] :as request}]
(assoc-in acc [chat-id message-id] request))
{}
stored-unanswered-requests)
chats (reduce (fn [acc {:keys [chat-id] :as chat}]
(assoc acc chat-id (assoc chat
:unviewed-messages (get chat->unviewed-messages chat-id)
:requests (get chat->message-id->request chat-id)
:messages (index-messages (get-stored-messages chat-id)))))
(assoc acc chat-id
(assoc chat
:unviewed-messages (get stored-unviewed-messages chat-id)
:requests (get chat->message-id->request chat-id)
:messages (index-messages (get-stored-messages chat-id)))))
{}
all-stored-chats)]
(-> db
@ -190,23 +190,22 @@
(handlers/register-handler-fx
:send-seen!
[re-frame/trim-v]
(fn [{:keys [db]} [{:keys [message-id chat-id from]}]]
(let [{:keys [web3 current-public-key chats]
:contacts/keys [contacts]} db
{:keys [group-chat public?]} (get chats chat-id)]
(fn [{:keys [db]} [{:keys [chat-id from me message-id]}]]
(let [{:keys [web3 chats] :contacts/keys [contacts]} db
{:keys [group-chat public? messages]} (get chats chat-id)
statuses (assoc (get-in messages [message-id :user-statuses]) me :seen)]
(cond-> {:db (-> db
(unviewed-messages-model/remove-unviewed-message chat-id message-id)
(assoc-in [:chats chat-id :messages message-id :message-status] :seen))
:update-message {:message-id message-id
:message-status :seen}}
(and (not (get-in contacts [chat-id] :dapp?))
(not public?))
(assoc :protocol-send-seen
{:web3 web3
:message (cond-> {:from current-public-key
:to from
:message-id message-id}
group-chat (assoc :group-id chat-id))})))))
(update-in [:chats chat-id :unviewed-messages] disj message-id)
(assoc-in [:chats chat-id :messages message-id :user-statuses] statuses))
:update-message {:message-id message-id
:user-statuses statuses}}
;; for public chats and 1-1 bot/dapp chats, it makes no sense to signalise `:seen` msg
(not (or public? (get-in contacts [chat-id] :dapp?)))
(assoc :protocol-send-seen {:web3 web3
:message (cond-> {:from me
:to from
:message-id message-id}
group-chat (assoc :group-id chat-id))})))))
(handlers/register-handler-fx
:show-mnemonic

View File

@ -44,10 +44,13 @@
(sign-up db phone-number message-id)))
(defn- message-seen [{:keys [db] :as fx} message-id]
(-> fx
(assoc-in [:db :chats const/console-chat-id :messages message-id :message-status] :seen)
(assoc :update-message {:message-id message-id
:message-status :seen})))
(let [statuses-path [:chats const/console-chat-id :messages message-id :user-statuses]
statuses (-> (get-in db statuses-path)
(assoc const/console-chat-id :seen))]
(-> fx
(assoc-in (into [:db] statuses-path) statuses)
(assoc :update-message {:message-id message-id
:user-statuses statuses}))))
(handlers/register-handler-fx
:start-listening-confirmation-code-sms

View File

@ -79,9 +79,15 @@
(dispatch [:prepare-command! chat-id params])))
(dispatch [:set-chat-ui-props {:sending-in-progress? false}]))))
(defn- message-type [{:keys [group-chat public?]}]
(cond
(and group-chat public?) :public-group-user-message
(and group-chat (not public?)) :group-user-message
:else :user-message))
(register-handler :prepare-command!
(u/side-effect!
(fn [{:keys [current-public-key network-status] :as db}
(fn [{:keys [current-public-key network-status chats] :as db}
[_ add-to-chat-id {{:keys [handler-data
command]
:as content} :command
@ -92,7 +98,8 @@
hidden-params (->> (:params command)
(filter :hidden)
(map :name))
command' (prepare-command current-public-key chat-id clock-value request content)]
command' (-> (prepare-command current-public-key chat-id clock-value request content)
(assoc :message-type (message-type (get chats chat-id))))]
(dispatch [:update-message-overhead! chat-id network-status])
(dispatch [:set-chat-ui-props {:sending-in-progress? false}])
(dispatch [::send-command! add-to-chat-id (assoc params :command command') hidden-params])

View File

@ -4,8 +4,7 @@
[status-im.constants :as constants]
[status-im.chat.utils :as chat-utils]
[status-im.chat.models :as chat-model]
[status-im.chat.models.commands :as commands-model]
[status-im.chat.models.unviewed-messages :as unviewed-messages-model]
[status-im.chat.models.commands :as commands-model]
[status-im.chat.events.requests :as requests-events]
[taoensso.timbre :as log]))
@ -33,8 +32,8 @@
(defn- add-message-to-db
[db {:keys [message-id] :as message} chat-id]
(-> db
(chat-utils/add-message-to-db chat-id chat-id message (:new? message))
(unviewed-messages-model/add-unviewed-message chat-id message-id)))
(update-in [:chats chat-id :unviewed-messages] (fnil conj #{}) message-id)
(chat-utils/add-message-to-db chat-id chat-id message (:new? message))))
(defn receive
[{:keys [db message-exists? pop-up-chat? get-last-clock-value now] :as cofx}
@ -57,21 +56,23 @@
command-request? (= content-type constants/content-type-command-request)
command (:command content)
enriched-message (cond-> (assoc message
:chat-id chat-identifier
:timestamp (or timestamp now)
:show? true
:clock-value (clocks/receive
clock-value
(get-last-clock-value chat-identifier)))
(and command command-request?)
(assoc-in [:content :content-command-ref]
(lookup-response-ref access-scope->commands-responses
current-account
(get-in fx [:db :chats chat-identifier])
contacts
command)))]
:chat-id chat-identifier
:timestamp (or timestamp now)
:show? true
:clock-value (clocks/receive
clock-value
(get-last-clock-value chat-identifier)))
public-key
(assoc :user-statuses {public-key :received})
(and command command-request?)
(assoc-in [:content :content-command-ref]
(lookup-response-ref access-scope->commands-responses
current-account
(get-in fx [:db :chats chat-identifier])
contacts
command)))]
(cond-> (-> fx
(update :db add-message-to-db enriched-message chat-identifier)
(assoc :save-message (dissoc enriched-message :new?)))
command-request?
(requests-events/add-request chat-identifier enriched-message))))))
command-request?
(requests-events/add-request chat-identifier enriched-message))))))

View File

@ -1,13 +0,0 @@
(ns status-im.chat.models.unviewed-messages)
(defn index-unviewed-messages [unviewed-messages]
(into {}
(map (fn [[chat-id messages]]
[chat-id (into #{} (map :message-id) messages)]))
(group-by :chat-id unviewed-messages)))
(defn add-unviewed-message [db chat-id message-id]
(update-in db [:chats chat-id :unviewed-messages] (fnil conj #{}) message-id))
(defn remove-unviewed-message [db chat-id message-id]
(update-in db [:chats chat-id :unviewed-messages] disj message-id))

View File

@ -10,10 +10,8 @@
(s/def :chat/chat-list-ui-props (s/nilable map?))
(s/def :chat/layout-height (s/nilable number?)) ; height of chat's view layout
(s/def :chat/expandable-view-height-to-value (s/nilable number?))
(s/def :chat/message-status (s/nilable map?)) ; TODO janherich: remove later
(s/def :chat/selected-participants (s/nilable set?))
(s/def :chat/chat-loaded-callbacks (s/nilable map?))
(s/def :chat/command-hash-valid? (s/nilable boolean?))
(s/def :chat/chat-loaded-callbacks (s/nilable map?))
(s/def :chat/public-group-topic (s/nilable string?))
(s/def :chat/confirmation-code-sms-listener (s/nilable any?)) ; .addListener result object
(s/def :chat/messages (s/nilable map?)) ; messages indexed by message-id

View File

@ -61,6 +61,12 @@
(fn [[chats current-chat-id]]
(get chats current-chat-id)))
(reg-sub
:get-current-chat-message
:<- [:get-current-chat]
(fn [{:keys [messages]} [_ message-id]]
(get messages message-id)))
(reg-sub
:chat
:<- [:chats]
@ -265,7 +271,7 @@
(:web-view-extra-js current-chat)))
(reg-sub
:photo-path
:get-photo-path
:<- [:get-contacts]
(fn [contacts [_ id]]
(:photo-path (contacts id))))

View File

@ -9,5 +9,8 @@
:new? (if (nil? new?) true new?))]
(update-in db [:chats add-to-chat-id :messages] assoc message-id prepared-message))))
(defn message-seen-by? [message user-pk]
(= :seen (get-in message [:user-statuses user-pk])))
(defn command-name [{:keys [name]}]
(str chat.constants/command-char name))

View File

@ -1,72 +1,63 @@
(ns status-im.chat.views.input.suggestions
(:require-macros [status-im.utils.views :refer [defview]])
(:require [re-frame.core :refer [subscribe dispatch]]
[status-im.ui.components.react :refer [view
scroll-view
touchable-highlight
text
icon]]
[status-im.data-store.messages :as messages]
(:require [re-frame.core :as re-frame]
[status-im.ui.components.react :as react]
[status-im.chat.styles.input.suggestions :as style]
[status-im.chat.constants :as const]
[status-im.chat.views.input.animations.expandable :refer [expandable-view]]
[status-im.chat.views.input.utils :as input-utils]
[status-im.i18n :refer [label]]
[taoensso.timbre :as log]
[status-im.chat.utils :as chat-utils]))
[status-im.chat.views.input.animations.expandable :as expandable]
[status-im.chat.utils :as chat.utils]
[status-im.i18n :as i18n]))
(defn suggestion-item [{:keys [on-press name description last?]}]
[touchable-highlight {:on-press on-press}
[view (style/item-suggestion-container last?)
[view {:style style/item-suggestion-name}
[text {:style style/item-suggestion-name-text
:font :roboto-mono} name]]
[text {:style style/item-suggestion-description
:number-of-lines 2}
[react/touchable-highlight {:on-press on-press}
[react/view (style/item-suggestion-container last?)
[react/view {:style style/item-suggestion-name}
[react/text {:style style/item-suggestion-name-text
:font :roboto-mono} name]]
[react/text {:style style/item-suggestion-description
:number-of-lines 2}
description]]])
(defview response-item [{:keys [name description]
{:keys [type message-id]} :request :as command} last?]
[{:keys [chat-id]} [:get-current-chat]]
{:keys [message-id]} :request :as command} last?]
[{{:keys [params]} :content} [:get-current-chat-message message-id]]
[suggestion-item
{:on-press #(let [{:keys [params]} (messages/get-message-content-by-id message-id)
metadata (assoc params :to-message-id message-id)]
(dispatch [:select-chat-input-command command metadata]))
:name (chat-utils/command-name command)
{:on-press #(let [metadata (assoc params :to-message-id message-id)]
(re-frame/dispatch [:select-chat-input-command command metadata]))
:name (chat.utils/command-name command)
:description description
:last? last?}])
(defn command-item [{:keys [name description bot] :as command} last?]
[suggestion-item
{:on-press #(dispatch [:select-chat-input-command command nil])
:name (chat-utils/command-name command)
{:on-press #(re-frame/dispatch [:select-chat-input-command command nil])
:name (chat.utils/command-name command)
:description description
:last? last?}])
(defn item-title [top-padding? s]
[view (style/item-title-container top-padding?)
[text {:style style/item-title-text}
s]])
(defn item-title [top-padding? title]
[react/view (style/item-title-container top-padding?)
[react/text {:style style/item-title-text}
title]])
(defview suggestions-view []
[show-suggestions? [:show-suggestions?]
responses [:get-available-responses]
commands [:get-available-commands]]
(when show-suggestions?
[expandable-view {:key :suggestions
:draggable? false
:height 212}
[view {:flex 1}
[scroll-view {:keyboardShouldPersistTaps :always}
[expandable/expandable-view {:key :suggestions
:draggable? false
:height 212}
[react/view {:flex 1}
[react/scroll-view {:keyboardShouldPersistTaps :always}
(when (seq responses)
[view
[item-title false (label :t/suggestions-requests)]
[react/view
[item-title false (i18n/label :t/suggestions-requests)]
(for [[i response] (map-indexed vector responses)]
^{:key i}
[response-item response (= i (dec (count responses)))])])
(when (seq commands)
[view
[item-title (seq responses) (label :t/suggestions-commands)]
[react/view
[item-title (seq responses) (i18n/label :t/suggestions-commands)]
(for [[i command] (map-indexed vector commands)]
^{:key i}
[command-item command (= i (dec (count commands)))])])]]]))

View File

@ -174,71 +174,66 @@
[message-content-audio {:content content
:content-type content-type}]]])
(defview group-message-delivery-status [{:keys [message-id group-id message-status user-statuses] :as msg}]
(letsubs [chat [:get-current-chat]
contacts [:get-contacts]]
(let [status (or message-status :sending)
participants (:contacts chat)
seen-by-everyone? (and (= (count user-statuses) (count participants))
(every? (fn [[_ {:keys [status]}]]
(= (keyword status) :seen)) user-statuses))]
(if (or (zero? (count user-statuses))
seen-by-everyone?)
[react/view style/delivery-view
[react/text {:style style/delivery-text
:font :default}
(i18n/message-status-label
(if seen-by-everyone?
:seen-by-everyone
status))]]
(defn- text-status [status]
[react/view style/delivery-view
[react/text {:style style/delivery-text
:font :default}
(i18n/message-status-label status)]])
(defview group-message-delivery-status [{:keys [message-id group-id current-public-key user-statuses] :as msg}]
(letsubs [{participants :contacts} [:get-current-chat]
contacts [:get-contacts]]
(let [outgoing-status (or (get user-statuses current-public-key) :sending)
delivery-statuses (dissoc user-statuses current-public-key)
delivery-statuses-count (count delivery-statuses)
seen-by-everyone (and (= delivery-statuses-count (count participants))
(every? (comp (partial = :seen) second) delivery-statuses)
:seen-by-everyone)]
(if (or seen-by-everyone (zero? delivery-statuses-count))
[text-status (or seen-by-everyone outgoing-status)]
[react/touchable-highlight
{:on-press (fn []
(re-frame/dispatch [:show-message-details {:message-status status
:user-statuses user-statuses
:participants participants}]))}
{:on-press #(re-frame/dispatch [:show-message-details {:message-status outgoing-status
:user-statuses delivery-statuses
:participants participants}])}
[react/view style/delivery-view
(for [[_ {:keys [whisper-identity]}] (take 3 user-statuses)]
(for [[whisper-identity] (take 3 delivery-statuses)]
^{:key whisper-identity}
[react/image {:source {:uri (or (get-in contacts [whisper-identity :photo-path])
(identicon/identicon whisper-identity))}
:style {:width 16
:height 16
:borderRadius 8}}])
(if (> (count user-statuses) 3)
(if (> delivery-statuses-count 3)
[react/text {:style style/delivery-text
:font :default}
(str "+ " (- (count user-statuses) 3))])]]))))
(str "+ " (- delivery-statuses-count 3))])]]))))
(defn message-delivery-status
[{:keys [message-id chat-id message-status user-statuses content]}]
(let [delivery-status (get-in user-statuses [chat-id :status])
status (cond (and (not (console/commands-with-delivery-status (:command content)))
(= constants/console-chat-id chat-id))
[{:keys [message-id chat-id current-public-key user-statuses content]}]
(let [outgoing-status (or (get user-statuses current-public-key) :sending)
delivery-status (get user-statuses chat-id)
status (cond (and (= constants/console-chat-id chat-id)
(not (console/commands-with-delivery-status (:command content))))
:seen
:else
(or delivery-status message-status :sending))]
[react/view style/delivery-view
[react/text {:style style/delivery-text
:font :default}
(i18n/message-status-label status)]]))
(or delivery-status outgoing-status))]
[text-status status]))
(defn- photo [from photo-path]
[react/view
[react/image {:source {:uri (if (string/blank? photo-path)
(identicon/identicon from)
photo-path)}
:style style/photo}]])
(defview member-photo [from]
(letsubs [photo-path [:photo-path from]]
[react/view
[react/image {:source {:uri (if (string/blank? photo-path)
(identicon/identicon from)
photo-path)}
:style style/photo}]]))
(letsubs [photo-path [:get-photo-path from]]
(photo from photo-path)))
(defview my-photo [from]
(letsubs [account [:get-current-account]]
(let [{:keys [photo-path]} account]
[react/view
[react/image {:source {:uri (if (string/blank? photo-path)
(identicon/identicon from)
photo-path)}
:style style/photo}]])))
(letsubs [{:keys [photo-path]} [:get-current-account]]
(photo from photo-path)))
(defn message-body
[{:keys [last-outgoing? message-type same-author? from outgoing] :as message} content]
@ -291,18 +286,16 @@
children)])}))
(into [react/view] children)))
(defn chat-message [{:keys [outgoing message-id chat-id message-status user-statuses
from current-public-key] :as message}]
(defn chat-message [{:keys [outgoing message-id chat-id from current-public-key] :as message}]
(reagent/create-class
{:display-name "chat-message"
:component-did-mount
#(when (and message-id
chat-id
(not outgoing)
(not= :seen message-status)
(not= :seen (keyword (get-in user-statuses [current-public-key :status]))))
;; send `:seen` signal when we have signed-in user, message not from us and we didn't sent it already
#(when (and current-public-key message-id chat-id (not outgoing)
(not (chat.utils/message-seen-by? message current-public-key)))
(re-frame/dispatch [:send-seen! {:chat-id chat-id
:from from
:me current-public-key
:message-id message-id}]))
:reagent-render
(fn [{:keys [outgoing group-chat content-type content] :as message}]
@ -321,4 +314,5 @@
[react/view
(let [incoming-group (and group-chat (not outgoing))]
[message-content message-body (merge message
{:incoming-group incoming-group})])]]])}))
{:current-public-key current-public-key
:incoming-group incoming-group})])]]])}))

View File

@ -14,8 +14,7 @@
(def default-values
{:outgoing false
:to nil
:preview nil})
:to nil})
(defn exists? [message-id]
(data-store/exists? message-id))
@ -24,17 +23,11 @@
[message-id]
(data-store/get-by-id message-id))
(defn get-message-content-by-id [message-id]
(when-let [{:keys [content-type content] :as message} (get-by-id message-id)]
(when (command-type? content-type)
(reader/read-string content))))
(defn get-by-chat-id
([chat-id]
(get-by-chat-id chat-id 0))
([chat-id from]
(->> (data-store/get-by-chat-id chat-id from constants/default-number-of-messages)
reverse
(keep (fn [{:keys [content-type preview] :as message}]
(if (command-type? content-type)
(update message :content reader/read-string)
@ -46,13 +39,6 @@
(filter #(= (:content-type %) constants/content-type-log-message))
(map #(select-keys % [:content :timestamp]))))
(defn get-last-outgoing
[chat-id number-of-messages]
(data-store/get-by-fields {:chat-id chat-id
:outgoing true}
0
number-of-messages))
(defn get-last-clock-value
[chat-id]
(if-let [message (data-store/get-last-message chat-id)]
@ -60,34 +46,46 @@
0))
(defn get-unviewed
[]
(data-store/get-unviewed))
[current-public-key]
(into {}
(map (fn [[chat-id user-statuses]]
[chat-id (into #{} (map :message-id) user-statuses)]))
(group-by :chat-id (data-store/get-unviewed current-public-key))))
(defn- prepare-content [content]
(if (string? content)
content
(pr-str
;; TODO janherich: this is ugly and not systematic, define something like `:not-persisent`
;; option for command params instead
;; option for command params instead
(update content :params dissoc :password :password-confirmation))))
(defn save
[{:keys [message-id content] :as message}]
(defn- prepare-statuses [{:keys [chat-id message-id] :as message}]
(utils/update-if-present message
:user-statuses
(partial map (fn [[whisper-identity status]]
{:whisper-identity whisper-identity
:status status
:chat-id chat-id
:message-id message-id}))))
(defn- prepare-message [message]
(-> message
prepare-statuses
(utils/update-if-present :content prepare-content)))
(defn save
[{:keys [message-id content from] :as message}]
(when-not (data-store/exists? message-id)
(let [content' (prepare-content content)
message' (merge default-values
message
{:content content'
:timestamp (random/timestamp)})]
(data-store/save message'))))
(data-store/save (prepare-message (merge default-values
message
{:from (or from "anonymous")
:timestamp (random/timestamp)})))))
(defn update-message
[{:keys [message-id] :as message}]
(when (data-store/exists? message-id)
(let [message (-> message
(utils/update-if-present :user-statuses vals)
(utils/update-if-present :content prepare-content))]
(data-store/save message))))
(when-let [{:keys [chat-id]} (data-store/get-by-id message-id)]
(data-store/save (prepare-message (assoc message :chat-id chat-id)))))
(defn delete-by-chat-id [chat-id]
(data-store/delete-by-chat-id chat-id))

View File

@ -41,8 +41,8 @@
(data-store/save message')))
(defn delete
[pending-message]
(data-store/delete pending-message))
[message-id]
(data-store/delete message-id))
(defn delete-all-by-chat-id
[chat-id]

View File

@ -11,12 +11,18 @@
[]
(realm/js-object->clj (get-all)))
(defn- transform-message [message]
(update message :user-statuses
(partial into {}
(map (fn [[_ {:keys [whisper-identity status]}]]
[whisper-identity (keyword status)])))))
(defn get-by-id
[message-id]
(when-let [message (realm/get-one-by-field-clj @realm/account-realm :message :message-id message-id)]
(realm/fix-map message :user-statuses :whisper-identity)))
(some-> (realm/get-one-by-field-clj @realm/account-realm :message :message-id message-id)
transform-message))
(defn get-by-chat-id
(defn get-by-chat-id
([chat-id number-of-messages]
(get-by-chat-id chat-id 0 number-of-messages))
([chat-id from number-of-messages]
@ -24,7 +30,7 @@
(realm/sorted :timestamp :desc)
(realm/page from (+ from number-of-messages))
realm/js-object->clj)]
(mapv #(realm/fix-map % :user-statuses :whisper-identity) messages))))
(mapv transform-message messages))))
(defn get-by-fields
[fields from number-of-messages]
@ -40,10 +46,10 @@
(realm/single-clj)))
(defn get-unviewed
[]
[current-public-key]
(-> @realm/account-realm
(realm/get-by-fields :message :and {:outgoing false
:message-status nil})
(realm/get-by-fields :user-status :and {:whisper-identity current-public-key
:status :received})
realm/js-object->clj))
(defn exists?
@ -52,8 +58,7 @@
(defn save
[message]
(let [message (update message :user-statuses #(if % % []))]
(realm/save @realm/account-realm :message message true)))
(realm/save @realm/account-realm :message message true))
(defn delete-by-chat-id
[chat-id]

View File

@ -23,9 +23,8 @@
(realm/save @realm/account-realm :pending-message pending-message true))
(defn delete
[{{:keys [message-id ack-of-message]} :payload}]
(let [message-id (or ack-of-message message-id)]
(realm/delete @realm/account-realm (get-by-message-id message-id))))
[message-id]
(realm/delete @realm/account-realm (get-by-message-id message-id)))
(defn delete-all-by-chat-id
[chat-id]

View File

@ -8,7 +8,7 @@
[status-im.data-store.realm.schemas.account.v1.processed-message :as processed-message]
[status-im.data-store.realm.schemas.account.v19.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]
[status-im.data-store.realm.schemas.account.v19.user-status :as user-status]
[status-im.data-store.realm.schemas.account.v5.contact-group :as contact-group]
[status-im.data-store.realm.schemas.account.v5.group-contact :as group-contact]
[status-im.data-store.realm.schemas.account.v8.local-storage :as local-storage]
@ -101,6 +101,24 @@
(log/debug "migrating v19 command/request database, updating: " content " with: " new-props)
(aset object "content" (pr-str new-content)))))))
(defn update-message-statuses [new-realm]
(some-> new-realm
(.objects "message")
(.map (fn [msg _ _]
(let [message-id (aget msg "message-id")
chat-id (aget msg "chat-id")
from (aget msg "from")
msg-status (aget msg "message-status")
statuses (aget msg "user-statuses")]
(when statuses
(.map statuses (fn [status _ _]
(aset status "message-id" message-id)
(aset status "chat-id" chat-id)))
(.push statuses (clj->js {"message-id" message-id
"chat-id" chat-id
"status" (or msg-status "received")
"whisper-identity" (or from "anonymous")}))))))))
(defn migration [old-realm new-realm]
(log/debug "migrating v19 account database: " old-realm new-realm)
(remove-contact! new-realm "transactor-personal")
@ -108,4 +126,5 @@
(remove-console-intro-message! new-realm)
(update-commands (juxt :bot :command) owner-command->new-props new-realm "command")
(update-commands (juxt :command) console-requests->new-props new-realm "command-request")
(update-commands (juxt :command (comp count :prefill)) transactor-requests->new-props new-realm "command-request"))
(update-commands (juxt :command (comp count :prefill)) transactor-requests->new-props new-realm "command-request")
(update-message-statuses new-realm))

View File

@ -17,13 +17,13 @@
:indexed true}
:outgoing :bool
:retry-count {:type :int
:default 0}
:default 0}
:message-type {:type :string
:optional true}
:message-status {:type :string
:optional true}
:user-statuses {:type :list
:objectType "user-status"}
:objectType :user-status}
:clock-value {:type :int
:default 0}
:show? {:type :bool

View File

@ -0,0 +1,7 @@
(ns status-im.data-store.realm.schemas.account.v19.user-status)
(def schema {:name :user-status
:properties {:message-id :string
:chat-id :string
:whisper-identity :string
:status :string}})

View File

@ -11,6 +11,7 @@
[status-im.i18n :as i18n]
[status-im.utils.random :as random]
[status-im.protocol.message-cache :as cache]
[status-im.chat.utils :as chat.utils]
[status-im.utils.datetime :as datetime]
[taoensso.timbre :as log :refer-macros [debug]]
[status-im.native-module.core :as status]
@ -197,39 +198,15 @@
:content (str (or inviter-name from) " " (i18n/label :t/invited) " " (or invitee-name identity))
:content-type constants/text-content-type}]))))
(re-frame/reg-fx
::save-message-status!
(fn [{:keys [message-id ack-of-message group-id from status]}]
(let [message-id' (or ack-of-message message-id)]
(when-let [{:keys [message-status] :as message} (messages/get-by-id message-id')]
(when-not (= (keyword message-status) :seen)
(let [group? (boolean group-id)
message' (-> (if (and group? (not= status :sent))
(update-in message
[:user-statuses from]
(fn [{old-status :status}]
{:id (random/id)
:whisper-identity from
:status (if (= (keyword old-status) :seen)
old-status
status)}))
(assoc message :message-status status))
;; we need to dissoc preview because it has been saved before
(dissoc :preview))]
(messages/update-message message')))))))
(re-frame/reg-fx
::pending-messages-delete
(fn [message]
(pending-messages/delete message)))
(fn [message-id]
(pending-messages/delete message-id)))
(re-frame/reg-fx
::pending-messages-save
(fn [{:keys [type id pending-message]}]
(pending-messages/save pending-message)
(when (#{:message :group-message} type)
(messages/update-message {:message-id id
:delivery-status :pending}))))
(fn [pending-message]
(pending-messages/save pending-message)))
(re-frame/reg-fx
::status-init-jail
@ -316,9 +293,18 @@
:to to
:chat-id from}))
(defn- message-from-self [{:keys [current-public-key]} {:keys [id to group-id]}]
{:from to
:sent-from current-public-key
:payload {:message-id id
:group-id group-id}})
(defn- get-message-id [{:keys [message-id ack-of-message]}]
(or ack-of-message message-id))
(handlers/register-handler-fx
:incoming-message
(fn [_ [_ type {:keys [payload ttl id] :as message}]]
:incoming-message
(fn [{:keys [db]} [_ type {:keys [payload ttl id] :as message}]]
(let [message-id (or id (:message-id payload))]
(when-not (cache/exists? message-id type)
(let [ttl-s (* 1000 (or ttl 120))
@ -326,74 +312,56 @@
:message-id message-id
:type type
:ttl (+ (datetime/now-ms) ttl-s)}
route-event (case type
(:message
:group-message
:public-group-message) [:chat-received-message/add (transform-protocol-message message)]
:ack (if (#{:message :group-message} (:type payload))
[:update-message-status message :delivered]
[:pending-message-remove message])
:seen [:update-message-status message :seen]
:group-invitation [:group-chat-invite-received message]
:update-group [:update-group-message message]
:add-group-identity [:participant-invited-to-group message]
:remove-group-identity [:participant-removed-from-group message]
:leave-group [:participant-left-group message]
:contact-request [:contact-request-received message]
:discover [:status-received message]
:discoveries-request [:discoveries-request-received message]
:discoveries-response [:discoveries-response-received message]
:profile [:contact-update-received message]
:update-keys [:update-keys-received message]
:online [:contact-online-received message]
:pending [:pending-message-upsert message]
:sent (let [{:keys [to id group-id]} message
message' {:from to
:payload {:message-id id
:group-id group-id}}]
[:update-message-status message' :sent])
nil)]
(when (nil? route-event) (debug "Unknown message type" type))
chat-message (#{:message :group-message} (:type payload))
route-fx (case type
(:message
:group-message
:public-group-message) {:dispatch [:chat-received-message/add (transform-protocol-message message)]}
:pending (cond-> {::pending-messages-save message}
chat-message
(assoc :dispatch
[:update-message-status (message-from-self db message) :pending]))
:sent {:dispatch [:update-message-status (message-from-self db message) :sent]}
:ack (cond-> {::pending-messages-delete (get-message-id payload)}
chat-message
(assoc :dispatch [:update-message-status message :delivered]))
:seen {:dispatch [:update-message-status message :seen]}
:group-invitation {:dispatch [:group-chat-invite-received message]}
:update-group {:dispatch [:update-group-message message]}
:add-group-identity {:dispatch [:participant-invited-to-group message]}
:remove-group-identity {:dispatch [:participant-removed-from-group message]}
:leave-group {:dispatch [:participant-left-group message]}
:contact-request {:dispatch [:contact-request-received message]}
:discover {:dispatch [:status-received message]}
:discoveries-request {:dispatch [:discoveries-request-received message]}
:discoveries-response {:dispatch [:discoveries-response-received message]}
:profile {:dispatch [:contact-update-received message]}
:update-keys {:dispatch [:update-keys-received message]}
:online {:dispatch [:contact-online-received message]}
nil)]
(when (nil? route-fx) (debug "Unknown message type" type))
(cache/add! processed-message)
(merge
{::save-processed-messages processed-message}
(when route-event {:dispatch route-event})))))))
(defn update-message-status [db {:keys [message-id ack-of-message group-id from status]}]
(let [message-id' (or ack-of-message message-id)
update-group-status? (and group-id (not= status :sent))
message-path [:chats (or group-id from) :messages message-id']
current-status (if update-group-status?
(get-in db (into message-path [:user-statuses from :status]))
(get-in db (into message-path [:message-status])))]
;; for some strange reason, we sometimes receive status update for message we don't have,
;; that's why the first condition in if
(if (and (get-in db message-path)
(not= :seen current-status))
(if update-group-status?
(assoc-in db (into message-path [:user-statuses from]) {:whisper-identity from
:status status})
(assoc-in db (into message-path [:message-status]) status))
db)))
route-fx))))))
(handlers/register-handler-fx
:update-message-status
[re-frame/trim-v
(re-frame/inject-cofx :get-stored-message)
(re-frame/inject-cofx ::chats-is-active?)]
(fn [{db :db chats-is-active? :chats-is-active?}
[{:keys [from]
{:keys [message-id ack-of-message group-id]} :payload
:as message}
status]]
(let [data {:status status
:message-id message-id :ack-of-message ack-of-message
:group-id group-id :from from}]
(merge
{::save-message-status! data}
(when (= status :delivered)
{:dispatch [:pending-message-remove message]})
(when chats-is-active?
{:db (update-message-status db data)})))))
(fn [{:keys [db chats-is-active? get-stored-message]} [{:keys [from sent-from payload]} status]]
(let [message-identifier (get-message-id payload)
message-db-path [:chats (or (:group-id payload) from) :messages message-identifier]
from-id (or sent-from from)
message (get-stored-message message-identifier)]
;; proceed with updating status if chat is active, and message was not already seen
(when (and chats-is-active? (not (chat.utils/message-seen-by? message from-id)))
(let [statuses (assoc (:user-statuses message) from-id status)]
(cond-> {:update-message {:message-id message-identifier
:user-statuses statuses}}
(get-in db message-db-path)
(assoc :db (assoc-in db (conj message-db-path :user-statuses) statuses))))))))
(handlers/register-handler-fx
:contact-request-received
@ -423,23 +391,6 @@
[:update-chat! chat]
[:watch-contact contact]]}))))))
(handlers/register-handler-fx
:pending-message-upsert
(fn [{db :db} [_ {:keys [type id to group-id] :as pending-message}]]
(let [chat-id (or group-id to)
current-status (get-in db [:message-status chat-id id])]
(merge
{::pending-messages-save {:type type :id id :pending-message pending-message}}
(when (and (#{:message :group-message} type) (not= :seen current-status))
{:db (assoc-in db [:message-status chat-id id] :pending)})))))
(handlers/register-handler-fx
:pending-message-remove
(fn [_ [_ message]]
{::pending-messages-delete message}))
;;GROUP
(handlers/register-handler-fx

View File

@ -21,7 +21,7 @@
:gas-price ethereum/default-gas-price})
;; initial state of app-db
(def app-db {:current-public-key ""
(def app-db {:current-public-key nil
:status-module-initialized? (or platform/ios? js/goog.DEBUG)
:keyboard-height 0
:accounts/accounts {}
@ -162,8 +162,7 @@
:chat/message-data
:chat/message-status
:chat/selected-participants
:chat/chat-loaded-callbacks
:chat/command-hash-valid?
:chat/chat-loaded-callbacks
:chat/public-group-topic
:chat/confirmation-code-sms-listener
:chat/messages