Merge pull request #223 from status-im/feature/group-chat-delivery-status-#205

Group chat delivery statuses (#205)

Former-commit-id: 21963e47de
This commit is contained in:
Roman Volosovskyi 2016-09-16 14:50:25 +03:00 committed by GitHub
commit d234817599
30 changed files with 425 additions and 190 deletions

View File

@ -8,9 +8,8 @@
[reagent "0.5.1" :exclusions [cljsjs/react]]
[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.2.2-20160909_082306-gcfbb92b"]
^{:voom {:repo "git@github.com:status-im/status-lib.git" :branch "group-chat-statuses"}}
[status-im/protocol "0.2.3-20160914_155558-gfed628a"]
[natal-shell "0.3.0"]
[com.andrewmcveigh/cljs-time "0.4.0"]
[tailrecursion/cljs-priority-map "1.2.0"]

View File

@ -36,9 +36,19 @@
status-im.chat.handlers.wallet-chat
[status-im.utils.logging :as log]))
(register-handler :set-show-actions
(fn [db [_ show-actions]]
(assoc db :show-actions show-actions)))
(register-handler :set-chat-ui-props
(fn [db [_ ui-element value]]
(assoc-in db [:chat-ui-props ui-element] value)))
(register-handler :set-show-info
(fn [db [_ show-info]]
(assoc db :show-info show-info)))
(register-handler :show-message-details
(u/side-effect!
(fn [_ [_ details]]
(dispatch [:set-chat-ui-props :show-bottom-info? true])
(dispatch [:set-chat-ui-props :bottom-info details]))))
(register-handler :load-more-messages
(fn [{:keys [current-chat-id loading-allowed] :as db} _]
@ -296,7 +306,6 @@
[{:keys [new-chat]} _]
(chats/create-chat new-chat))
(defn open-chat!
[_ [_ chat-id _ navigation-type]]
(dispatch [(or navigation-type :navigate-to) :chat chat-id]))
@ -395,11 +404,10 @@
(fn [db [_ h]]
(assoc db :layout-height h)))
(register-handler :send-seen!
(after (fn [_ [_ chat-id message-id]]
(when-not (console? chat-id))
(dispatch [:message-seen chat-id message-id])))
(dispatch [:message-seen {:message-id message-id
:chat-id chat-id}])))
(u/side-effect!
(fn [_ [_ chat-id message-id]]
(when-not (console? chat-id)
@ -419,16 +427,16 @@
(fn [db [_ chat-id mode]]
(assoc-in db [:kb-mode chat-id] mode)))
(defn save-chat!
(defn update-chat!
[_ [_ chat]]
(chats/create-chat chat))
(chats/update-chat 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 save-chat!))))
((after update-chat!))))
(register-handler :check-autorun
(u/side-effect!

View File

@ -6,7 +6,8 @@
[status-im.commands.utils :refer [generate-hiccup]]
[status-im.constants :refer [content-type-command-request]]
[cljs.reader :refer [read-string]]
[status-im.models.chats :as c]))
[status-im.models.chats :as c]
[status-im.utils.logging :as log]))
(defn check-preview [{:keys [content] :as message}]
(if-let [preview (:preview content)]
@ -34,7 +35,6 @@
message' (assoc (->> message
(cu/check-author-direction previous-message)
(check-preview))
:delivery-status :sending
:chat-id chat-id')]
(store-message message')
(when-not (c/chat-exists? chat-id')

View File

@ -24,10 +24,10 @@
[{: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 :and [[:chat-id chat-id']
(realm/get-by-fields :account :request :and [[:chat-id chat-id']
[:status "open"]])
(realm/sorted :added :desc)
(realm/collection->map))
(realm/sorted :added :desc)
(realm/realm-collection->list))
requests' (map #(update % :type keyword) requests)]
(assoc-in db [:chats chat-id' :requests] requests')))

View File

@ -16,11 +16,6 @@
[status-im.protocol.api :as api]
[status-im.utils.logging :as log]))
(defn default-delivery-status [chat-id]
(if (cu/console? chat-id)
:seen
:sending))
(defn prepare-message
[{:keys [identity current-chat-id] :as db} _]
(let [text (get-in db [:chats current-chat-id :input-text])
@ -33,7 +28,6 @@
: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
@ -49,7 +43,6 @@
:to chat-id
:content (assoc content :preview preview-string)
:content-type content-type-command
:delivery-status (default-delivery-status chat-id)
:outgoing true
:preview preview-string
:rendered-preview preview
@ -162,7 +155,6 @@
:content message
:from identity
:content-type text-content-type
:delivery-status (default-delivery-status chat-id)
:outgoing true
:timestamp (time/now-ms)})
message'' (if group-chat

View File

@ -1,12 +1,13 @@
(ns status-im.chat.handlers.unviewed-messages
(:require [re-frame.core :refer [after enrich path dispatch]]
[status-im.utils.handlers :refer [register-handler]]
[status-im.persistence.realm.core :as realm]))
[status-im.persistence.realm.core :as realm]
[status-im.utils.logging :as log]))
(defn delivered-messages []
(-> (realm/get-by-fields :account :message :and [[:delivery-status :delivered]
[:outgoing false]])
(realm/collection->map)))
(-> (realm/get-by-fields :account :message :and {:outgoing false
:message-status nil})
(realm/realm-collection->list)))
(defn set-unviewed-messages [db]
(let [messages (->> (::raw-unviewed-messages db)

View File

@ -24,6 +24,7 @@
[status-im.chat.views.response :refer [response-view]]
[status-im.chat.views.new-message :refer [chat-message-new]]
[status-im.chat.views.actions :refer [actions-view]]
[status-im.chat.views.bottom-info :refer [bottom-info-view]]
[status-im.i18n :refer [label label-pluralize]]
[status-im.components.animation :as anim]
[reagent.core :as r]
@ -101,7 +102,7 @@
(defn toolbar-content []
(let [{:keys [group-chat name contacts chat-id]} (subscribe [:chat-properties [:group-chat :name :contacts :chat-id]])
show-actions (subscribe [:show-actions])
show-actions (subscribe [:chat-ui-props :show-actions?])
contact (subscribe [:get-in [:contacts @chat-id]])]
(fn []
[view (st/chat-name-view @show-actions)
@ -123,20 +124,20 @@
(online-text @contact @chat-id)])])))
(defn toolbar-action []
(let [show-actions (subscribe [:show-actions])]
(let [show-actions (subscribe [:chat-ui-props :show-actions?])]
(fn []
(if @show-actions
[touchable-highlight
{:on-press #(dispatch [:set-show-actions false])}
{:on-press #(dispatch [:set-chat-ui-props :show-actions? false])}
[view st/action
[icon :up st/up-icon]]]
[touchable-highlight
{:on-press #(dispatch [:set-show-actions true])}
{:on-press #(dispatch [:set-chat-ui-props :show-actions? true])}
[view st/action
[chat-icon]]]))))
(defview chat-toolbar []
[show-actions [:show-actions]]
[show-actions [:chat-ui-props :show-actions?]]
[view
[status-bar]
[toolbar {:hide-nav? show-actions
@ -182,7 +183,8 @@
(defn chat []
(let [group-chat (subscribe [:chat :group-chat])
show-actions (subscribe [:show-actions])
show-actions? (subscribe [:chat-ui-props :show-actions?])
show-bottom-info? (subscribe [:chat-ui-props :show-bottom-info?])
command? (subscribe [:command?])
layout-height (subscribe [:get :layout-height])]
(r/create-class
@ -200,6 +202,10 @@
;; todo uncomment this
#_(when @group-chat [typing-all])
[response-view]
(when-not @command? [suggestion-container])
(when-not @command?
[suggestion-container])
[chat-message-new]
(when @show-actions [actions-view])])})))
(when @show-actions?
[actions-view])
(when @show-bottom-info?
[bottom-info-view])])})))

View File

@ -157,7 +157,6 @@
(def intro-status
{:message-id "intro-status"
:content (label :t/intro-status)
:delivery-status "seen"
:from "console"
:chat-id "console"
:content-type content-type-status

View File

@ -2,13 +2,15 @@
(:require [status-im.components.styles :refer [font
title-font
color-white
color-black
chat-background
online-color
selected-message-color
separator-color
text1-color
text2-color
toolbar-background1]]))
toolbar-background1]]
[status-im.utils.logging :as log]))
(def chat-view
{:flex 1
@ -114,6 +116,13 @@
:color text2-color
:font-size 12})
(def actions-overlay
{:position :absolute
:top 0
:bottom 0
:left 0
:right 0})
(def typing-all
{:marginBottom 20})
@ -137,12 +146,51 @@
:fontFamily font
:color text2-color})
(def actions-overlay
{:position :absolute
:top 0
:bottom 0
:left 0
:right 0})
(def overlay-highlight
{:flex 1})
;; this map looks a bit strange
;; but this way of setting elevation seems to be the only way to set z-index (in RN 0.30)
(def bottom-info-overlay
{:position :absolute
:top -16
:bottom -16
:left -16
:right -16
:background-color "#00000055"
:elevation 8})
(defn bottom-info-container [height]
{:backgroundColor toolbar-background1
:elevation 2
:position :absolute
:bottom 16
:left 16
:right 16
:height height})
(def bottom-info-list-container
{:padding-left 16
:padding-right 16
:padding-top 8
:padding-bottom 8})
(def bottom-info-row
{:flex-direction "row"
:padding-top 4
:padding-bottom 4})
(def bottom-info-row-photo
{:width 42
:height 42
:borderRadius 21})
(def bottom-info-row-text-container
{:margin-left 16
:margin-right 16})
(def bottom-info-row-text1
{:color "black"})
(def bottom-info-row-text2
{:color "#888888"})

View File

@ -3,6 +3,7 @@
(:require [re-frame.core :refer [register-sub dispatch subscribe path]]
[status-im.utils.platform :refer [ios?]]
[status-im.models.commands :as commands]
[status-im.models.chats :as chats]
[status-im.constants :refer [response-suggesstion-resize-duration]]
[status-im.chat.constants :as c]
[status-im.handlers.content-suggestions :refer [get-content-suggestions]]
@ -19,9 +20,9 @@
(into {}))))
(register-sub
:show-actions
(fn [db _]
(reaction (:show-actions @db))))
:chat-ui-props
(fn [db [_ ui-element]]
(reaction (get-in @db [:chat-ui-props ui-element]))))
(register-sub :chat
(fn [db [_ k]]
@ -48,6 +49,10 @@
(fn [db _]
(reaction (commands/get-commands @db))))
(register-sub :get-chat-by-id
(fn [_ [_ chat-id]]
(reaction (chats/chat-by-id chat-id))))
(register-sub :get-responses
(fn [db _]
(let [current-chat (@db :current-chat-id)]

View File

@ -105,7 +105,7 @@
subtitle]
icon-name :icon}]
[touchable-highlight {:on-press (fn []
(dispatch [:set-show-actions false])
(dispatch [:set-chat-ui-props :show-actions? false])
(when handler
(handler)))}
[view st/action-icon-row
@ -138,5 +138,5 @@
^{:key action} [action-view action]))]])))
(defn actions-view []
[overlay {:on-click-outside #(dispatch [:set-show-actions false])}
[overlay {:on-click-outside #(dispatch [:set-chat-ui-props :show-actions? false])}
[actions-list-view]])

View File

@ -0,0 +1,94 @@
(ns status-im.chat.views.bottom-info
(:require-macros [status-im.utils.views :refer [defview]])
(:require [re-frame.core :refer [subscribe dispatch]]
[reagent.core :as r]
[status-im.components.react :refer [view
animated-view
image
text
icon
touchable-highlight
list-view
list-item]]
[status-im.components.chat-icon.screen :refer [chat-icon-view-menu-item]]
[status-im.chat.styles.screen :as st]
[status-im.i18n :refer [label label-pluralize message-status-label]]
[status-im.components.animation :as anim]
[status-im.utils.utils :refer [truncate-str]]
[status-im.utils.identicon :refer [identicon]]
[status-im.utils.listview :as lw]
[status-im.utils.logging :as log]
[clojure.string :as str]))
(defn- container-animation-logic [{:keys [to-value val]}]
(fn [_]
(let [to-value @to-value]
(anim/start
(anim/spring val {:toValue to-value
:friction 6
:tension 40})))))
(defn overlay [{:keys [on-click-outside]} items]
[view {:style st/bottom-info-overlay}
[touchable-highlight {:on-press on-click-outside
:style st/overlay-highlight}
[view nil]]
items])
(defn container [& _]
(let [layout-height (r/atom 0)
anim-value (anim/create-value 1)
context {:to-value layout-height
:val anim-value}
on-update (container-animation-logic context)]
(r/create-class
{:component-did-update
on-update
:reagent-render
(fn [& children]
@layout-height
[animated-view {:style (st/bottom-info-container anim-value)}
(into [view {:onLayout (fn [event]
(let [height (.. event -nativeEvent -layout -height)]
(reset! layout-height height)))}]
children)])})))
(defn message-status-row [{:keys [photo-path name]} {:keys [whisper-identity status]}]
[view st/bottom-info-row
[image {:source {:uri (or photo-path (identicon whisper-identity))}
:style st/bottom-info-row-photo}]
[view st/bottom-info-row-text-container
[text {:style st/bottom-info-row-text1
:number-of-lines 1}
(truncate-str (if-not (str/blank? name)
name
whisper-identity) 30)]
[text {:style st/bottom-info-row-text2
:number-of-lines 1}
(message-status-label (or status :sending))]]])
(defn render-row [contacts]
(fn [{:keys [whisper-identity] :as row} _ _]
(let [contact (get contacts whisper-identity)]
(list-item [message-status-row contact row]))))
(defn bottom-info-view []
(let [bottom-info (subscribe [:chat-ui-props :bottom-info])
contacts (subscribe [:get-contacts])]
(r/create-class
{:reagent-render
(fn []
(let [{:keys [user-statuses message-status participants]} @bottom-info
participants (->> participants
(map (fn [{:keys [identity]}]
[identity {:whisper-identity identity
:status message-status}]))
(into {}))]
[overlay {:on-click-outside #(dispatch [:set-chat-ui-props :show-bottom-info? false])}
[container
[list-view {:dataSource (-> (merge participants user-statuses)
(vals)
(lw/to-datasource))
:enableEmptySections true
:renderRow (render-row @contacts)
:contentContainerStyle st/bottom-info-list-container}]]]))})))

View File

@ -12,6 +12,7 @@
[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.chats :refer [chat-by-id]]
[status-im.models.commands :refer [parse-command-message-content
parse-command-request]]
[status-im.resources :as res]
@ -20,7 +21,10 @@
content-type-status
content-type-command
content-type-command-request]]
[status-im.utils.logging :as log]))
[status-im.utils.logging :as log]
[status-im.protocol.api :as api]
[status-im.utils.identicon :refer [identicon]]
[status-im.chat.utils :as cu]))
(defn message-date [timestamp]
[view {}
@ -131,18 +135,64 @@
[message-content-audio {:content content
:content-type content-type}]]])
(defview message-delivery-status [{:keys [delivery-status chat-id message-id] :as message}]
[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}
:seen-by-everyone {:uri :icon_ok_small}
:failed res/delivery-failed-icon
nil)
:style st/delivery-image}]
[text {:style st/delivery-text
:font :default}
(message-status-label (or status delivery-status))]])
(defview group-message-delivery-status [{:keys [message-id group-id message-status user-statuses] :as msg}]
[app-db-message-user-statuses [:get-in [:message-user-statuses message-id]]
app-db-message-status-value [:get-in [:message-statuses message-id :status]]
chat [:get-chat-by-id group-id]
contacts [:get-contacts]]
(let [status (or message-status app-db-message-status-value :sending)
user-statuses (merge user-statuses app-db-message-user-statuses)
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?)
[view st/delivery-view
[image {:source (case status
:seen {:uri :icon_ok_small}
:failed res/delivery-failed-icon
nil)
:style st/delivery-image}]
[text {:style st/delivery-text
:font :default}
(message-status-label
(if seen-by-everyone?
:seen-by-everyone
status))]]
[touchable-highlight
{:on-press (fn []
(dispatch [:show-message-details {:message-status status
:user-statuses user-statuses
:participants participants}]))}
[view st/delivery-view
(for [[_ {:keys [whisper-identity]}] (take 3 user-statuses)]
^{:key whisper-identity}
[image {:source {:uri (or (get-in contacts [whisper-identity :photo-path])
(identicon whisper-identity))}
:style {:width 16
:height 16
:borderRadius 8}}])
(if (> (count user-statuses) 3)
[text {:style st/delivery-text
:font :default}
(str "+ " (- (count user-statuses) 3))])]])))
(defview message-delivery-status [{:keys [message-id chat-id message-status user-statuses]}]
[app-db-message-status-value [:get-in [:message-statuses message-id :status]]]
(let [delivery-status (get-in user-statuses [chat-id :status])
status (if (cu/console? chat-id)
:seen
(or delivery-status message-status app-db-message-status-value :sending))]
[view st/delivery-view
[image {:source (case status
:seen {:uri :icon_ok_small}
:failed res/delivery-failed-icon
nil)
:style st/delivery-image}]
[text {:style st/delivery-text
:font :default}
(message-status-label status)]]))
(defview member-photo [from]
[photo-path [:photo-path from]]
@ -167,14 +217,16 @@
content
;; TODO show for last or selected
(when (and selected delivery-status)
[message-delivery-status {:delivery-status delivery-status}])]]]))
[message-delivery-status message])]]]))
(defn message-body
[{:keys [outgoing] :as message} content]
[{:keys [outgoing message-type] :as message} content]
[view (st/message-body message)
content
(when outgoing
[message-delivery-status message])])
(if (= (keyword message-type) :group-user-message)
[group-message-delivery-status message]
[message-delivery-status message]))])
(defn message-container-animation-logic [{:keys [to-value val callback]}]
(fn [_]
@ -210,19 +262,18 @@
children)])}))
(into [view] children)))
(defn chat-message [{:keys [outgoing delivery-status timestamp new-day group-chat message-id chat-id]
:as message}]
(let [status (subscribe [:get-in [:message-status chat-id message-id]])]
(defn chat-message [{:keys [outgoing message-id chat-id user-statuses]}]
(let [my-identity (api/my-identity)
status (subscribe [:get-in [:message-user-statuses message-id my-identity]])]
(r/create-class
{:component-did-mount
(fn []
(when (and (not outgoing)
(not= :seen delivery-status)
(not= :seen @status))
(not= :seen (keyword @status))
(not= :seen (keyword (get-in user-statuses [my-identity :status]))))
(dispatch [:send-seen! chat-id message-id])))
:reagent-render
(fn [{:keys [outgoing delivery-status timestamp new-day group-chat]
:as message}]
(fn [{:keys [outgoing timestamp new-day group-chat] :as message}]
[message-container message
;; TODO there is no new-day info in message
(when new-day
@ -233,5 +284,4 @@
(if incoming-group
incoming-group-message-body
message-body)
(merge message {:delivery-status (keyword delivery-status)
:incoming-group incoming-group})])]])})))
(merge message {:incoming-group incoming-group})])]])})))

View File

@ -9,7 +9,6 @@
image
touchable-highlight]]
[status-im.utils.listview :refer [to-datasource]]
[reagent.core :as r]
[status-im.chats-list.views.chat-list-item :refer [chat-list-item]]
[status-im.components.action-button :refer [action-button
action-button-item]]

View File

@ -1,6 +1,5 @@
(ns status-im.components.status-bar
(:require [status-im.components.react :as ui]
[status-im.components.styles :as cst]
[status-im.utils.platform :refer [platform-specific]]))
(defn status-bar [{type :type

View File

@ -6,7 +6,7 @@
(register-sub :get-contacts
(fn [db _]
(let [contacts (reaction (:contacts @db))]
(reaction (vals @contacts)))))
(reaction @contacts))))
(defn sort-contacts [contacts]
(sort-by :name #(compare (clojure.string/lower-case %1)

View File

@ -7,7 +7,7 @@
(.isAddress js/Web3.prototype s))
(defn unique-identity? [identity]
(not (realm/exists? :account :contact :whisper-identity identity)))
(not (realm/exists? :account :contact {:whisper-identity identity})))
(defn valid-length? [identity]
(let [length (count identity)]

View File

@ -35,7 +35,8 @@
:contacts-ids #{}
:selected-contacts #{}
:chats-updated-signal 0
:show-actions false
:chat-ui-props {:show-actions? false
:show-bottom-info? false}
:selected-participants #{}
:signed-up false
:view-id default-view

View File

@ -39,7 +39,7 @@
(defn discovery-list []
(->> (-> (r/get-all :account :discovery)
(r/sorted :priority :desc)
(r/collection->map))
(r/realm-collection->list))
(mapv #(update % :tags vals))))
(defn- add-discoveries [discoveries]
@ -65,5 +65,5 @@
(defn all-tags []
(-> (r/get-all :account :tag)
(r/sorted :count :desc)
r/collection->map))
r/realm-collection->list))

View File

@ -3,7 +3,7 @@
(defn get-accounts []
(-> (r/get-all :base :account)
r/collection->map))
r/realm-collection->list))
(defn save-account [update?]
#(r/create :base :account % update?))

View File

@ -25,7 +25,7 @@
chat-id))
(defn chat-exists? [chat-id]
(r/exists? :account :chat :chat-id chat-id))
(r/exists? :account :chat {:chat-id chat-id}))
(defn add-status-message [chat-id]
;; TODO Get real status
@ -39,29 +39,6 @@
:content-type content-type-status
:outgoing false}))
(defn create-chat
([{: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 true))))
([db chat-id identities group-chat? chat-name]
(when-not (chat-exists? chat-id)
(let [chat-name (or chat-name
(get-chat-name chat-id identities))
_ (log/debug "creating chat" chat-name)]
(r/write :account
(fn []
(let [contacts (mapv (fn [ident]
{:identity ident}) identities)]
(r/create :account :chat
{:chat-id chat-id
:is-active true
:name chat-name
:group-chat group-chat?
:timestamp (timestamp)
:contacts contacts
:last-message-id ""}))))
(add-status-message chat-id)))))
(defn chat-contacts [chat-id]
(-> (r/get-by-field :account :chat :chat-id chat-id)
(r/single)
@ -93,7 +70,7 @@
(defn chats-list []
(-> (r/get-all :account :chat)
(r/sorted :timestamp :desc)
r/collection->map
r/realm-collection->list
normalize-contacts))
(defn chat-by-id [chat-id]
@ -101,6 +78,37 @@
(r/single-cljs)
(r/list-to-array :contacts)))
(defn update-chat [{:keys [last-message-id chat-id] :as chat}]
(let [{old-chat-id :chat-id
:as old-chat} (chat-by-id chat-id)]
(when old-chat-id
(let [chat (-> (merge old-chat chat)
(assoc chat :last-message-id (or last-message-id "")))]
(r/write :account #(r/create :account :chat chat true))))))
(defn create-chat
([{: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 true))))
([db chat-id identities group-chat? chat-name]
(when-not (chat-exists? chat-id)
(let [chat-name (or chat-name
(get-chat-name chat-id identities))
_ (log/debug "creating chat" chat-name)]
(r/write :account
(fn []
(let [contacts (mapv (fn [ident]
{:identity ident}) identities)]
(r/create :account :chat
{:chat-id chat-id
:is-active true
:name chat-name
:group-chat group-chat?
:timestamp (timestamp)
:contacts contacts
:last-message-id ""}))))
(add-status-message chat-id)))))
(defn chat-add-participants [chat-id identities]
(r/write :account
(fn []

View File

@ -8,7 +8,7 @@
(defn get-contacts []
(-> (r/get-all :account :contact)
(r/sorted :name :asc)
r/collection->map))
r/realm-collection->list))
(defn get-contact [id]
(r/get-one-by-field :account :contact :whisper-identity id))

View File

@ -17,6 +17,13 @@
[s]
(keywordize-keys (apply hash-map (split s #"[;=]"))))
(defn- user-statuses-to-map
[user-statuses]
(->> (vals user-statuses)
(mapv (fn [{:keys [whisper-identity] :as status}]
[whisper-identity status]))
(into {})))
(def default-values
{:outgoing false
:to nil
@ -26,10 +33,9 @@
(defn save-message
;; todo remove chat-id parameter
[chat-id {:keys [delivery-status message-id content]
:or {delivery-status :sending}
[chat-id {:keys [message-id content]
:as message}]
(when-not (r/exists? :account :message :message-id message-id)
(when-not (r/exists? :account :message {:message-id message-id})
(r/write :account
(fn []
(let [content' (if (string? content)
@ -39,7 +45,6 @@
message
{:chat-id chat-id
:content content'
:delivery-status delivery-status
:timestamp (timestamp)})]
(r/create :account :message message' true))))))
@ -54,26 +59,29 @@
(->> (-> (r/get-by-field :account :message :chat-id chat-id)
(r/sorted :timestamp :desc)
(r/page from (+ from c/default-number-of-messages))
(r/collection->map))
(r/realm-collection->list))
(mapv #(update % :user-statuses user-statuses-to-map))
(into '())
reverse
(keep (fn [{:keys [content-type preview] :as message}]
(if (command-type? content-type)
(-> message
(update :content str-to-map)
(assoc :rendered-preview
(when preview
(generate-hiccup (read-string preview)))))
message))))))
(if (command-type? content-type)
(-> message
(update :content str-to-map)
(assoc :rendered-preview
(when preview
(generate-hiccup (read-string preview)))))
message))))))
(defn update-message! [{:keys [message-id] :as message}]
(r/write :account
(fn []
(when (r/exists? :account :message :message-id message-id)
(r/create :account :message message true)))))
(when (r/exists? :account :message {:message-id message-id})
(let [message (update message :user-statuses vals)]
(r/create :account :message message true))))))
(defn get-message [id]
(r/get-one-by-field :account :message :message-id id))
(some-> (r/get-one-by-field :account :message :message-id id)
(update :user-statuses user-statuses-to-map)))
(defn get-last-message [chat-id]
(-> (r/get-by-field :account :message :chat-id chat-id)

View File

@ -15,7 +15,7 @@
[:status :sent]
[:status :failed]])
(r/sorted :timestamp :desc)
(r/collection->map))]
(r/realm-collection->list))]
(->> collection
(map (fn [{:keys [message-id] :as message}]
(let [message (-> message

View File

@ -3,7 +3,7 @@
(defn get-requests []
(-> (r/get-all :account :request)
r/collection->map))
r/realm-collection->list))
(defn create-request [request]
(r/create :account :request request true))

View File

@ -168,8 +168,8 @@
(defn delete [schema obj]
(.delete (realm schema) obj))
(defn exists? [schema schema-name field value]
(pos? (.-length (get-by-field schema schema-name field value))))
(defn exists? [schema schema-name fields]
(pos? (.-length (get-by-fields schema schema-name :and fields))))
(defn get-count [objs]
(.-length objs))
@ -177,7 +177,7 @@
(defn get-list [schema schema-name]
(vals (js->clj (.objects (realm schema) (to-string schema-name)) :keywordize-keys true)))
(defn collection->map [collection]
(defn realm-collection->list [collection]
(-> (.map collection (fn [object _ _] object))
(js->clj :keywordize-keys true)))

View File

@ -52,30 +52,38 @@
:primaryKey :key
:properties {:key "string"
:value "string"}}
{:name :user-status
:primaryKey :id
:properties {:id "string"
:whisper-identity {:type "string"
:default ""}
:status "string"}}
{: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"
: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}}}
: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"}}}
{:name :pending-message
:primaryKey :message-id
:properties {:message-id "string"

View File

@ -16,7 +16,7 @@
(r/single-cljs)
(r/decode-value)))
(contains-key? [_ key]
(r/exists? schema :kv-store :key key))
(r/exists? schema :kv-store {:key key}))
(delete [_ key]
(r/write schema
(fn []

View File

@ -15,7 +15,8 @@
[status-im.models.protocol :refer [update-identity
set-initialized]]
[status-im.constants :refer [text-content-type]]
[status-im.i18n :refer [label]]))
[status-im.i18n :refer [label]]
[status-im.utils.random :as random]))
(register-handler :initialize-protocol
(u/side-effect!
@ -104,49 +105,54 @@
(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 [_ [_ _ message-id]]
(messages/update-message! {:message-id message-id
:delivery-status status})))
(defn save-message-status! [status]
(fn [_ [_ {:keys [message-id whisper-identity]}]]
(when-let [message (messages/get-message message-id)]
(let [message (if whisper-identity
(update-in message
[:user-statuses whisper-identity]
(fn [{old-status :status}]
{:id (random/id)
:whisper-identity whisper-identity
:status (if (= (keyword old-status) :seen)
old-status
status)}))
(assoc message :message-status status))]
(messages/update-message! message)))))
(defn update-message-status [status]
(fn [db [_ chat-id message-id]]
(let [current-status (get-in db [:message-status chat-id message-id])]
(fn [db [_ {:keys [message-id whisper-identity]}]]
(let [db-key (if whisper-identity
[:message-user-statuses message-id whisper-identity]
[:message-statuses message-id])
current-status (get-in db db-key)]
(if-not (= :seen current-status)
(assoc-in db [:message-status chat-id message-id] status)
(assoc-in db db-key {:whisper-identity whisper-identity
:status status})
db))))
(register-handler :message-delivered
(after (update-message! :delivered))
(update-message-status :delivered))
(register-handler :message-failed
(after (update-message! :failed))
(after (save-message-status! :failed))
(update-message-status :failed))
(register-handler :message-sent
(after (update-message! :sent))
(after (save-message-status! :sent))
(update-message-status :sent))
(register-handler :message-delivered
(after (save-message-status! :delivered))
(update-message-status :delivered))
(register-handler :message-seen
[(after (update-message! :seen))
(after (fn [_ [_ chat-id]]
[(after (save-message-status! :seen))
(after (fn [_ [_ {:keys [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)))
(u/side-effect!
(fn [_ [_ pending-message]]
(pending-messages/upsert-pending-message! pending-message))))
(register-handler :pending-message-remove
(u/side-effect!

View File

@ -23,14 +23,18 @@
:to to)]))
:contact-request (let [{:keys [from payload]} event]
(dispatch [:contact-request-received (assoc payload :from from)]))
: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]))
:message-delivered (let [{:keys [from message-id]} event]
(dispatch [:message-delivered {:whisper-identity from
:message-id message-id}]))
:message-seen (let [{:keys [from message-id]} event]
(dispatch [:message-seen {:whisper-identity from
:message-id message-id}]))
:message-failed (let [{:keys [chat-id message-id]} event]
(dispatch [:message-failed {:chat-id chat-id
:message-id message-id}]))
:message-sent (let [{:keys [chat-id message-id] :as data} event]
(dispatch [:message-sent {:chat-id chat-id
:message-id message-id}]))
:user-discovery-keypair (let [{:keys [from]} event]
(dispatch [:contact-keypair-received from]))
:pending-message-upsert (let [{message :message} event]