mirror of
https://github.com/status-im/status-mobile.git
synced 2025-02-21 04:49:10 +00:00
messages improvements
This commit is contained in:
parent
f572f5ef5e
commit
25d5672355
@ -3,7 +3,6 @@
|
||||
[taoensso.timbre :as log]
|
||||
[status-im.multiaccounts.model :as multiaccounts.model]
|
||||
[status-im.transport.filters.core :as transport.filters]
|
||||
[status-im.contact.core :as contact.core]
|
||||
[status-im.data-store.chats :as chats-store]
|
||||
[status-im.data-store.messages :as messages-store]
|
||||
[status-im.ethereum.json-rpc :as json-rpc]
|
||||
@ -18,7 +17,9 @@
|
||||
[status-im.utils.types :as types]
|
||||
[status-im.add-new.db :as new-public-chat.db]
|
||||
[status-im.mailserver.topics :as mailserver.topics]
|
||||
[status-im.mailserver.constants :as mailserver.constants]))
|
||||
[status-im.mailserver.constants :as mailserver.constants]
|
||||
[status-im.chat.models.loading :as loading]
|
||||
[status-im.ui.screens.chat.state :as chat.state]))
|
||||
|
||||
(defn chats []
|
||||
(:chats (types/json->clj (js/require "./chats.js"))))
|
||||
@ -68,6 +69,12 @@
|
||||
([cofx chat-id]
|
||||
(timeline-chat? (get-chat cofx chat-id))))
|
||||
|
||||
(defn profile-chat?
|
||||
([chat]
|
||||
(:profile-public-key chat))
|
||||
([cofx chat-id]
|
||||
(profile-chat? (get-chat cofx chat-id))))
|
||||
|
||||
(defn set-chat-ui-props
|
||||
"Updates ui-props in active chat by merging provided kvs into them"
|
||||
[{:keys [current-chat-id] :as db} kvs]
|
||||
@ -165,19 +172,6 @@
|
||||
[{:keys [db] :as cofx} chat-id on-success]
|
||||
(chats-store/save-chat cofx (get-in db [:chats chat-id]) on-success))
|
||||
|
||||
(fx/defn handle-mark-all-read-successful
|
||||
{:events [::mark-all-read-successful]}
|
||||
[{:keys [db] :as cofx} chat-id]
|
||||
{:db (assoc-in db [:chats chat-id :unviewed-messages-count] 0)})
|
||||
|
||||
(fx/defn handle-mark-all-read
|
||||
{:events [:chat.ui/mark-all-read-pressed
|
||||
:chat.ui/mark-public-all-read]}
|
||||
[{:keys [db] :as cofx} chat-id]
|
||||
{::json-rpc/call [{:method (json-rpc/call-ext-method "markAllRead")
|
||||
:params [chat-id]
|
||||
:on-success #(re-frame/dispatch [::mark-all-read-successful chat-id])}]})
|
||||
|
||||
(fx/defn add-public-chat
|
||||
"Adds new public group chat to db"
|
||||
[cofx topic profile-public-key timeline?]
|
||||
@ -198,8 +192,7 @@
|
||||
:contacts #{}
|
||||
:public? true
|
||||
:might-have-join-time-messages? (get-in cofx [:db :multiaccount :use-mailservers?])
|
||||
:unviewed-messages-count 0
|
||||
:loaded-unviewed-messages-ids #{}}
|
||||
:unviewed-messages-count 0}
|
||||
nil))
|
||||
|
||||
(fx/defn clear-history
|
||||
@ -232,6 +225,23 @@
|
||||
(assoc-in [:chats chat-id :is-active] false)
|
||||
(assoc-in [:current-chat-id] nil))})
|
||||
|
||||
(fx/defn offload-messages
|
||||
{:events [:offload-messages]}
|
||||
[{:keys [db]} chat-id]
|
||||
{:db (-> db
|
||||
(update :messages dissoc chat-id)
|
||||
(update :message-lists dissoc chat-id)
|
||||
(update :pagination-info dissoc chat-id))})
|
||||
|
||||
(fx/defn close-chat
|
||||
{:events [:close-chat]}
|
||||
[{:keys [db] :as cofx} chat-id]
|
||||
(chat.state/reset-visible-item)
|
||||
(fx/merge cofx
|
||||
{:db (dissoc db :current-chat-id)}
|
||||
(offload-messages chat-id)
|
||||
(navigation/navigate-to-cofx :home {})))
|
||||
|
||||
(fx/defn remove-chat
|
||||
"Removes chat completely from app, producing all necessary effects for that"
|
||||
{:events [:chat.ui/remove-chat]}
|
||||
@ -240,43 +250,27 @@
|
||||
(mailserver/remove-gaps chat-id)
|
||||
(mailserver/remove-range chat-id)
|
||||
(deactivate-chat chat-id)
|
||||
(offload-messages chat-id)
|
||||
(clear-history chat-id true)
|
||||
(transport.filters/stop-listening chat-id)
|
||||
(when (not (= (:view-id db) :home))
|
||||
(navigation/navigate-to-cofx :home {}))))
|
||||
|
||||
(fx/defn offload-all-messages
|
||||
{:events [::offload-all-messages]}
|
||||
[{:keys [db] :as cofx}]
|
||||
(when-let [current-chat-id (:current-chat-id db)]
|
||||
{:db
|
||||
(-> db
|
||||
(dissoc :loaded-chat-id)
|
||||
(update :messages dissoc current-chat-id)
|
||||
(update :message-lists dissoc current-chat-id)
|
||||
(update :pagination-info dissoc current-chat-id))}))
|
||||
|
||||
(fx/defn preload-chat-data
|
||||
"Takes chat-id and coeffects map, returns effects necessary when navigating to chat"
|
||||
{:events [:chat.ui/preload-chat-data]}
|
||||
[{:keys [db] :as cofx} chat-id]
|
||||
(let [old-current-chat-id (:current-chat-id db)]
|
||||
(fx/merge cofx
|
||||
{:dispatch [:load-messages]}
|
||||
(when-not (= old-current-chat-id chat-id)
|
||||
(offload-all-messages))
|
||||
(fn [{:keys [db]}]
|
||||
{:db (assoc db :current-chat-id chat-id)})
|
||||
;; Group chat don't need this to load as all the loading of topics
|
||||
;; happens on membership changes
|
||||
(when-not (or (group-chat? cofx chat-id) (timeline-chat? cofx chat-id))
|
||||
(transport.filters/load-chat chat-id)))))
|
||||
(fx/merge cofx
|
||||
(when-not (or (group-chat? cofx chat-id) (timeline-chat? cofx chat-id))
|
||||
(transport.filters/load-chat chat-id))
|
||||
(loading/load-messages chat-id)))
|
||||
|
||||
(fx/defn navigate-to-chat
|
||||
"Takes coeffects map and chat-id, returns effects necessary for navigation and preloading data"
|
||||
{:events [:chat.ui/navigate-to-chat]}
|
||||
[{db :db :as cofx} chat-id]
|
||||
(fx/merge cofx
|
||||
{:db (assoc db :inactive-chat-id chat-id)}
|
||||
{:db (assoc db :current-chat-id chat-id)}
|
||||
(preload-chat-data chat-id)
|
||||
(navigation/navigate-to-cofx :chat-stack {:screen :chat})))
|
||||
|
||||
@ -287,17 +281,18 @@
|
||||
;; don't allow to open chat with yourself
|
||||
(when (not= (multiaccounts.model/current-public-key cofx) chat-id)
|
||||
(fx/merge cofx
|
||||
{:dispatch [:chat.ui/navigate-to-chat chat-id]}
|
||||
(upsert-chat {:chat-id chat-id
|
||||
:is-active true}
|
||||
nil)
|
||||
(transport.filters/load-chat chat-id)
|
||||
(navigate-to-chat chat-id))))
|
||||
|
||||
(def timeline-chat-id "@timeline70bd746ddcc12beb96b2c9d572d0784ab137ffc774f5383e50585a932080b57cca0484b259e61cecbaa33a4c98a300a")
|
||||
(transport.filters/load-chat chat-id))))
|
||||
|
||||
(defn profile-chat-topic [public-key]
|
||||
(str "@" public-key))
|
||||
|
||||
(defn my-profile-chat-topic [db]
|
||||
(profile-chat-topic (get-in db [:multiaccount :public-key])))
|
||||
|
||||
(fx/defn start-public-chat
|
||||
"Starts a new public chat"
|
||||
{:events [:chat.ui/start-public-chat]}
|
||||
@ -310,7 +305,7 @@
|
||||
(add-public-chat topic profile-public-key false)
|
||||
(transport.filters/load-chat topic)
|
||||
#(when-not dont-navigate?
|
||||
(navigate-to-chat % topic))))
|
||||
{:dispatch [:chat.ui/navigate-to-chat topic]})))
|
||||
{:utils/show-popup {:title (i18n/label :t/cant-open-public-chat)
|
||||
:content (i18n/label :t/invalid-public-chat-topic)}}))
|
||||
|
||||
@ -327,8 +322,8 @@
|
||||
(fx/defn start-timeline-chat
|
||||
"Starts a new timeline chat"
|
||||
[cofx]
|
||||
(when-not (active-chat? cofx timeline-chat-id)
|
||||
(add-public-chat cofx timeline-chat-id nil true)))
|
||||
(when-not (active-chat? cofx constants/timeline-chat-id)
|
||||
(add-public-chat cofx constants/timeline-chat-id nil true)))
|
||||
|
||||
(fx/defn disable-chat-cooldown
|
||||
"Turns off chat cooldown (protection against message spamming)"
|
||||
@ -344,17 +339,6 @@
|
||||
(i18n/label :cooldown/warning-message)
|
||||
#())))
|
||||
|
||||
(fx/defn show-profile-without-adding-contact
|
||||
{:events [:chat.ui/show-profile-without-adding-contact]}
|
||||
[{:keys [db] :as cofx} identity]
|
||||
(let [my-public-key (get-in db [:multiaccount :public-key])]
|
||||
(if (= my-public-key identity)
|
||||
(navigation/navigate-to-cofx cofx :profile-stack {:screen :my-profile})
|
||||
(fx/merge
|
||||
cofx
|
||||
{:db (assoc db :contacts/identity identity)}
|
||||
(navigation/navigate-to-cofx :profile nil)))))
|
||||
|
||||
(fx/defn mute-chat-failed
|
||||
{:events [::mute-chat-failed]}
|
||||
[{:keys [db] :as cofx} chat-id muted? error]
|
||||
@ -380,10 +364,16 @@
|
||||
|
||||
(fx/defn show-profile
|
||||
{:events [:chat.ui/show-profile]}
|
||||
[cofx identity]
|
||||
(fx/merge (assoc-in cofx [:db :contacts/identity] identity)
|
||||
(contact.core/create-contact identity)
|
||||
(navigation/navigate-to-cofx :profile nil)))
|
||||
[{:keys [db] :as cofx} identity]
|
||||
(let [my-public-key (get-in db [:multiaccount :public-key])]
|
||||
(if (= my-public-key identity)
|
||||
(navigation/navigate-to-cofx cofx :profile-stack {:screen :my-profile})
|
||||
(fx/merge
|
||||
cofx
|
||||
{:db (assoc db :contacts/identity identity)
|
||||
:dispatch [:chat.ui/preload-chat-data (profile-chat-topic identity)]}
|
||||
(start-profile-chat identity)
|
||||
(navigation/navigate-to-cofx :profile nil)))))
|
||||
|
||||
(fx/defn clear-history-pressed
|
||||
{:events [:chat.ui/clear-history-pressed]}
|
||||
@ -398,13 +388,12 @@
|
||||
|
||||
(fx/defn chat-ui-fill-gaps
|
||||
{:events [:chat.ui/fill-gaps]}
|
||||
[{:keys [db] :as cofx} gap-ids]
|
||||
(let [chat-id (:current-chat-id db)
|
||||
topics (mailserver.topics/topics-for-current-chat db)
|
||||
gaps (keep
|
||||
(fn [id]
|
||||
(get-in db [:mailserver/gaps chat-id id]))
|
||||
gap-ids)]
|
||||
[{:keys [db] :as cofx} gap-ids chat-id]
|
||||
(let [topics (mailserver.topics/topics-for-chat db chat-id)
|
||||
gaps (keep
|
||||
(fn [id]
|
||||
(get-in db [:mailserver/gaps chat-id id]))
|
||||
gap-ids)]
|
||||
(mailserver/fill-the-gap
|
||||
cofx
|
||||
{:gaps gaps
|
||||
@ -413,16 +402,14 @@
|
||||
|
||||
(fx/defn chat-ui-fetch-more
|
||||
{:events [:chat.ui/fetch-more]}
|
||||
[{:keys [db] :as cofx}]
|
||||
(let [chat-id (:current-chat-id db)
|
||||
|
||||
{:keys [lowest-request-from]}
|
||||
[{:keys [db] :as cofx} chat-id]
|
||||
(let [{:keys [lowest-request-from]}
|
||||
(get-in db [:mailserver/ranges chat-id])
|
||||
|
||||
topics (mailserver.topics/topics-for-current-chat db)
|
||||
gaps [{:id :first-gap
|
||||
:to lowest-request-from
|
||||
:from (- lowest-request-from mailserver.constants/one-day)}]]
|
||||
topics (mailserver.topics/topics-for-chat db chat-id)
|
||||
gaps [{:id :first-gap
|
||||
:to lowest-request-from
|
||||
:from (- lowest-request-from mailserver.constants/one-day)}]]
|
||||
(mailserver/fill-the-gap
|
||||
cofx
|
||||
{:gaps gaps
|
||||
|
@ -9,7 +9,8 @@
|
||||
[status-im.utils.image-processing :as image-processing]
|
||||
[taoensso.timbre :as log]
|
||||
[clojure.string :as string]
|
||||
[status-im.utils.platform :as platform]))
|
||||
[status-im.utils.platform :as platform]
|
||||
[status-im.chat.models :as chat]))
|
||||
|
||||
(def maximum-image-size-px 2000)
|
||||
|
||||
@ -55,24 +56,24 @@
|
||||
|
||||
(re-frame/reg-fx
|
||||
::chat-open-image-picker-camera
|
||||
(fn []
|
||||
(fn [current-chat-id]
|
||||
(react/show-image-picker-camera
|
||||
#(re-frame/dispatch [:chat.ui/image-captured (.-path %)]) {})))
|
||||
#(re-frame/dispatch [:chat.ui/image-captured current-chat-id (.-path %)]) {})))
|
||||
|
||||
(re-frame/reg-fx
|
||||
::chat-open-image-picker
|
||||
(fn []
|
||||
(fn [chat-id]
|
||||
(react/show-image-picker
|
||||
(fn [^js images]
|
||||
;; NOTE(Ferossgp): Because we can't highlight the already selected images inside
|
||||
;; gallery, we just clean previous state and set all newly picked images
|
||||
(when (and platform/ios? (pos? (count images)))
|
||||
(re-frame/dispatch [:chat.ui/clear-sending-images]))
|
||||
(re-frame/dispatch [:chat.ui/clear-sending-images chat-id]))
|
||||
(doseq [^js result (if platform/ios?
|
||||
(take config/max-images-batch images)
|
||||
[images])]
|
||||
(resize-and-call (.-path result)
|
||||
#(re-frame/dispatch [:chat.ui/image-selected (result->id result) %]))))
|
||||
#(re-frame/dispatch [:chat.ui/image-selected chat-id (result->id result) %]))))
|
||||
;; NOTE(Ferossgp): On android you cannot set max limit on images, when a user
|
||||
;; selects too many images the app crashes.
|
||||
{:media-type "photo"
|
||||
@ -80,10 +81,10 @@
|
||||
|
||||
(re-frame/reg-fx
|
||||
::image-selected
|
||||
(fn [uri]
|
||||
(fn [[uri chat-id]]
|
||||
(resize-and-call
|
||||
uri
|
||||
#(re-frame/dispatch [:chat.ui/image-selected uri %]))))
|
||||
#(re-frame/dispatch [:chat.ui/image-selected chat-id uri %]))))
|
||||
|
||||
(re-frame/reg-fx
|
||||
::camera-roll-get-photos
|
||||
@ -97,12 +98,12 @@
|
||||
|
||||
(fx/defn image-captured
|
||||
{:events [:chat.ui/image-captured]}
|
||||
[{:keys [db]} uri]
|
||||
(let [current-chat-id (:current-chat-id db)
|
||||
[{:keys [db]} chat-id uri]
|
||||
(let [current-chat-id (or chat-id (:current-chat-id db))
|
||||
images (get-in db [:chat/inputs current-chat-id :metadata :sending-image])]
|
||||
(when (and (< (count images) config/max-images-batch)
|
||||
(not (get images uri)))
|
||||
{::image-selected uri})))
|
||||
{::image-selected [uri current-chat-id]})))
|
||||
|
||||
(fx/defn camera-roll-get-photos
|
||||
{:events [:chat.ui/camera-roll-get-photos]}
|
||||
@ -116,20 +117,24 @@
|
||||
|
||||
(fx/defn clear-sending-images
|
||||
{:events [:chat.ui/clear-sending-images]}
|
||||
[{:keys [db]}]
|
||||
(let [current-chat-id (:current-chat-id db)]
|
||||
{:db (update-in db [:chat/inputs current-chat-id :metadata] assoc :sending-image {})}))
|
||||
[{:keys [db]} current-chat-id]
|
||||
{:db (update-in db [:chat/inputs current-chat-id :metadata] assoc :sending-image {})})
|
||||
|
||||
(fx/defn cancel-sending-image
|
||||
{:events [:chat.ui/cancel-sending-image]}
|
||||
[cofx]
|
||||
(clear-sending-images cofx))
|
||||
[{:keys [db] :as cofx} chat-id]
|
||||
(let [current-chat-id (or chat-id (:current-chat-id db))]
|
||||
(clear-sending-images cofx current-chat-id)))
|
||||
|
||||
(fx/defn cancel-sending-image-timeline
|
||||
{:events [:chat.ui/cancel-sending-image-timeline]}
|
||||
[{:keys [db] :as cofx}]
|
||||
(cancel-sending-image cofx (chat/my-profile-chat-topic db)))
|
||||
|
||||
(fx/defn image-selected
|
||||
{:events [:chat.ui/image-selected]}
|
||||
[{:keys [db]} original uri]
|
||||
(let [current-chat-id (:current-chat-id db)]
|
||||
{:db (update-in db [:chat/inputs current-chat-id :metadata :sending-image original] merge {:uri uri})}))
|
||||
[{:keys [db]} current-chat-id original uri]
|
||||
{:db (update-in db [:chat/inputs current-chat-id :metadata :sending-image original] merge {:uri uri})})
|
||||
|
||||
(fx/defn image-unselected
|
||||
{:events [:chat.ui/image-unselected]}
|
||||
@ -139,31 +144,46 @@
|
||||
|
||||
(fx/defn chat-open-image-picker
|
||||
{:events [:chat.ui/open-image-picker]}
|
||||
[{:keys [db]}]
|
||||
(let [current-chat-id (:current-chat-id db)
|
||||
[{:keys [db]} chat-id]
|
||||
(let [current-chat-id (or chat-id (:current-chat-id db))
|
||||
images (get-in db [:chat/inputs current-chat-id :metadata :sending-image])]
|
||||
(when (< (count images) config/max-images-batch)
|
||||
{::chat-open-image-picker nil})))
|
||||
{::chat-open-image-picker current-chat-id})))
|
||||
|
||||
(fx/defn chat-open-image-picker-timeline
|
||||
{:events [:chat.ui/open-image-picker-timeline]}
|
||||
[{:keys [db] :as cofx}]
|
||||
(chat-open-image-picker cofx (chat/my-profile-chat-topic db)))
|
||||
|
||||
(fx/defn chat-show-image-picker-camera
|
||||
{:events [:chat.ui/show-image-picker-camera]}
|
||||
[{:keys [db]}]
|
||||
(let [current-chat-id (:current-chat-id db)
|
||||
[{:keys [db]} chat-id]
|
||||
(let [current-chat-id (or chat-id (:current-chat-id db))
|
||||
images (get-in db [:chat/inputs current-chat-id :metadata :sending-image])]
|
||||
(when (< (count images) config/max-images-batch)
|
||||
{::chat-open-image-picker-camera nil})))
|
||||
{::chat-open-image-picker-camera current-chat-id})))
|
||||
|
||||
(fx/defn chat-show-image-picker-camera-timeline
|
||||
{:events [:chat.ui/show-image-picker-camera-timeline]}
|
||||
[{:keys [db] :as cofx}]
|
||||
(chat-show-image-picker-camera cofx (chat/my-profile-chat-topic db)))
|
||||
|
||||
(fx/defn camera-roll-pick
|
||||
{:events [:chat.ui/camera-roll-pick]}
|
||||
[{:keys [db]} uri]
|
||||
(let [current-chat-id (:current-chat-id db)
|
||||
[{:keys [db]} uri chat-id]
|
||||
(let [current-chat-id (or chat-id (:current-chat-id db))
|
||||
images (get-in db [:chat/inputs current-chat-id :metadata :sending-image])]
|
||||
(if (get-in db [:chats current-chat-id :timeline?])
|
||||
{:db (assoc-in db [:chat/inputs current-chat-id :metadata :sending-image] {})
|
||||
::image-selected uri}
|
||||
::image-selected [uri current-chat-id]}
|
||||
(when (and (< (count images) config/max-images-batch)
|
||||
(not (get images uri)))
|
||||
{::image-selected uri}))))
|
||||
{::image-selected [uri current-chat-id]}))))
|
||||
|
||||
(fx/defn camera-roll-pick-timeline
|
||||
{:events [:chat.ui/camera-roll-pick-timeline]}
|
||||
[{:keys [db] :as cofx} uri]
|
||||
(camera-roll-pick cofx uri (chat/my-profile-chat-topic db)))
|
||||
|
||||
(fx/defn save-image-to-gallery
|
||||
{:events [:chat.ui/save-image-to-gallery]}
|
||||
|
@ -28,8 +28,15 @@
|
||||
"Set input text for current-chat. Takes db and input text and cofx
|
||||
as arguments and returns new fx. Always clear all validation messages."
|
||||
{:events [:chat.ui/set-chat-input-text]}
|
||||
[{{:keys [current-chat-id] :as db} :db} new-input]
|
||||
{:db (assoc-in db [:chat/inputs current-chat-id :input-text] (text->emoji new-input))})
|
||||
[{db :db} new-input chat-id]
|
||||
(let [current-chat-id (or chat-id (:current-chat-id db))]
|
||||
{:db (assoc-in db [:chat/inputs current-chat-id :input-text] (text->emoji new-input))}))
|
||||
|
||||
(fx/defn set-timeline-input-text
|
||||
{:events [:chat.ui/set-timeline-input-text]}
|
||||
[{db :db} new-input]
|
||||
{:db (assoc-in db [:chat/inputs (chat/my-profile-chat-topic db) :input-text]
|
||||
(text->emoji new-input))})
|
||||
|
||||
(fx/defn select-mention
|
||||
{:events [:chat.ui/select-mention]}
|
||||
@ -43,7 +50,7 @@
|
||||
{:db (-> db
|
||||
(assoc-in [:chats/cursor chat-id] cursor)
|
||||
(assoc-in [:chats/mention-suggestions chat-id] nil))}
|
||||
(set-chat-input-text new-text)
|
||||
(set-chat-input-text new-text chat-id)
|
||||
;; NOTE(rasom): Some keyboards do not react on selection property passed to
|
||||
;; text input (specifically Samsung keyboard with predictive text set on).
|
||||
;; In this case, if the user continues typing after the programmatic change,
|
||||
@ -132,8 +139,8 @@
|
||||
:ens-name preferred-name})))
|
||||
|
||||
(defn build-image-messages
|
||||
[{{:keys [current-chat-id] :as db} :db} chat-id]
|
||||
(let [images (get-in db [:chat/inputs current-chat-id :metadata :sending-image])]
|
||||
[{db :db} chat-id]
|
||||
(let [images (get-in db [:chat/inputs chat-id :metadata :sending-image])]
|
||||
(mapv (fn [[_ {:keys [uri]}]]
|
||||
{:chat-id chat-id
|
||||
:content-type constants/content-type-image
|
||||
@ -141,13 +148,12 @@
|
||||
:text (i18n/label :t/update-to-see-image)})
|
||||
images)))
|
||||
|
||||
(fx/defn clean-input [{:keys [db] :as cofx}]
|
||||
(let [current-chat-id (:current-chat-id db)]
|
||||
(fx/merge cofx
|
||||
{:db (-> db
|
||||
(assoc-in [:chat/inputs current-chat-id :metadata :sending-image] nil)
|
||||
(assoc-in [:chat/inputs current-chat-id :metadata :responding-to-message] nil))}
|
||||
(set-chat-input-text nil))))
|
||||
(fx/defn clean-input [{:keys [db] :as cofx} current-chat-id]
|
||||
(fx/merge cofx
|
||||
{:db (-> db
|
||||
(assoc-in [:chat/inputs current-chat-id :metadata :sending-image] nil)
|
||||
(assoc-in [:chat/inputs current-chat-id :metadata :responding-to-message] nil))}
|
||||
(set-chat-input-text nil current-chat-id)))
|
||||
|
||||
(fx/defn send-messages [{:keys [db] :as cofx} input-text current-chat-id]
|
||||
(let [image-messages (build-image-messages cofx current-chat-id)
|
||||
@ -155,17 +161,23 @@
|
||||
messages (keep identity (conj image-messages text-message))]
|
||||
(when (seq messages)
|
||||
(fx/merge cofx
|
||||
(clean-input cofx)
|
||||
(clean-input cofx (:current-chat-id db))
|
||||
(process-cooldown)
|
||||
(chat.message/send-messages messages)))))
|
||||
|
||||
(fx/defn send-my-status-message
|
||||
"when not empty, proceed by sending text message with public key topic"
|
||||
{:events [:profile.ui/send-my-status-message]}
|
||||
[{{:keys [current-chat-id] :as db} :db :as cofx}]
|
||||
(let [{:keys [input-text]} (get-in db [:chat/inputs current-chat-id])
|
||||
chat-id (chat/profile-chat-topic (get-in db [:multiaccount :public-key]))]
|
||||
(send-messages cofx input-text chat-id)))
|
||||
[{db :db :as cofx}]
|
||||
(let [current-chat-id (chat/my-profile-chat-topic db)
|
||||
{:keys [input-text]} (get-in db [:chat/inputs current-chat-id])
|
||||
image-messages (build-image-messages cofx current-chat-id)
|
||||
text-message (build-text-message cofx input-text current-chat-id)
|
||||
messages (keep identity (conj image-messages text-message))]
|
||||
(when (seq messages)
|
||||
(fx/merge cofx
|
||||
(clean-input current-chat-id)
|
||||
(chat.message/send-messages messages)))))
|
||||
|
||||
(fx/defn send-audio-message
|
||||
[cofx audio-path duration current-chat-id]
|
||||
|
@ -1,27 +1,26 @@
|
||||
(ns status-im.chat.models.loading
|
||||
(:require [re-frame.core :as re-frame]
|
||||
[status-im.constants :as constants]
|
||||
[status-im.ui.screens.chat.state :as chat.state]
|
||||
[status-im.data-store.chats :as data-store.chats]
|
||||
[status-im.data-store.messages :as data-store.messages]
|
||||
[status-im.mailserver.core :as mailserver]
|
||||
[status-im.utils.fx :as fx]
|
||||
[status-im.chat.models.reactions :as reactions]
|
||||
[status-im.chat.models.message-list :as message-list]
|
||||
[taoensso.timbre :as log]
|
||||
[status-im.chat.models.message-seen :as message-seen]
|
||||
[status-im.chat.models.mentions :as mentions]))
|
||||
[status-im.ethereum.json-rpc :as json-rpc]
|
||||
[clojure.string :as string]))
|
||||
|
||||
(defn cursor->clock-value
|
||||
[^js cursor]
|
||||
(js/parseInt (.substring cursor 51 64)))
|
||||
|
||||
(defn clock-value->cursor [clock-value]
|
||||
(str "000000000000000000000000000000000000000000000000000" clock-value "0x0000000000000000000000000000000000000000000000000000000000000000"))
|
||||
(str "000000000000000000000000000000000000000000000000000"
|
||||
clock-value
|
||||
"0x0000000000000000000000000000000000000000000000000000000000000000"))
|
||||
|
||||
(fx/defn update-chats-in-app-db
|
||||
{:events [:chats-list/load-success]}
|
||||
[{:keys [db] :as cofx} new-chats]
|
||||
[{:keys [db]} new-chats]
|
||||
(let [old-chats (:chats db)
|
||||
chats (reduce (fn [acc {:keys [chat-id] :as chat}]
|
||||
(assoc acc chat-id chat))
|
||||
@ -31,35 +30,13 @@
|
||||
{:db (assoc db :chats chats
|
||||
:chats/loading? false)}))
|
||||
|
||||
(fx/defn handle-chat-visibility-changed
|
||||
{:events [:chat.ui/message-visibility-changed]}
|
||||
[{:keys [db] :as cofx} ^js event]
|
||||
(let [^js viewable-items (.-viewableItems event)
|
||||
^js last-element (aget viewable-items (dec (.-length viewable-items)))]
|
||||
(when last-element
|
||||
(let [last-element-clock-value (:clock-value (.-item last-element))
|
||||
chat-id (:chat-id (.-item last-element))]
|
||||
(when (and last-element-clock-value
|
||||
(get-in db [:pagination-info chat-id :messages-initialized?]))
|
||||
(let [new-messages (reduce-kv (fn [acc message-id {:keys [clock-value] :as v}]
|
||||
(if (<= last-element-clock-value clock-value)
|
||||
(assoc acc message-id v)
|
||||
acc))
|
||||
{}
|
||||
(get-in db [:messages chat-id]))]
|
||||
{:db (-> db
|
||||
(assoc-in [:messages chat-id] new-messages)
|
||||
(assoc-in [:pagination-info chat-id] {:all-loaded? false
|
||||
:messages-initialized? true
|
||||
:cursor (clock-value->cursor last-element-clock-value)})
|
||||
(assoc-in [:message-lists chat-id] (message-list/add-many nil (vals new-messages))))}))))))
|
||||
|
||||
(fx/defn initialize-chats
|
||||
"Initialize persisted chats on startup"
|
||||
[cofx]
|
||||
(data-store.chats/fetch-chats-rpc cofx {:on-success
|
||||
#(re-frame/dispatch
|
||||
[:chats-list/load-success %])}))
|
||||
|
||||
(fx/defn handle-failed-loading-messages
|
||||
{:events [::failed-loading-messages]}
|
||||
[{:keys [db]} current-chat-id _ err]
|
||||
@ -67,104 +44,88 @@
|
||||
(when current-chat-id
|
||||
{:db (assoc-in db [:pagination-info current-chat-id :loading-messages?] false)}))
|
||||
|
||||
(fx/defn handle-mark-all-read-successful
|
||||
{:events [::mark-all-read-successful]}
|
||||
[{:keys [db]} chat-id]
|
||||
{:db (assoc-in db [:chats chat-id :unviewed-messages-count] 0)})
|
||||
|
||||
(fx/defn handle-mark-all-read
|
||||
{:events [:chat.ui/mark-all-read-pressed :chat/mark-all-as-read]}
|
||||
[_ chat-id]
|
||||
{::json-rpc/call [{:method (json-rpc/call-ext-method "markAllRead")
|
||||
:params [chat-id]
|
||||
:on-success #(re-frame/dispatch [::mark-all-read-successful chat-id])}]})
|
||||
|
||||
(fx/defn messages-loaded
|
||||
"Loads more messages for current chat"
|
||||
{:events [::messages-loaded]}
|
||||
[{{:keys [current-chat-id] :as db} :db :as cofx}
|
||||
chat-id
|
||||
session-id
|
||||
{:keys [cursor messages]}]
|
||||
(when-not (or (nil? current-chat-id)
|
||||
(not= chat-id current-chat-id)
|
||||
(and (get-in db [:pagination-info current-chat-id :messages-initialized?])
|
||||
(not= session-id
|
||||
(get-in db [:pagination-info current-chat-id :messages-initialized?]))))
|
||||
(let [already-loaded-messages (get-in db [:messages current-chat-id])
|
||||
loaded-unviewed-messages-ids (get-in db [:chats current-chat-id :loaded-unviewed-messages-ids] #{})
|
||||
users (get-in db [:chats current-chat-id :users] {})
|
||||
[{db :db} chat-id session-id {:keys [cursor messages]}]
|
||||
(when-not (and (get-in db [:pagination-info chat-id :messages-initialized?])
|
||||
(not= session-id
|
||||
(get-in db [:pagination-info chat-id :messages-initialized?])))
|
||||
(let [already-loaded-messages (get-in db [:messages chat-id])
|
||||
;; We remove those messages that are already loaded, as we might get some duplicates
|
||||
{:keys [all-messages
|
||||
new-messages
|
||||
last-clock-value
|
||||
unviewed-message-ids
|
||||
users]}
|
||||
(reduce (fn [{:keys [last-clock-value all-messages users] :as acc}
|
||||
{:keys [clock-value seen message-id alias name identicon from]
|
||||
:as message}]
|
||||
(let [nickname (get-in db [:contacts/contacts from :nickname])]
|
||||
(cond-> acc
|
||||
(and alias (not= alias ""))
|
||||
(update :users assoc from
|
||||
(mentions/add-searchable-phrases
|
||||
{:alias alias
|
||||
:name (or name alias)
|
||||
:identicon identicon
|
||||
:public-key from
|
||||
:nickname nickname}))
|
||||
(or (nil? last-clock-value)
|
||||
(> last-clock-value clock-value))
|
||||
(assoc :last-clock-value clock-value)
|
||||
{:keys [all-messages new-messages senders]}
|
||||
(reduce (fn [{:keys [all-messages] :as acc}
|
||||
{:keys [message-id alias from]
|
||||
:as message}]
|
||||
(cond-> acc
|
||||
(and (not (string/blank? alias))
|
||||
(not (get-in db [:chats chat-id :users from])))
|
||||
(update :senders assoc from message)
|
||||
|
||||
(not seen)
|
||||
(update :unviewed-message-ids conj message-id)
|
||||
(nil? (get all-messages message-id))
|
||||
(update :new-messages conj message)
|
||||
|
||||
(nil? (get all-messages message-id))
|
||||
(update :new-messages conj message)
|
||||
:always
|
||||
(update :all-messages assoc message-id message)))
|
||||
{:all-messages already-loaded-messages
|
||||
:senders {}
|
||||
:new-messages []}
|
||||
messages)
|
||||
current-clock-value (get-in db [:pagination-info chat-id :cursor-clock-value])
|
||||
clock-value (when cursor (cursor->clock-value cursor))]
|
||||
{:dispatch [:chat/add-senders-to-chat-users (vals senders)]
|
||||
:db (-> db
|
||||
(update-in [:pagination-info chat-id :cursor-clock-value]
|
||||
#(if (and (seq cursor) (or (not %) (< clock-value %)))
|
||||
clock-value
|
||||
%))
|
||||
|
||||
:always
|
||||
(update :all-messages assoc message-id message))))
|
||||
{:all-messages already-loaded-messages
|
||||
:unviewed-message-ids loaded-unviewed-messages-ids
|
||||
:users users
|
||||
:new-messages []}
|
||||
messages)]
|
||||
(fx/merge cofx
|
||||
{:db (-> db
|
||||
(assoc-in [:pagination-info current-chat-id :cursor-clock-value] (when (seq cursor) (cursor->clock-value cursor)))
|
||||
(assoc-in [:chats current-chat-id :loaded-unviewed-messages-ids] unviewed-message-ids)
|
||||
(assoc-in [:chats current-chat-id :users] users)
|
||||
(assoc-in [:pagination-info current-chat-id :loading-messages?] false)
|
||||
(assoc-in [:messages current-chat-id] all-messages)
|
||||
(update-in [:message-lists current-chat-id] message-list/add-many new-messages)
|
||||
(assoc-in [:pagination-info current-chat-id :cursor] cursor)
|
||||
(assoc-in [:pagination-info current-chat-id :all-loaded?]
|
||||
(empty? cursor)))}
|
||||
(message-seen/mark-messages-seen current-chat-id)))))
|
||||
(update-in [:pagination-info chat-id :cursor]
|
||||
#(if (or (empty? cursor) (not current-clock-value) (< clock-value current-clock-value))
|
||||
cursor
|
||||
%))
|
||||
(assoc-in [:pagination-info chat-id :loading-messages?] false)
|
||||
(assoc-in [:messages chat-id] all-messages)
|
||||
(update-in [:message-lists chat-id] message-list/add-many new-messages)
|
||||
(assoc-in [:pagination-info chat-id :all-loaded?]
|
||||
(empty? cursor)))})))
|
||||
|
||||
(fx/defn load-more-messages
|
||||
{:events [:chat.ui/load-more-messages]}
|
||||
[{:keys [db] :as cofx}]
|
||||
(when-let [current-chat-id (:current-chat-id db)]
|
||||
(when-let [session-id (get-in db [:pagination-info current-chat-id :messages-initialized?])]
|
||||
(when-not (or (get-in db [:pagination-info current-chat-id :all-loaded?])
|
||||
(get-in db [:pagination-info current-chat-id :loading-messages?]))
|
||||
(let [cursor (get-in db [:pagination-info current-chat-id :cursor])
|
||||
load-messages-fx (data-store.messages/messages-by-chat-id-rpc
|
||||
current-chat-id
|
||||
cursor
|
||||
constants/default-number-of-messages
|
||||
#(re-frame/dispatch [::messages-loaded current-chat-id session-id %])
|
||||
#(re-frame/dispatch [::failed-loading-messages current-chat-id session-id %]))]
|
||||
(fx/merge cofx
|
||||
load-messages-fx
|
||||
(reactions/load-more-reactions cursor)
|
||||
(mailserver/load-gaps-fx current-chat-id)))))))
|
||||
[{:keys [db]} chat-id first-request]
|
||||
(when-let [session-id (get-in db [:pagination-info chat-id :messages-initialized?])]
|
||||
(when (and
|
||||
(not (get-in db [:pagination-info chat-id :all-loaded?]))
|
||||
(not (get-in db [:pagination-info chat-id :loading-messages?])))
|
||||
(let [cursor (get-in db [:pagination-info chat-id :cursor])]
|
||||
(when (or first-request cursor)
|
||||
(merge
|
||||
{:db (assoc-in db [:pagination-info chat-id :loading-messages?] true)}
|
||||
{:utils/dispatch-later [{:ms 100 :dispatch [:load-more-reactions cursor chat-id]}
|
||||
{:ms 100 :dispatch [:load-gaps chat-id]}]}
|
||||
(data-store.messages/messages-by-chat-id-rpc
|
||||
chat-id
|
||||
cursor
|
||||
constants/default-number-of-messages
|
||||
#(re-frame/dispatch [::messages-loaded chat-id session-id %])
|
||||
#(re-frame/dispatch [::failed-loading-messages chat-id session-id %]))))))))
|
||||
|
||||
(fx/defn load-messages
|
||||
{:events [:load-messages]}
|
||||
[{:keys [db now] :as cofx}]
|
||||
(when-let [current-chat-id (:current-chat-id db)]
|
||||
(if-not (get-in db [:pagination-info current-chat-id :messages-initialized?])
|
||||
(do
|
||||
; reset chat first-not-visible-items state
|
||||
(chat.state/reset)
|
||||
(fx/merge cofx
|
||||
{:db (-> db
|
||||
;; We keep track of whether there's a loaded chat
|
||||
;; which will be reset only if we hit home
|
||||
(assoc :loaded-chat-id current-chat-id)
|
||||
(assoc-in [:pagination-info current-chat-id :messages-initialized?] now))}
|
||||
(message-seen/mark-messages-seen current-chat-id)
|
||||
(load-more-messages)))
|
||||
;; We mark messages as seen in case we received them while on a different tab
|
||||
(message-seen/mark-messages-seen cofx current-chat-id))))
|
||||
[{:keys [db now] :as cofx} chat-id]
|
||||
(when-not (get-in db [:pagination-info chat-id :messages-initialized?])
|
||||
(fx/merge cofx
|
||||
{:db (assoc-in db [:pagination-info chat-id :messages-initialized?] now)
|
||||
:utils/dispatch-later [{:ms 500 :dispatch [:chat.ui/mark-all-read-pressed chat-id]}]}
|
||||
(load-more-messages chat-id true))))
|
||||
|
@ -1,214 +1,188 @@
|
||||
(ns status-im.chat.models.message
|
||||
(:require [re-frame.core :as re-frame]
|
||||
[status-im.chat.models :as chat-model]
|
||||
[status-im.chat.models.loading :as chat-loading]
|
||||
(:require [status-im.chat.models :as chat-model]
|
||||
[status-im.chat.models.message-list :as message-list]
|
||||
[status-im.constants :as constants]
|
||||
[status-im.data-store.messages :as data-store.messages]
|
||||
[status-im.ethereum.json-rpc :as json-rpc]
|
||||
[status-im.multiaccounts.model :as multiaccounts.model]
|
||||
[status-im.transport.message.protocol :as protocol]
|
||||
[status-im.ui.screens.chat.state :as view.state]
|
||||
[status-im.utils.fx :as fx]
|
||||
[taoensso.timbre :as log]
|
||||
[status-im.chat.models.mentions :as mentions]
|
||||
[clojure.string :as string]))
|
||||
[clojure.string :as string]
|
||||
[status-im.contact.db :as contact.db]
|
||||
[status-im.utils.types :as types]
|
||||
[status-im.ui.screens.chat.state :as view.state]
|
||||
[status-im.chat.models.loading :as chat.loading]
|
||||
[status-im.utils.platform :as platform]))
|
||||
|
||||
(defn- prepare-message
|
||||
[message current-chat?]
|
||||
(cond-> message
|
||||
current-chat?
|
||||
(assoc :seen true)))
|
||||
(defn- message-loaded?
|
||||
[db chat-id message-id]
|
||||
(get-in db [:messages chat-id message-id]))
|
||||
|
||||
(defn- earlier-than-deleted-at?
|
||||
[db chat-id clock-value]
|
||||
(>= (get-in db [:chats chat-id :deleted-at-clock-value]) clock-value))
|
||||
|
||||
(defn add-timeline-message [acc chat-id message-id message]
|
||||
(-> acc
|
||||
(update-in [:db :messages chat-id] assoc message-id message)
|
||||
(update-in [:db :message-lists chat-id] message-list/add message)))
|
||||
|
||||
;;TODO this is too expensive, probably we could mark message somehow and just hide it in the UI
|
||||
(fx/defn rebuild-message-list
|
||||
[{:keys [db]} chat-id]
|
||||
{:db (assoc-in db [:message-lists chat-id]
|
||||
(message-list/add-many nil (vals (get-in db [:messages chat-id]))))})
|
||||
|
||||
(fx/defn hidden-message-marked-as-seen
|
||||
{:events [::hidden-message-marked-as-seen]}
|
||||
[{:keys [db] :as cofx} chat-id _ hidden-message-count]
|
||||
(when (= 1 hidden-message-count)
|
||||
{:db (update-in db [:chats chat-id]
|
||||
update
|
||||
:unviewed-messages-count dec)}))
|
||||
(fx/defn hide-message
|
||||
(defn hide-message
|
||||
"Hide chat message, rebuild message-list"
|
||||
[{:keys [db] :as cofx} chat-id {:keys [seen message-id]}]
|
||||
(fx/merge cofx
|
||||
{:db (update-in db [:messages chat-id] dissoc message-id)}
|
||||
(data-store.messages/mark-messages-seen chat-id [message-id] #(re-frame/dispatch [::hidden-message-marked-as-seen %1 %2 %3]))
|
||||
(rebuild-message-list chat-id)))
|
||||
[{:keys [db]} chat-id message-id]
|
||||
;;TODO this is too expensive, probably we could mark message somehow and just hide it in the UI
|
||||
(rebuild-message-list {:db (update-in db [:messages chat-id] dissoc message-id)} chat-id))
|
||||
|
||||
(fx/defn add-message
|
||||
[{:keys [db] :as cofx}
|
||||
{{:keys [chat-id message-id replace timestamp from] :as message} :message
|
||||
:keys [seen-by-user?]}]
|
||||
(let [current-public-key (multiaccounts.model/current-public-key cofx)
|
||||
message-to-be-removed (when replace
|
||||
(get-in db [:messages chat-id replace]))
|
||||
prepared-message (prepare-message message seen-by-user?)]
|
||||
(fx/merge cofx
|
||||
(when message-to-be-removed
|
||||
(hide-message chat-id message-to-be-removed))
|
||||
(fn [{:keys [db]}]
|
||||
{:db (cond-> (-> db
|
||||
;; We should not be always adding to the list, as it does not make sense
|
||||
;; if the chat has not been initialized, but run into
|
||||
;; some troubles disabling it, so next time
|
||||
(update-in [:messages chat-id] assoc message-id prepared-message)
|
||||
(update-in [:message-lists chat-id] message-list/add prepared-message))
|
||||
(and (not seen-by-user?)
|
||||
(not= from current-public-key)
|
||||
(not (get-in db [:chats chat-id :profile-public-key]))
|
||||
(not (get-in db [:chats chat-id :timeline?])))
|
||||
(update-in [:chats chat-id :loaded-unviewed-messages-ids]
|
||||
(fnil conj #{}) message-id))}))))
|
||||
(fx/defn join-times-messages-checked
|
||||
"The key :might-have-join-time-messages? in public chats signals that
|
||||
the public chat is freshly (re)created and requests for messages to the
|
||||
mailserver for the topic has not completed yet. Likewise, the key
|
||||
:join-time-mail-request-id is associated a little bit after, to signal that
|
||||
the request to mailserver was a success. When request is signalled complete
|
||||
by mailserver, corresponding event :chat.ui/join-times-messages-checked
|
||||
dissociates these two fileds via this function, thereby signalling that the
|
||||
public chat is not fresh anymore."
|
||||
{:events [:chat/join-times-messages-checked]}
|
||||
[{:keys [db] :as cofx} chat-ids]
|
||||
(reduce (fn [acc chat-id]
|
||||
(cond-> acc
|
||||
(:might-have-join-time-messages? (chat-model/get-chat cofx chat-id))
|
||||
(update :db #(chat-model/dissoc-join-time-fields % chat-id))))
|
||||
{:db db}
|
||||
chat-ids))
|
||||
|
||||
(fx/defn add-sender-to-chat-users
|
||||
[{:keys [db]} {:keys [chat-id alias name identicon from]}]
|
||||
(when (and alias (not= alias ""))
|
||||
(let [nickname (get-in db [:contacts/contacts from :nickname])]
|
||||
{:db (update-in db [:chats chat-id :users] assoc
|
||||
from
|
||||
(mentions/add-searchable-phrases
|
||||
{:alias alias
|
||||
:name (or name alias)
|
||||
:identicon identicon
|
||||
:public-key from
|
||||
:nickname nickname}))})))
|
||||
(fx/defn add-senders-to-chat-users
|
||||
{:events [:chat/add-senders-to-chat-users]}
|
||||
[{:keys [db]} messages]
|
||||
(reduce (fn [acc {:keys [chat-id alias name identicon from]}]
|
||||
(update-in acc [:db :chats chat-id :users] assoc
|
||||
from
|
||||
(mentions/add-searchable-phrases
|
||||
{:alias alias
|
||||
:name (or name alias)
|
||||
:identicon identicon
|
||||
:public-key from
|
||||
:nickname (get-in db [:contacts/contacts from :nickname])})))
|
||||
{:db db}
|
||||
messages))
|
||||
|
||||
(fx/defn add-received-message
|
||||
[{:keys [db] :as cofx}
|
||||
{:keys [chat-id clock-value] :as message}]
|
||||
(let [{:keys [loaded-chat-id view-id current-chat-id]} db
|
||||
cursor-clock-value (get-in db [:chats current-chat-id :cursor-clock-value])
|
||||
current-chat? (= chat-id loaded-chat-id)]
|
||||
(when current-chat?
|
||||
(fx/merge
|
||||
cofx
|
||||
;; If we don't have any hidden message or the hidden message is before
|
||||
;; this one, we add the message to the UI
|
||||
(if (or (not @view.state/first-not-visible-item)
|
||||
(<= (:clock-value @view.state/first-not-visible-item)
|
||||
clock-value))
|
||||
(add-message {:message message
|
||||
:seen-by-user? (and current-chat?
|
||||
(= view-id :chat))})
|
||||
;; Not in the current view, set all-loaded to false
|
||||
;; and offload to db and update cursor if necessary
|
||||
{:db (cond-> (assoc-in db [:chats chat-id :all-loaded?] false)
|
||||
(>= clock-value cursor-clock-value)
|
||||
(update-in [:chats chat-id] assoc
|
||||
:cursor (chat-loading/clock-value->cursor clock-value)
|
||||
:cursor-clock-value clock-value))})
|
||||
(add-sender-to-chat-users message)))))
|
||||
(defn timeline-message? [db chat-id]
|
||||
(and
|
||||
(get-in db [:pagination-info constants/timeline-chat-id :messages-initialized?])
|
||||
(or
|
||||
(= chat-id (chat-model/my-profile-chat-topic db))
|
||||
(when-let [pub-key (get-in db [:chats chat-id :profile-public-key])]
|
||||
(contact.db/added? db pub-key)))))
|
||||
|
||||
(defn- message-loaded?
|
||||
[{:keys [db]} {:keys [chat-id message-id]}]
|
||||
(get-in db [:messages chat-id message-id]))
|
||||
(defn get-timeline-message [db chat-id message-js]
|
||||
(when (timeline-message? db chat-id)
|
||||
(data-store.messages/<-rpc (types/js->clj message-js))))
|
||||
|
||||
(defn- earlier-than-deleted-at?
|
||||
[{:keys [db]} {:keys [chat-id clock-value]}]
|
||||
(let [{:keys [deleted-at-clock-value]}
|
||||
(get-in db [:chats chat-id])]
|
||||
(>= deleted-at-clock-value clock-value)))
|
||||
(defn add-message [{:keys [db] :as acc} message-js chat-id message-id cursor-clock-value]
|
||||
(let [{:keys [alias replace from clock-value] :as message}
|
||||
(data-store.messages/<-rpc (types/js->clj message-js))]
|
||||
(if (message-loaded? db chat-id message-id)
|
||||
;; If the message is already loaded, it means it's an update, that
|
||||
;; happens when a message that was missing a reply had the reply
|
||||
;; coming through, in which case we just insert the new message
|
||||
(assoc-in acc [:db :messages chat-id message-id] message)
|
||||
(cond-> acc
|
||||
;;add new message to db
|
||||
:always
|
||||
(update-in [:db :messages chat-id] assoc message-id message)
|
||||
:always
|
||||
(update-in [:db :message-lists chat-id] message-list/add message)
|
||||
|
||||
(fx/defn update-unviewed-count
|
||||
[{:keys [db] :as cofx} {:keys [chat-id from message-type message-id new?]}]
|
||||
(when-not (= message-type constants/message-type-private-group-system-message)
|
||||
(let [{:keys [current-chat-id view-id]} db
|
||||
chat-view? (= :chat view-id)
|
||||
current-count (get-in db [:chats chat-id :unviewed-messages-count])]
|
||||
(cond
|
||||
(= from (multiaccounts.model/current-public-key cofx))
|
||||
;; nothing to do
|
||||
nil
|
||||
(or (not cursor-clock-value) (< clock-value cursor-clock-value))
|
||||
(update-in [:db :pagination-info chat-id] assoc
|
||||
:cursor (chat.loading/clock-value->cursor clock-value)
|
||||
:cursor-clock-value clock-value)
|
||||
|
||||
(and chat-view? (= current-chat-id chat-id))
|
||||
(fx/merge cofx
|
||||
(data-store.messages/mark-messages-seen current-chat-id [message-id] nil))
|
||||
;;conj sender for add-sender-to-chat-users
|
||||
(and (not (string/blank? alias))
|
||||
(not (get-in db [:chats chat-id :users from])))
|
||||
(update :senders assoc from message)
|
||||
|
||||
new?
|
||||
{:db (update-in db [:chats chat-id]
|
||||
assoc
|
||||
:unviewed-messages-count (inc current-count))}))))
|
||||
(not (string/blank? replace))
|
||||
;;TODO this is expensive
|
||||
(hide-message chat-id replace)))))
|
||||
|
||||
(fx/defn check-for-incoming-tx
|
||||
[cofx {{:keys [transaction-hash]} :command-parameters}]
|
||||
(when (and transaction-hash
|
||||
(not (string/blank? transaction-hash)))
|
||||
;; NOTE(rasom): dispatch later is needed because of circular dependency
|
||||
{:dispatch-later
|
||||
[{:dispatch [:watch-tx transaction-hash]
|
||||
:ms 20}]}))
|
||||
(defn reduce-js-messages [{:keys [db] :as acc} ^js message-js]
|
||||
(let [chat-id (.-localChatId message-js)
|
||||
clock-value (.-clock message-js)
|
||||
message-id (.-id message-js)
|
||||
current-chat-id (:current-chat-id db)
|
||||
cursor-clock-value (get-in db [:pagination-info current-chat-id :cursor-clock-value])]
|
||||
;;ignore not opened chats and earlier clock
|
||||
(if (and (get-in db [:pagination-info chat-id :messages-initialized?])
|
||||
;;TODO why do we need this ?
|
||||
(not (earlier-than-deleted-at? db chat-id clock-value)))
|
||||
(if (or (not @view.state/first-not-visible-item)
|
||||
(<= (:clock-value @view.state/first-not-visible-item)
|
||||
clock-value))
|
||||
(add-message acc message-js chat-id message-id cursor-clock-value)
|
||||
;; Not in the current view, set all-loaded to false
|
||||
;; and offload to db and update cursor if necessary
|
||||
;;TODO if we'll offload messages , it will conflict with end reached, so probably if we reached the end of visible area,
|
||||
;; we need to drop other messages with (< clock-value cursor-clock-value) from response-js so we don't update
|
||||
;; :cursor-clock-value because it will be changed when we loadMore message
|
||||
{:db (cond-> (assoc-in db [:pagination-info chat-id :all-loaded?] false)
|
||||
(> clock-value cursor-clock-value)
|
||||
;;TODO cut older messages from messages-list
|
||||
(update-in [:pagination-info chat-id] assoc
|
||||
:cursor (chat.loading/clock-value->cursor clock-value)
|
||||
:cursor-clock-value clock-value))})
|
||||
acc)))
|
||||
|
||||
(fx/defn receive-one
|
||||
{:events [::receive-one]}
|
||||
[{:keys [db] :as cofx} {:keys [message-id chat-id] :as message}]
|
||||
(fx/merge cofx
|
||||
;;If its a profile updates we want to add this message to the timeline as well
|
||||
#(when (get-in cofx [:db :chats chat-id :profile-public-key])
|
||||
{:dispatch-n [[::receive-one (assoc message :chat-id chat-model/timeline-chat-id)]]})
|
||||
#(when-not (earlier-than-deleted-at? cofx message)
|
||||
(if (message-loaded? cofx message)
|
||||
;; If the message is already loaded, it means it's an update, that
|
||||
;; happens when a message that was missing a reply had the reply
|
||||
;; coming through, in which case we just insert the new message
|
||||
{:db (assoc-in db [:messages chat-id message-id] message)}
|
||||
(fx/merge cofx
|
||||
(add-received-message message)
|
||||
(update-unviewed-count message)
|
||||
(chat-model/join-time-messages-checked chat-id)
|
||||
(check-for-incoming-tx message))))))
|
||||
(defn receive-many [{:keys [db]} ^js response-js]
|
||||
(let [current-chat-id (:current-chat-id db)
|
||||
messages-js ^js (.splice (.-messages response-js) 0 (if platform/low-device? 3 10))
|
||||
{:keys [db chats senders]}
|
||||
(reduce reduce-js-messages
|
||||
{:db db :chats #{} :senders {} :transactions #{}}
|
||||
messages-js)]
|
||||
;;we want to render new messages as soon as possible
|
||||
;;so we dispatch later all other events which can be handled async
|
||||
{:db db
|
||||
|
||||
;;TODO currently we process every message, we need to precess them by batches
|
||||
;;or better move processing to status-go
|
||||
#_((fx/defn add-received-messages
|
||||
[{:keys [db] :as cofx} grouped-messages]
|
||||
(when-let [messages (get grouped-messages (:loaded-chat-id db))]
|
||||
(apply fx/merge cofx (map add-received-message messages))))
|
||||
:utils/dispatch-later
|
||||
(concat [{:ms 20 :dispatch [:process-response response-js]}]
|
||||
(when (and current-chat-id
|
||||
(get chats current-chat-id)
|
||||
(not (chat-model/profile-chat? {:db db} current-chat-id)))
|
||||
[{:ms 100 :dispatch [:chat/mark-all-as-read (:current-chat-id db)]}])
|
||||
(when (seq senders)
|
||||
[{:ms 100 :dispatch [:chat/add-senders-to-chat-users (vals senders)]}]))}))
|
||||
|
||||
(defn reduce-count-messages [me]
|
||||
(fn [acc chat-id messages]
|
||||
(assoc acc chat-id
|
||||
(remove #(or
|
||||
(= (:message-type %)
|
||||
constants/message-type-private-group-system-message)
|
||||
(= (:from %) me))
|
||||
messages))))
|
||||
(defn reduce-js-statuses [db ^js message-js]
|
||||
(let [chat-id (.-localChatId message-js)
|
||||
profile-initialized (get-in db [:pagination-info chat-id :messages-initialized?])
|
||||
timeline-message (timeline-message? db chat-id)]
|
||||
(if (or profile-initialized timeline-message)
|
||||
(let [{:keys [message-id] :as message} (data-store.messages/<-rpc (types/js->clj message-js))]
|
||||
(cond-> db
|
||||
profile-initialized
|
||||
(update-in [:messages chat-id] assoc message-id message)
|
||||
profile-initialized
|
||||
(update-in [:message-lists chat-id] message-list/add message)
|
||||
timeline-message
|
||||
(update-in [:messages constants/timeline-chat-id] assoc message-id message)
|
||||
timeline-message
|
||||
(update-in [:message-lists constants/timeline-chat-id] message-list/add message)))
|
||||
db)))
|
||||
|
||||
(defn reduce-chat-messages [chat-view? current-chat-id]
|
||||
(fn [acc chat-id messages]
|
||||
(if (and chat-view? (= current-chat-id chat-id))
|
||||
(data-store.messages/mark-messages-seen acc current-chat-id (map :message-id messages) nil)
|
||||
(update-in acc [:db :chats chat-id :unviewed-messages-count] + (count messages)))))
|
||||
(fx/defn process-statuses
|
||||
{:events [:process-statuses]}
|
||||
[{:keys [db]} statuses]
|
||||
{:db (reduce reduce-js-statuses db statuses)})
|
||||
|
||||
(fx/defn update-unviewed-counts
|
||||
[{:keys [db] :as cofx} grouped-messages]
|
||||
(let [{:keys [current-chat-id view-id]} db
|
||||
me (multiaccounts.model/current-public-key cofx)
|
||||
messages (reduce-kv (reduce-count-messages me)
|
||||
{}
|
||||
grouped-messages)]
|
||||
(when (seq messages)
|
||||
(reduce-kv (reduce-chat-messages (= :chat view-id) current-chat-id) {:db db} messages))))
|
||||
|
||||
(fx/defn receive [cofx messages]
|
||||
(when-let [grouped-messages
|
||||
(->> (into []
|
||||
(comp
|
||||
(map #(assoc % :chat-id (extract-chat-id cofx %)))
|
||||
(remove #(earlier-than-deleted-at? cofx %)))
|
||||
messages)
|
||||
(group-by :chat-id))]
|
||||
(when (seq grouped-messages)
|
||||
(fx/merge cofx
|
||||
(add-received-messages grouped-messages)
|
||||
(update-unviewed-counts grouped-messages)
|
||||
(chat-model/join-time-messages-checked-for-chats (keys grouped-messages)))))))
|
||||
|
||||
;;;; Send message
|
||||
(fx/defn update-db-message-status
|
||||
[{:keys [db] :as cofx} chat-id message-id status]
|
||||
(when (get-in db [:messages chat-id message-id])
|
||||
@ -242,13 +216,9 @@
|
||||
(rebuild-message-list chat-id)))
|
||||
|
||||
(fx/defn send-message
|
||||
[{:keys [db now] :as cofx} message]
|
||||
[cofx message]
|
||||
(protocol/send-chat-messages cofx [message]))
|
||||
|
||||
(fx/defn send-messages
|
||||
[{:keys [db now] :as cofx} messages]
|
||||
[cofx messages]
|
||||
(protocol/send-chat-messages cofx messages))
|
||||
|
||||
(fx/defn toggle-expand-message
|
||||
[{:keys [db]} chat-id message-id]
|
||||
{:db (update-in db [:messages chat-id message-id :expanded?] not)})
|
||||
|
@ -4,6 +4,7 @@
|
||||
["functional-red-black-tree" :as rb-tree]))
|
||||
|
||||
(defn- add-datemark [{:keys [whisper-timestamp] :as msg}]
|
||||
;;TODO this is slow
|
||||
(assoc msg :datemark (time/day-relative whisper-timestamp)))
|
||||
|
||||
(defn- add-timestamp [{:keys [whisper-timestamp] :as msg}]
|
||||
@ -132,12 +133,10 @@
|
||||
(let [^js iter (.find tree message)
|
||||
^js previous-message (when (.-hasPrev iter)
|
||||
(get-prev-element iter))
|
||||
^js next-message (when (.-hasNext iter)
|
||||
(get-next-element iter))
|
||||
^js next-message (when (.-hasNext iter)
|
||||
(get-next-element iter))
|
||||
^js message-with-pos-data (add-group-info message previous-message next-message)]
|
||||
(cond->
|
||||
(.update iter message-with-pos-data)
|
||||
|
||||
(cond-> (.update iter message-with-pos-data)
|
||||
next-message
|
||||
(-> ^js (.find next-message)
|
||||
(.update (update-next-message message-with-pos-data next-message)))
|
||||
@ -170,8 +169,7 @@
|
||||
(update-message tree prepared-message)))
|
||||
|
||||
(defn add [message-list message]
|
||||
(insert-message (or message-list (rb-tree compare-fn))
|
||||
(prepare-message message)))
|
||||
(insert-message (or message-list (rb-tree compare-fn)) (prepare-message message)))
|
||||
|
||||
(defn add-many [message-list messages]
|
||||
(reduce add
|
||||
|
@ -13,8 +13,7 @@
|
||||
{:db (update-in db [:chats chat-id] assoc
|
||||
:unviewed-messages-count (subtract-seen-messages
|
||||
unviewed-messages-count
|
||||
loaded-unviewed-messages-ids)
|
||||
:loaded-unviewed-messages-ids #{})}))
|
||||
loaded-unviewed-messages-ids))}))
|
||||
|
||||
(fx/defn mark-messages-seen
|
||||
"Marks all unviewed loaded messages as seen in particular chat"
|
||||
@ -28,11 +27,4 @@
|
||||
db
|
||||
loaded-unviewed-ids)}
|
||||
(messages-store/mark-messages-seen chat-id loaded-unviewed-ids nil)
|
||||
(update-chats-unviewed-messages-count {:chat-id chat-id})))))
|
||||
|
||||
(fx/defn ui-mark-messages-seen
|
||||
{:events [:chat.ui/mark-messages-seen]}
|
||||
[{:keys [db] :as cofx} view-id]
|
||||
(fx/merge cofx
|
||||
{:db (assoc db :view-id view-id)}
|
||||
(mark-messages-seen cofx (:current-chat-id db))))
|
||||
(update-chats-unviewed-messages-count {:chat-id chat-id})))))
|
@ -1,155 +1,135 @@
|
||||
(ns status-im.chat.models.message-test
|
||||
(:require [cljs.test :refer-macros [deftest is testing]]
|
||||
[status-im.chat.models.loading :as chat-loading]
|
||||
[status-im.chat.models.message :as message]
|
||||
[status-im.chat.models.message-list :as models.message-list]
|
||||
[status-im.ui.screens.chat.state :as view.state]
|
||||
[status-im.utils.datetime :as time]))
|
||||
[status-im.chat.models.loading :as loading]
|
||||
[status-im.utils.datetime :as time]
|
||||
[status-im.ui.screens.chat.state :as view.state]))
|
||||
|
||||
(deftest add-received-message-test
|
||||
(with-redefs [message/add-message (constantly :added)]
|
||||
(with-redefs [message/add-message #(identity %1)]
|
||||
(let [chat-id "chat-id"
|
||||
clock-value 10
|
||||
cursor-clock-value (dec clock-value)
|
||||
cursor (chat-loading/clock-value->cursor cursor-clock-value)
|
||||
cursor (loading/clock-value->cursor cursor-clock-value)
|
||||
cofx {:now 0
|
||||
:db {:loaded-chat-id chat-id
|
||||
:current-chat-id chat-id
|
||||
:all-loaded? true
|
||||
:chats {chat-id {:cursor cursor
|
||||
:cursor-clock-value cursor-clock-value}}}}
|
||||
message {:chat-id chat-id
|
||||
:clock-value clock-value
|
||||
:alias "alias"
|
||||
:name "name"
|
||||
:identicon "identicon"
|
||||
:from "from"}]
|
||||
(testing "not current-chat"
|
||||
(is (nil? (message/add-received-message
|
||||
(update cofx :db dissoc :loaded-chat-id)
|
||||
message))))
|
||||
:db {:current-chat-id chat-id
|
||||
:pagination-info {chat-id {:messages-initialized? true
|
||||
:cursor cursor
|
||||
:cursor-clock-value cursor-clock-value}}}}
|
||||
message #js {:localChatId chat-id
|
||||
:clock clock-value
|
||||
:alias "alias"
|
||||
:name "name"
|
||||
:identicon "identicon"
|
||||
:from "from"}]
|
||||
;; <- cursor
|
||||
;; <- message
|
||||
;; <- top of the chat
|
||||
(testing "there's no hidden item"
|
||||
(with-redefs [view.state/first-not-visible-item (atom nil)]
|
||||
(is (= {:db {:loaded-chat-id "chat-id",
|
||||
:current-chat-id "chat-id",
|
||||
:all-loaded? true,
|
||||
:chats
|
||||
(is (= {:db {:current-chat-id "chat-id",
|
||||
:pagination-info
|
||||
{"chat-id"
|
||||
{:cursor
|
||||
{:messages-initialized? true
|
||||
:cursor
|
||||
"00000000000000000000000000000000000000000000000000090x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
:cursor-clock-value 9,
|
||||
:users
|
||||
{"from" {:alias "alias",
|
||||
:name "name",
|
||||
:identicon "identicon",
|
||||
:public-key "from"
|
||||
:nickname nil
|
||||
:searchable-phrases ["alias" "name"]}}}}}}
|
||||
(message/add-received-message
|
||||
cofx
|
||||
message)))))
|
||||
:cursor-clock-value 9}}}}
|
||||
(dissoc (message/receive-many
|
||||
cofx
|
||||
#js {:messages (to-array [message])})
|
||||
:utils/dispatch-later)))))
|
||||
;; <- cursor
|
||||
;; <- first-hidden-item
|
||||
;; <- message
|
||||
;; <- top of the chat
|
||||
(testing "the hidden item has a clock value less than the current"
|
||||
(with-redefs [view.state/first-not-visible-item (atom {:clock-value (dec clock-value)})]
|
||||
(is (= {:db {:loaded-chat-id "chat-id",
|
||||
:current-chat-id "chat-id",
|
||||
:all-loaded? true,
|
||||
:chats
|
||||
(is (= {:db {:current-chat-id "chat-id",
|
||||
:pagination-info
|
||||
{"chat-id"
|
||||
{:cursor
|
||||
{:messages-initialized? true
|
||||
:cursor
|
||||
"00000000000000000000000000000000000000000000000000090x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
:cursor-clock-value 9,
|
||||
:users
|
||||
{"from" {:alias "alias",
|
||||
:name "name",
|
||||
:identicon "identicon",
|
||||
:public-key "from"
|
||||
:nickname nil
|
||||
:searchable-phrases ["alias" "name"]}}}}}}
|
||||
(message/add-received-message
|
||||
cofx
|
||||
message)))))
|
||||
:cursor-clock-value 9}}}}
|
||||
(dissoc (message/receive-many
|
||||
cofx
|
||||
#js {:messages (to-array [message])})
|
||||
:utils/dispatch-later)))))
|
||||
;; <- cursor
|
||||
;; <- message
|
||||
;; <- first-hidden-item
|
||||
;; <- top of the chat
|
||||
(testing "the message falls between the first-hidden-item and cursor"
|
||||
(with-redefs [view.state/first-not-visible-item (atom {:clock-value (inc clock-value)})]
|
||||
(let [result (message/add-received-message
|
||||
cofx
|
||||
message)]
|
||||
(let [result (dissoc (message/receive-many
|
||||
cofx
|
||||
#js {:messages (to-array [message])})
|
||||
:utils/dispatch-later)]
|
||||
(testing "it sets all-loaded? to false"
|
||||
(is (not (get-in result [:db :chats chat-id :all-loaded?]))))
|
||||
(is (not (get-in result [:db :pagination-info chat-id :all-loaded?]))))
|
||||
(testing "it updates cursor-clock-value & cursor"
|
||||
(is (= clock-value (get-in result [:db :chats chat-id :cursor-clock-value])))
|
||||
(is (= (chat-loading/clock-value->cursor clock-value) (get-in result [:db :chats chat-id :cursor])))))))
|
||||
(is (= clock-value (get-in result [:db :pagination-info chat-id :cursor-clock-value])))
|
||||
(is (= (loading/clock-value->cursor clock-value) (get-in result [:db :pagination-info chat-id :cursor])))))))
|
||||
;; <- message
|
||||
;; <- first-hidden-item
|
||||
;; <- top of the chat
|
||||
(testing "the message falls between the first-hidden-item and cursor is nil"
|
||||
(with-redefs [view.state/first-not-visible-item (atom {:clock-value (inc clock-value)})]
|
||||
(let [result (message/add-received-message
|
||||
(update-in cofx [:db :chats chat-id] dissoc :cursor :cursor-clock-value)
|
||||
message)]
|
||||
(let [result (dissoc (message/receive-many
|
||||
(update-in cofx [:db :pagination-info chat-id] dissoc :cursor :cursor-clock-value)
|
||||
#js {:messages (to-array [message])})
|
||||
:utils/dispatch-later)]
|
||||
(testing "it sets all-loaded? to false"
|
||||
(is (not (get-in result [:db :chats chat-id :all-loaded?]))))
|
||||
(is (not (get-in result [:db :pagination-info chat-id :all-loaded?]))))
|
||||
(testing "it updates cursor-clock-value & cursor"
|
||||
(is (= clock-value (get-in result [:db :chats chat-id :cursor-clock-value])))
|
||||
(is (= (chat-loading/clock-value->cursor clock-value) (get-in result [:db :chats chat-id :cursor])))))))
|
||||
(is (= clock-value (get-in result [:db :pagination-info chat-id :cursor-clock-value])))
|
||||
(is (= (loading/clock-value->cursor clock-value) (get-in result [:db :pagination-info chat-id :cursor])))))))
|
||||
;; <- message
|
||||
;; <- cursor
|
||||
;; <- first-hidden-item
|
||||
;; <- top of the chat
|
||||
(testing "the message falls before both the first-hidden-item and cursor"
|
||||
(with-redefs [view.state/first-not-visible-item (atom {:clock-value (inc clock-value)})]
|
||||
(let [result (message/add-received-message
|
||||
cofx
|
||||
(update message :clock-value #(- % 2)))]
|
||||
(let [message #js {:localChatId chat-id
|
||||
:clock (- clock-value 2)
|
||||
:alias "alias"
|
||||
:name "name"
|
||||
:identicon "identicon"
|
||||
:from "from"}
|
||||
result (dissoc (message/receive-many
|
||||
cofx
|
||||
#js {:messages (to-array [message])})
|
||||
:utils/dispatch-later)]
|
||||
(testing "it sets all-loaded? to false"
|
||||
(is (not (get-in result [:db :chats chat-id :all-loaded?]))))
|
||||
(is (not (get-in result [:db :pagination-info chat-id :all-loaded?]))))
|
||||
(testing "it does not update cursor-clock-value & cursor"
|
||||
(is (= cursor-clock-value (get-in result [:db :chats chat-id :cursor-clock-value])))
|
||||
(is (= cursor (get-in result [:db :chats chat-id :cursor]))))))))))
|
||||
(is (= cursor-clock-value (get-in result [:db :pagination-info chat-id :cursor-clock-value])))
|
||||
(is (= cursor (get-in result [:db :pagination-info chat-id :cursor]))))))))))
|
||||
|
||||
(deftest message-loaded?
|
||||
(testing "it returns false when it's not in loaded message"
|
||||
(is (not (#'status-im.chat.models.message/message-loaded? {:db {:chats {"a" {}}}}
|
||||
{:message-id "message-id"
|
||||
:from "a"
|
||||
:clock-value 1
|
||||
:chat-id "a"}))))
|
||||
(is (not (#'status-im.chat.models.message/message-loaded? {:messages {"a" {}}} "a" "message-id"))))
|
||||
(testing "it returns true when it's already in the loaded message"
|
||||
(is (#'status-im.chat.models.message/message-loaded? {:db
|
||||
{:messages {"a" {"message-id" {}}}}}
|
||||
{:message-id "message-id"
|
||||
:from "a"
|
||||
:clock-value 1
|
||||
:chat-id "a"}))))
|
||||
(is (#'status-im.chat.models.message/message-loaded? {:messages {"a" {"message-id" {}}}} "a" "message-id"))))
|
||||
|
||||
(deftest earlier-than-deleted-at?
|
||||
(testing "it returns true when the clock-value is the same as the deleted-clock-value in chat"
|
||||
(is (#'status-im.chat.models.message/earlier-than-deleted-at? {:db {:chats {"a" {:deleted-at-clock-value 1}}}}
|
||||
{:message-id "message-id"
|
||||
:from "a"
|
||||
:clock-value 1
|
||||
:chat-id "a"})))
|
||||
(is (#'status-im.chat.models.message/earlier-than-deleted-at?
|
||||
{:chats {"a" {:deleted-at-clock-value 1}}}
|
||||
"a"
|
||||
1)))
|
||||
(testing "it returns false when the clock-value is greater than the deleted-clock-value in chat"
|
||||
(is (not (#'status-im.chat.models.message/earlier-than-deleted-at? {:db {:chats {"a" {:deleted-at-clock-value 1}}}}
|
||||
{:message-id "message-id"
|
||||
:from "a"
|
||||
:clock-value 2
|
||||
:chat-id "a"}))))
|
||||
(is (not (#'status-im.chat.models.message/earlier-than-deleted-at?
|
||||
{:chats {"a" {:deleted-at-clock-value 1}}}
|
||||
"a"
|
||||
2))))
|
||||
|
||||
(testing "it returns true when the clock-value is less than the deleted-clock-value in chat"
|
||||
(is (#'status-im.chat.models.message/earlier-than-deleted-at? {:db {:chats {"a" {:deleted-at-clock-value 1}}}}
|
||||
{:message-id "message-id"
|
||||
:from "a"
|
||||
:clock-value 0
|
||||
:chat-id "a"}))))
|
||||
(is (#'status-im.chat.models.message/earlier-than-deleted-at?
|
||||
{:chats {"a" {:deleted-at-clock-value 1}}}
|
||||
"a"
|
||||
0))))
|
||||
|
||||
(deftest delete-message
|
||||
(with-redefs [time/day-relative (constantly "day-relative")
|
||||
|
@ -4,8 +4,7 @@
|
||||
[status-im.utils.fx :as fx]
|
||||
[taoensso.timbre :as log]
|
||||
[status-im.transport.message.protocol :as message.protocol]
|
||||
[status-im.data-store.reactions :as data-store.reactions]
|
||||
[status-im.chat.models :as chat]))
|
||||
[status-im.data-store.reactions :as data-store.reactions]))
|
||||
|
||||
(defn update-reaction [acc retracted chat-id message-id emoji-id emoji-reaction-id reaction]
|
||||
;; NOTE(Ferossgp): For a better performance, better to not keep in db all retracted reactions
|
||||
@ -23,7 +22,7 @@
|
||||
:as reaction}]
|
||||
(cond-> (update-reaction acc retracted chat-id message-id emoji-id emoji-reaction-id reaction)
|
||||
(get-in chats [chat-id :profile-public-key])
|
||||
(update-reaction retracted chat/timeline-chat-id message-id emoji-id emoji-reaction-id reaction)))
|
||||
(update-reaction retracted constants/timeline-chat-id message-id emoji-id emoji-reaction-id reaction)))
|
||||
reactions
|
||||
new-reactions)))
|
||||
|
||||
@ -39,27 +38,25 @@
|
||||
{:db (update db :reactions (process-reactions (:chats db)) reactions)}))
|
||||
|
||||
(fx/defn load-more-reactions
|
||||
[{:keys [db] :as cofx} cursor]
|
||||
(when-let [current-chat-id (:current-chat-id db)]
|
||||
(when-let [session-id (get-in db [:pagination-info current-chat-id :messages-initialized?])]
|
||||
(data-store.reactions/reactions-by-chat-id-rpc
|
||||
current-chat-id
|
||||
cursor
|
||||
constants/default-number-of-messages
|
||||
#(re-frame/dispatch [::reactions-loaded current-chat-id session-id %])
|
||||
#(log/error "failed loading reactions" current-chat-id %)))))
|
||||
{:events [:load-more-reactions]}
|
||||
[{:keys [db]} cursor chat-id]
|
||||
(when-let [session-id (get-in db [:pagination-info chat-id :messages-initialized?])]
|
||||
(data-store.reactions/reactions-by-chat-id-rpc
|
||||
chat-id
|
||||
cursor
|
||||
constants/default-number-of-messages
|
||||
#(re-frame/dispatch [::reactions-loaded chat-id session-id %])
|
||||
#(log/error "failed loading reactions" chat-id %))))
|
||||
|
||||
(fx/defn reactions-loaded
|
||||
{:events [::reactions-loaded]}
|
||||
[{{:keys [current-chat-id] :as db} :db}
|
||||
[{db :db}
|
||||
chat-id
|
||||
session-id
|
||||
reactions]
|
||||
(when-not (or (nil? current-chat-id)
|
||||
(not= chat-id current-chat-id)
|
||||
(and (get-in db [:pagination-info current-chat-id :messages-initialized?])
|
||||
(not= session-id
|
||||
(get-in db [:pagination-info current-chat-id :messages-initialized?]))))
|
||||
(when-not (and (get-in db [:pagination-info chat-id :messages-initialized?])
|
||||
(not= session-id
|
||||
(get-in db [:pagination-info chat-id :messages-initialized?])))
|
||||
(let [reactions-w-chat-id (map #(assoc % :chat-id chat-id) reactions)]
|
||||
{:db (update db :reactions (process-reactions (:chats db)) reactions-w-chat-id)})))
|
||||
|
||||
|
@ -1,3 +1,4 @@
|
||||
;;this ns is needed because of cycled deps
|
||||
(ns status-im.chat.models.transport
|
||||
(:require [status-im.utils.fx :as fx]
|
||||
[status-im.transport.message.core :as transport.message]
|
||||
@ -9,5 +10,5 @@
|
||||
(let [message (get-in db [:messages chat-id message-id])]
|
||||
(fx/merge
|
||||
cofx
|
||||
(transport.message/set-message-envelope-hash chat-id message-id (:message-type message) 1)
|
||||
(transport.message/set-message-envelope-hash chat-id message-id (:message-type message))
|
||||
(chat.message/resend-message chat-id message-id))))
|
@ -151,7 +151,6 @@
|
||||
|
||||
:messages {"status" {"4" {} "5" {} "6" {}}}
|
||||
:chats {"status" {:public? true
|
||||
:group-chat true
|
||||
:loaded-unviewed-messages-ids #{"6" "5" "4"}}
|
||||
"opened" {:loaded-unviewed-messages-ids #{}}
|
||||
"1-1" {:loaded-unviewed-messages-ids #{"6" "5" "4"}}}})
|
||||
:group-chat true}
|
||||
"opened" {}
|
||||
"1-1" {}}})
|
||||
|
@ -24,18 +24,21 @@
|
||||
{:db (dissoc db :commands/select-account)
|
||||
::json-rpc/call [{:method (json-rpc/call-ext-method "acceptRequestAddressForTransaction")
|
||||
:params [message-id address]
|
||||
:on-success #(re-frame/dispatch [:transport/message-sent % 1])}]})
|
||||
:js-response true
|
||||
:on-success #(re-frame/dispatch [:transport/message-sent %])}]})
|
||||
|
||||
(fx/defn handle-decline-request-address-for-transaction
|
||||
{:events [::decline-request-address-for-transaction]}
|
||||
[cofx message-id]
|
||||
{::json-rpc/call [{:method (json-rpc/call-ext-method "declineRequestAddressForTransaction")
|
||||
:params [message-id]
|
||||
:on-success #(re-frame/dispatch [:transport/message-sent % 1])}]})
|
||||
:js-response true
|
||||
:on-success #(re-frame/dispatch [:transport/message-sent %])}]})
|
||||
|
||||
(fx/defn handle-decline-request-transaction
|
||||
{:events [::decline-request-transaction]}
|
||||
[cofx message-id]
|
||||
{::json-rpc/call [{:method (json-rpc/call-ext-method "declineRequestTransaction")
|
||||
:params [message-id]
|
||||
:on-success #(re-frame/dispatch [:transport/message-sent % 1])}]})
|
||||
:js-response true
|
||||
:on-success #(re-frame/dispatch [:transport/message-sent %])}]})
|
||||
|
@ -175,8 +175,9 @@
|
||||
:text "Upgrade here to see an invitation to community"
|
||||
:communityId community-id
|
||||
:contentType constants/content-type-community}]
|
||||
:js-response true
|
||||
:on-success
|
||||
#(re-frame/dispatch [:transport/message-sent % 1])
|
||||
#(re-frame/dispatch [:transport/message-sent %])
|
||||
:on-failure #(log/error "failed to send a message" %)}]})
|
||||
|
||||
(fx/defn invite-users
|
||||
@ -242,7 +243,7 @@
|
||||
[{:keys [db]}]
|
||||
(let [{:keys [name description membership]} (get db :communities/create)
|
||||
my-public-key (get-in db [:multiaccount :public-key])]
|
||||
(log/error "Edit community is not yet implemented")
|
||||
(log/error "Edit community is not yet implemented")))
|
||||
;; {::json-rpc/call [{:method "wakuext_editCommunity"
|
||||
;; :params [{:identity {:display_name name
|
||||
;; :description description}
|
||||
@ -251,7 +252,7 @@
|
||||
;; :on-error #(do
|
||||
;; (log/error "failed to create community" %)
|
||||
;; (re-frame/dispatch [::failed-to-edit-community %]))}]}
|
||||
))
|
||||
|
||||
|
||||
(fx/defn create-channel
|
||||
{:events [::create-channel-confirmation-pressed]}
|
||||
|
@ -132,3 +132,5 @@
|
||||
(def ^:const keycard-integration-link "https://status.im/keycard-integration")
|
||||
|
||||
(def ^:const status-community-id "0x039b2da47552aa117a96ea8f1d4d108ba66637c7517a3c94a57b99dbb8a002eda2")
|
||||
|
||||
(def ^:const timeline-chat-id "@timeline70bd746ddcc12beb96b2c9d572d0784ab137ffc774f5383e50585a932080b57cca0484b259e61cecbaa33a4c98a300a")
|
||||
|
@ -43,7 +43,7 @@
|
||||
public-key)
|
||||
(assoc :last-updated now)
|
||||
(update :system-tags (fnil conj #{}) :contact/blocked))
|
||||
from-one-to-one-chat? (not (get-in db [:chats (:inactive-chat-id db) :group-chat]))]
|
||||
from-one-to-one-chat? (not (get-in db [:chats (:current-chat-id db) :group-chat]))]
|
||||
(fx/merge cofx
|
||||
{:db (-> db
|
||||
;; add the contact to blocked contacts
|
||||
|
@ -8,7 +8,8 @@
|
||||
[status-im.navigation :as navigation]
|
||||
[status-im.utils.fx :as fx]
|
||||
[taoensso.timbre :as log]
|
||||
[clojure.string :as string]))
|
||||
[clojure.string :as string]
|
||||
[status-im.constants :as constants]))
|
||||
|
||||
(fx/defn load-contacts
|
||||
{:events [::contacts-loaded]}
|
||||
@ -81,7 +82,8 @@
|
||||
(fnil #(conj % :contact/added) #{})))]
|
||||
(fx/merge cofx
|
||||
{:db (dissoc db :contacts/new-identity)
|
||||
:dispatch [:start-profile-chat public-key]}
|
||||
:dispatch-n [[:start-profile-chat public-key]
|
||||
[:offload-messages constants/timeline-chat-id]]}
|
||||
(upsert-contact contact)
|
||||
(send-contact-request contact)
|
||||
(mailserver/process-next-messages-request)))))
|
||||
@ -95,7 +97,7 @@
|
||||
(fnil #(disj % :contact/added) #{}))]
|
||||
(fx/merge cofx
|
||||
{:db (assoc-in db [:contacts/contacts public-key] new-contact)
|
||||
:dispatch [:chat/remove-update-chat public-key]}
|
||||
:dispatch [:offload-messages constants/timeline-chat-id]}
|
||||
(contacts-store/save-contact new-contact))))
|
||||
|
||||
(fx/defn create-contact
|
||||
|
@ -83,8 +83,7 @@
|
||||
:last-clock-value :lastClockValue
|
||||
:profile-public-key :profile})
|
||||
(dissoc :public? :group-chat :messages
|
||||
:might-have-join-time-messages?
|
||||
:loaded-unviewed-messages-ids :chat-type
|
||||
:might-have-join-time-messages? :chat-type
|
||||
:contacts :admins :members-joined)))
|
||||
|
||||
(defn <-rpc [chat]
|
||||
|
@ -16,7 +16,6 @@
|
||||
:unviewed-messages-count 2
|
||||
:is-active true
|
||||
:chat-id "chat-id"
|
||||
:loaded-unviewed-messages-ids []
|
||||
:timestamp 2}
|
||||
expected-chat {:id "chat-id"
|
||||
:color "color"
|
||||
|
@ -57,8 +57,8 @@
|
||||
limit
|
||||
on-success
|
||||
on-failure]
|
||||
{::json-rpc/call [{:method (json-rpc/call-ext-method "chatMessages")
|
||||
:params [chat-id cursor limit]
|
||||
{::json-rpc/call [{:method (json-rpc/call-ext-method "chatMessages")
|
||||
:params [chat-id cursor limit]
|
||||
:on-success (fn [result]
|
||||
(on-success (update result :messages #(map <-rpc %))))
|
||||
:on-failure on-failure}]})
|
||||
|
@ -200,7 +200,7 @@
|
||||
(str "wakuext_" method))
|
||||
|
||||
(defn call
|
||||
[{:keys [method params on-success on-error] :as arg}]
|
||||
[{:keys [method params on-success on-error js-response] :as arg}]
|
||||
(if-let [method-options (json-rpc-api method)]
|
||||
(let [params (or params [])
|
||||
{:keys [id on-result subscription?]
|
||||
@ -226,15 +226,16 @@
|
||||
(fn [response]
|
||||
(if (string/blank? response)
|
||||
(on-error {:message "Blank response"})
|
||||
(let [{:keys [error result]} (types/json->clj response)]
|
||||
(if error
|
||||
(on-error error)
|
||||
(let [response-js (types/json->js response)]
|
||||
(if (.-error response-js)
|
||||
(on-error (types/js->clj (.-error response-js)))
|
||||
(if subscription?
|
||||
(re-frame/dispatch
|
||||
[:ethereum.callback/subscription-success
|
||||
result on-success])
|
||||
(on-success (on-result result))))))))))
|
||||
|
||||
(types/js->clj (.-result response-js)) on-success])
|
||||
(on-success (on-result (if js-response
|
||||
(.-result response-js)
|
||||
(types/js->clj (.-result response-js)))))))))))))
|
||||
(log/warn "method" method "not found" arg)))
|
||||
|
||||
(defn eth-call
|
||||
|
@ -1,16 +1,9 @@
|
||||
(ns status-im.events
|
||||
(:require [re-frame.core :as re-frame]
|
||||
[status-im.chat.models :as chat]
|
||||
[status-im.chat.models.message :as chat.message]
|
||||
[status-im.chat.models.reactions :as chat.reactions]
|
||||
[status-im.data-store.chats :as data-store.chats]
|
||||
[status-im.data-store.messages :as data-store.messages]
|
||||
[status-im.data-store.reactions :as data-store.reactions]
|
||||
[status-im.group-chats.core :as group-chats]
|
||||
[status-im.i18n.i18n :as i18n]
|
||||
[status-im.mailserver.core :as mailserver]
|
||||
[status-im.multiaccounts.core :as multiaccounts]
|
||||
[status-im.transport.message.core :as transport.message]
|
||||
[status-im.ui.components.react :as react]
|
||||
[status-im.utils.fx :as fx]
|
||||
status-im.utils.logging.core
|
||||
@ -22,8 +15,6 @@
|
||||
[status-im.native-module.core :as status]
|
||||
[status-im.ui.components.permissions :as permissions]
|
||||
[status-im.utils.utils :as utils]
|
||||
[status-im.data-store.invitations :as data-store.invitations]
|
||||
[status-im.contact.db :as contact.db]
|
||||
[status-im.ethereum.json-rpc :as json-rpc]
|
||||
clojure.set
|
||||
status-im.currency.core
|
||||
@ -55,7 +46,6 @@
|
||||
status-im.mailserver.constants
|
||||
status-im.ethereum.subscriptions
|
||||
status-im.fleet.core
|
||||
status-im.chat.models.message-seen
|
||||
status-im.contact.block
|
||||
status-im.contact.core
|
||||
status-im.contact.chat
|
||||
@ -101,30 +91,6 @@
|
||||
(fn []
|
||||
(dimensions/add-event-listener)))
|
||||
|
||||
;;TODO: map fx/merge is slow, we shouldn't do it, its much better to have one fx for vector of items
|
||||
(fx/defn handle-update
|
||||
{:events [:transport/reaction-sent :transport/retraction-sent :transport/invitation-sent]}
|
||||
[cofx {:keys [chats messages emojiReactions invitations]}]
|
||||
(let [chats (map data-store.chats/<-rpc chats)
|
||||
messages (map data-store.messages/<-rpc messages)
|
||||
message-fxs (map chat.message/receive-one messages)
|
||||
emoji-reactions (map data-store.reactions/<-rpc emojiReactions)
|
||||
emoji-react-fxs (map chat.reactions/receive-one emoji-reactions)
|
||||
invitations-fxs [(group-chats/handle-invitations
|
||||
(map data-store.invitations/<-rpc invitations))]
|
||||
chat-fxs (map #(chat/ensure-chat (dissoc % :unviewed-messages-count)) chats)]
|
||||
(apply fx/merge cofx (concat chat-fxs message-fxs emoji-react-fxs invitations-fxs))))
|
||||
|
||||
(fx/defn transport-message-sent
|
||||
{:events [:transport/message-sent]}
|
||||
[cofx response messages-count]
|
||||
(let [set-hash-fxs (map (fn [{:keys [localChatId id messageType]}]
|
||||
(transport.message/set-message-envelope-hash localChatId id messageType messages-count))
|
||||
(:messages response))]
|
||||
(apply fx/merge cofx
|
||||
(conj set-hash-fxs
|
||||
(handle-update response)))))
|
||||
|
||||
(fx/defn dismiss-keyboard
|
||||
{:events [:dismiss-keyboard]}
|
||||
[_]
|
||||
@ -210,35 +176,15 @@
|
||||
[{:keys [db]} dimensions]
|
||||
{:db (assoc db :dimensions/window (dimensions/window dimensions))})
|
||||
|
||||
(fx/defn reset-current-profile-chat [{:keys [db] :as cofx} public-key]
|
||||
(let [chat-id (chat/profile-chat-topic public-key)]
|
||||
(when-not (= (:current-chat-id db) chat-id)
|
||||
(fx/merge cofx
|
||||
(chat/start-profile-chat public-key)
|
||||
(chat/offload-all-messages)
|
||||
(chat/preload-chat-data chat-id)))))
|
||||
(fx/defn init-timeline-chat
|
||||
{:events [:init-timeline-chat]}
|
||||
[{:keys [db] :as cofx}]
|
||||
(when-not (get-in db [:pagination-info constants/timeline-chat-id :messages-initialized?])
|
||||
(chat/preload-chat-data cofx constants/timeline-chat-id)))
|
||||
|
||||
(fx/defn reset-current-timeline-chat [{:keys [db] :as cofx}]
|
||||
(let [profile-chats (conj (map :public-key (contact.db/get-active-contacts (:contacts/contacts db)))
|
||||
(get-in db [:multiaccount :public-key]))]
|
||||
(when-not (= (:current-chat-id db) chat/timeline-chat-id)
|
||||
(fx/merge cofx
|
||||
(fn [cofx]
|
||||
(apply fx/merge cofx (map chat/start-profile-chat profile-chats)))
|
||||
(chat/start-timeline-chat)
|
||||
(chat/offload-all-messages)
|
||||
(chat/preload-chat-data chat/timeline-chat-id)))))
|
||||
|
||||
(fx/defn reset-current-chat [{:keys [db] :as cofx} chat-id]
|
||||
(when-not (= (:current-chat-id db) chat-id)
|
||||
(fx/merge cofx
|
||||
(chat/offload-all-messages)
|
||||
(chat/preload-chat-data chat-id))))
|
||||
|
||||
;; NOTE: Will be removed with the keycard PR
|
||||
(fx/defn on-will-focus
|
||||
{:events [:screens/on-will-focus]}
|
||||
[{:keys [db] :as cofx} view-id]
|
||||
[cofx view-id]
|
||||
(fx/merge cofx
|
||||
#(case view-id
|
||||
:keycard-settings (keycard/settings-screen-did-load %)
|
||||
@ -247,30 +193,8 @@
|
||||
:keycard-login-pin (keycard/enter-pin-screen-did-load %)
|
||||
:add-new-account-pin (keycard/enter-pin-screen-did-load %)
|
||||
:keycard-authentication-method (keycard/authentication-method-screen-did-load %)
|
||||
(:chat :group-chat-profile) (reset-current-chat % (get db :inactive-chat-id))
|
||||
:multiaccounts (keycard/multiaccounts-screen-did-load %)
|
||||
(:wallet-stack :wallet) (wallet/wallet-will-focus %)
|
||||
(:status :status-stack)
|
||||
(reset-current-timeline-chat %)
|
||||
:profile
|
||||
(reset-current-profile-chat % (get-in % [:db :contacts/identity]))
|
||||
nil)))
|
||||
|
||||
(fx/defn tab-will-change
|
||||
{:events [:screens/tab-will-change]}
|
||||
[{:keys [db] :as cofx} view-id]
|
||||
(fx/merge cofx
|
||||
#(case view-id
|
||||
;;when we back to chat we want to show inactive chat
|
||||
:chat
|
||||
(reset-current-chat % (get db :inactive-chat-id))
|
||||
|
||||
(:status :status-stack)
|
||||
(reset-current-timeline-chat %)
|
||||
|
||||
:profile
|
||||
(reset-current-profile-chat % (get-in % [:db :contacts/identity]))
|
||||
|
||||
nil)))
|
||||
|
||||
;;TODO :replace by named events
|
||||
|
@ -4,9 +4,6 @@
|
||||
[clojure.string :as string]
|
||||
[re-frame.core :as re-frame]
|
||||
[status-im.chat.models :as models.chat]
|
||||
[status-im.chat.models.message :as models.message]
|
||||
[status-im.data-store.chats :as data-store.chats]
|
||||
[status-im.data-store.messages :as data-store.messages]
|
||||
[status-im.ethereum.json-rpc :as json-rpc]
|
||||
[status-im.group-chats.db :as group-chats.db]
|
||||
[status-im.multiaccounts.model :as multiaccounts.model]
|
||||
@ -16,13 +13,27 @@
|
||||
[status-im.constants :as constants]
|
||||
[status-im.i18n.i18n :as i18n]))
|
||||
|
||||
(fx/defn navigate-chat-updated
|
||||
{:events [:navigate-chat-updated]}
|
||||
[cofx chat-id]
|
||||
(if (get-in cofx [:db :chats chat-id :is-active])
|
||||
(models.chat/navigate-to-chat cofx chat-id)
|
||||
(navigation/navigate-to-cofx cofx :home {})))
|
||||
|
||||
(fx/defn handle-chat-update
|
||||
{:events [:chat-updated]}
|
||||
[_ response]
|
||||
{:dispatch-n [[:sanitize-messages-and-process-response response]
|
||||
[:navigate-chat-updated (.-id (aget (.-chats response) 0))]]})
|
||||
|
||||
(fx/defn remove-member
|
||||
"Format group update message and sign membership"
|
||||
{:events [:group-chats.ui/remove-member-pressed]}
|
||||
[_ chat-id member]
|
||||
{::json-rpc/call [{:method (json-rpc/call-ext-method "removeMemberFromGroupChat")
|
||||
:params [nil chat-id member]
|
||||
:on-success #(re-frame/dispatch [::chat-updated %])}]})
|
||||
:js-response true
|
||||
:on-success #(re-frame/dispatch [:chat-updated %])}]})
|
||||
|
||||
(fx/defn set-up-filter
|
||||
"Listen/Tear down the shared topic/contact-codes. Stop listening for members who
|
||||
@ -39,34 +50,13 @@
|
||||
(transport.filters/upsert-group-chat-topics)
|
||||
(transport.filters/load-members members)))))
|
||||
|
||||
(fx/defn handle-chat-update
|
||||
{:events [::chat-updated]}
|
||||
[cofx {:keys [chats messages]}]
|
||||
(let [{:keys [chat-id] :as chat} (-> chats
|
||||
first
|
||||
(data-store.chats/<-rpc))
|
||||
|
||||
previous-chat (get-in cofx [:chats chat-id])
|
||||
set-up-filter-fx (set-up-filter chat-id previous-chat)
|
||||
chat-fx (models.chat/ensure-chat
|
||||
(dissoc chat :unviewed-messages-count))
|
||||
messages-fx (map #(models.message/receive-one
|
||||
(data-store.messages/<-rpc %))
|
||||
messages)
|
||||
navigate-fx #(if (get-in % [:db :chats chat-id :is-active])
|
||||
(models.chat/navigate-to-chat % chat-id)
|
||||
(navigation/navigate-to-cofx % :home {}))]
|
||||
|
||||
(apply fx/merge cofx (concat [chat-fx]
|
||||
messages-fx
|
||||
[navigate-fx]))))
|
||||
|
||||
(fx/defn join-chat
|
||||
{:events [:group-chats.ui/join-pressed]}
|
||||
[_ chat-id]
|
||||
{::json-rpc/call [{:method (json-rpc/call-ext-method "confirmJoiningGroup")
|
||||
:params [chat-id]
|
||||
:on-success #(re-frame/dispatch [::chat-updated %])}]})
|
||||
:js-response true
|
||||
:on-success #(re-frame/dispatch [:chat-updated %])}]})
|
||||
|
||||
(fx/defn create
|
||||
{:events [:group-chats.ui/create-pressed]
|
||||
@ -75,7 +65,8 @@
|
||||
(let [selected-contacts (:group/selected-contacts db)]
|
||||
{::json-rpc/call [{:method (json-rpc/call-ext-method "createGroupChatWithMembers")
|
||||
:params [nil group-name (into [] selected-contacts)]
|
||||
:on-success #(re-frame/dispatch [::chat-updated %])}]}))
|
||||
:js-response true
|
||||
:on-success #(re-frame/dispatch [:chat-updated %])}]}))
|
||||
|
||||
(fx/defn create-from-link
|
||||
[cofx {:keys [chat-id invitation-admin chat-name]}]
|
||||
@ -83,14 +74,16 @@
|
||||
(models.chat/navigate-to-chat cofx chat-id)
|
||||
{::json-rpc/call [{:method (json-rpc/call-ext-method "createGroupChatFromInvitation")
|
||||
:params [chat-name chat-id invitation-admin]
|
||||
:on-success #(re-frame/dispatch [::chat-updated %])}]}))
|
||||
:js-response true
|
||||
:on-success #(re-frame/dispatch [:chat-updated %])}]}))
|
||||
|
||||
(fx/defn make-admin
|
||||
{:events [:group-chats.ui/make-admin-pressed]}
|
||||
[_ chat-id member]
|
||||
{::json-rpc/call [{:method (json-rpc/call-ext-method "addAdminsToGroupChat")
|
||||
:params [nil chat-id [member]]
|
||||
:on-success #(re-frame/dispatch [::chat-updated %])}]})
|
||||
:js-response true
|
||||
:on-success #(re-frame/dispatch [:chat-updated %])}]})
|
||||
|
||||
(fx/defn add-members
|
||||
"Add members to a group chat"
|
||||
@ -98,7 +91,8 @@
|
||||
[{{:keys [current-chat-id selected-participants]} :db :as cofx}]
|
||||
{::json-rpc/call [{:method (json-rpc/call-ext-method "addMembersToGroupChat")
|
||||
:params [nil current-chat-id selected-participants]
|
||||
:on-success #(re-frame/dispatch [::chat-updated %])}]})
|
||||
:js-response true
|
||||
:on-success #(re-frame/dispatch [:chat-updated %])}]})
|
||||
|
||||
(fx/defn add-members-from-invitation
|
||||
"Add members to a group chat"
|
||||
@ -107,7 +101,8 @@
|
||||
{:db (assoc-in db [:group-chat/invitations id :state] constants/invitation-state-approved)
|
||||
::json-rpc/call [{:method (json-rpc/call-ext-method "addMembersToGroupChat")
|
||||
:params [nil current-chat-id [participant]]
|
||||
:on-success #(re-frame/dispatch [::chat-updated %])}]})
|
||||
:js-response true
|
||||
:on-success #(re-frame/dispatch [:chat-updated %])}]})
|
||||
|
||||
(fx/defn leave
|
||||
"Leave chat"
|
||||
@ -115,7 +110,8 @@
|
||||
[{:keys [db] :as cofx} chat-id]
|
||||
{::json-rpc/call [{:method (json-rpc/call-ext-method "leaveGroupChat")
|
||||
:params [nil chat-id true]
|
||||
:on-success #(re-frame/dispatch [::chat-updated %])}]})
|
||||
:js-response true
|
||||
:on-success #(re-frame/dispatch [:chat-updated %])}]})
|
||||
|
||||
(fx/defn remove
|
||||
"Remove chat"
|
||||
@ -142,7 +138,8 @@
|
||||
{:db (assoc-in db [:chats chat-id :name] new-name)
|
||||
::json-rpc/call [{:method (json-rpc/call-ext-method "changeGroupChatName")
|
||||
:params [nil chat-id new-name]
|
||||
:on-success #(re-frame/dispatch [::chat-updated %])}]}))
|
||||
:js-response true
|
||||
:on-success #(re-frame/dispatch [:chat-updated %])}]}))
|
||||
|
||||
(fx/defn membership-retry
|
||||
{:events [:group-chats.ui/membership-retry]}
|
||||
@ -163,7 +160,7 @@
|
||||
{:db (assoc-in db [:chat/memberships current-chat-id] nil)
|
||||
::json-rpc/call [{:method (json-rpc/call-ext-method "sendGroupChatInvitationRequest")
|
||||
:params [nil current-chat-id invitation-admin message]
|
||||
:on-success #(re-frame/dispatch [:transport/invitation-sent %])}]}))
|
||||
:on-success #(re-frame/dispatch [:sanitize-messages-and-process-response %])}]}))
|
||||
|
||||
(fx/defn send-group-chat-membership-rejection
|
||||
"Send group chat membership rejection"
|
||||
@ -171,7 +168,7 @@
|
||||
[cofx invitation-id]
|
||||
{::json-rpc/call [{:method (json-rpc/call-ext-method "sendGroupChatInvitationRejection")
|
||||
:params [nil invitation-id]
|
||||
:on-success #(re-frame/dispatch [:transport/invitation-sent %])}]})
|
||||
:on-success #(re-frame/dispatch [:sanitize-messages-and-process-response %])}]})
|
||||
|
||||
(fx/defn handle-invitations
|
||||
[{db :db} invitations]
|
||||
|
@ -1256,7 +1256,9 @@
|
||||
{})
|
||||
(dismiss-connection-error false))))
|
||||
|
||||
(fx/defn load-gaps-fx [{:keys [db] :as cofx} chat-id]
|
||||
(fx/defn load-gaps-fx
|
||||
{:events [:load-gaps]}
|
||||
[{:keys [db] :as cofx} chat-id]
|
||||
(when-not (get-in db [:gaps-loaded? chat-id])
|
||||
(let [success-fn #(re-frame/dispatch [::gaps-loaded %1 %2])]
|
||||
(data-store.mailservers/load-gaps cofx chat-id success-fn))))
|
||||
|
@ -169,9 +169,3 @@
|
||||
(extract-topics (:mailserver/topics db)
|
||||
chat-id
|
||||
(not (get-in (:chats db) [chat-id :public?]))))
|
||||
|
||||
(defn topics-for-current-chat
|
||||
"return a list of topics used by the current-chat, include discovery if
|
||||
private group chat or one-to-one"
|
||||
[{:keys [current-chat-id] :as db}]
|
||||
(topics-for-chat db current-chat-id))
|
||||
|
@ -32,7 +32,8 @@
|
||||
[status-im.data-store.invitations :as data-store.invitations]
|
||||
[status-im.chat.models.link-preview :as link-preview]
|
||||
[status-im.utils.mobile-sync :as utils.mobile-sync]
|
||||
[status-im.async-storage.core :as async-storage]))
|
||||
[status-im.async-storage.core :as async-storage]
|
||||
[status-im.chat.models :as chat.models]))
|
||||
|
||||
(re-frame/reg-fx
|
||||
::login
|
||||
@ -317,6 +318,8 @@
|
||||
(finish-keycard-setup)
|
||||
(transport/start-messenger)
|
||||
(communities/fetch)
|
||||
(chat.models/start-timeline-chat)
|
||||
(chat.models/start-profile-chat (:public-key multiaccount))
|
||||
(multiaccounts/switch-preview-privacy-mode-flag)
|
||||
(link-preview/request-link-preview-whitelist)
|
||||
(logging/set-log-level (:log-level multiaccount)))))
|
||||
|
@ -138,6 +138,7 @@
|
||||
{:keys [identicon]} :contact
|
||||
contact-id :contact-id}]
|
||||
(when (and chat-type chat-id)
|
||||
;;TODO : DON'T USE SUBS IN EVENTS
|
||||
(let [contact-name @(re-frame/subscribe
|
||||
[:contacts/contact-name-by-identity contact-id])
|
||||
group-chat? (not= chat-type constants/one-to-one-chat-type)
|
||||
|
@ -55,7 +55,7 @@
|
||||
"node.login" (status-node-started cofx (js->clj event-js :keywordize-keys true))
|
||||
"envelope.sent" (transport.message/update-envelopes-status cofx (:ids (js->clj event-js :keywordize-keys true)) :sent)
|
||||
"envelope.expired" (transport.message/update-envelopes-status cofx (:ids (js->clj event-js :keywordize-keys true)) :not-sent)
|
||||
"message.delivered" (let [{:keys [chatID messageID] :as event-cljs} (js->clj event-js :keywordize-keys true)]
|
||||
"message.delivered" (let [{:keys [chatID messageID]} (js->clj event-js :keywordize-keys true)]
|
||||
(models.message/update-db-message-status cofx chatID messageID :delivered))
|
||||
"mailserver.request.completed" (mailserver/handle-request-completed cofx (js->clj event-js :keywordize-keys true))
|
||||
"mailserver.request.expired" (when (multiaccounts.model/logged-in? cofx)
|
||||
@ -64,7 +64,7 @@
|
||||
"subscriptions.data" (ethereum.subscriptions/handle-signal cofx (js->clj event-js :keywordize-keys true))
|
||||
"subscriptions.error" (ethereum.subscriptions/handle-error cofx (js->clj event-js :keywordize-keys true))
|
||||
"whisper.filter.added" (transport.filters/handle-negotiated-filter cofx (js->clj event-js :keywordize-keys true))
|
||||
"messages.new" (transport.message/process-response cofx event-js)
|
||||
"messages.new" (transport.message/sanitize-messages-and-process-response cofx event-js)
|
||||
"wallet" (ethereum.subscriptions/new-wallet-event cofx (js->clj event-js :keywordize-keys true))
|
||||
"local-notifications" (local-notifications/process cofx (js->clj event-js :keywordize-keys true))
|
||||
(log/debug "Event " type " not handled"))))
|
||||
|
@ -240,8 +240,9 @@
|
||||
:params [chat-id (str value) contract transaction-hash
|
||||
(or (:result (types/json->clj signature))
|
||||
(ethereum/normalized-hex signature))]
|
||||
:js-response true
|
||||
:on-success
|
||||
#(re-frame/dispatch [:transport/message-sent % 1])}]})
|
||||
#(re-frame/dispatch [:transport/message-sent %])}]})
|
||||
|
||||
(fx/defn send-accept-request-transaction-message
|
||||
{:events [:sign/send-accept-transaction-message]}
|
||||
@ -250,8 +251,9 @@
|
||||
:params [transaction-hash message-id
|
||||
(or (:result (types/json->clj signature))
|
||||
(ethereum/normalized-hex signature))]
|
||||
:js-response true
|
||||
:on-success
|
||||
#(re-frame/dispatch [:transport/message-sent % 1])}]})
|
||||
#(re-frame/dispatch [:transport/message-sent %])}]})
|
||||
|
||||
(fx/defn transaction-result
|
||||
[{:keys [db] :as cofx} result tx-obj]
|
||||
@ -429,7 +431,8 @@
|
||||
amount
|
||||
(when-not (= symbol :ETH)
|
||||
address)]
|
||||
:on-success #(re-frame/dispatch [:transport/message-sent % 1])}]})))
|
||||
:js-response true
|
||||
:on-success #(re-frame/dispatch [:transport/message-sent %])}]})))
|
||||
|
||||
(fx/defn sign-transaction-button-clicked-from-request
|
||||
{:events [:wallet.ui/sign-transaction-button-clicked-from-request]}
|
||||
|
@ -116,7 +116,6 @@
|
||||
(reg-root-key-sub :group-chat/invitations :group-chat/invitations)
|
||||
(reg-root-key-sub :chats/mention-suggestions :chats/mention-suggestions)
|
||||
(reg-root-key-sub :chat/inputs-with-mentions :chat/inputs-with-mentions)
|
||||
(reg-root-key-sub :inactive-chat-id :inactive-chat-id)
|
||||
;;browser
|
||||
(reg-root-key-sub :browsers :browser/browsers)
|
||||
(reg-root-key-sub :browser/options :browser/options)
|
||||
@ -758,7 +757,7 @@
|
||||
chats)))
|
||||
|
||||
(re-frame/reg-sub
|
||||
::chat
|
||||
:chat-by-id
|
||||
:<- [::chats]
|
||||
(fn [chats [_ chat-id]]
|
||||
(get chats chat-id)))
|
||||
@ -777,6 +776,19 @@
|
||||
(fn [[chat-id inputs]]
|
||||
(get inputs chat-id)))
|
||||
|
||||
(re-frame/reg-sub
|
||||
:chats/timeline-chat-input
|
||||
:<- [:chat/inputs]
|
||||
:<- [:multiaccount/public-key]
|
||||
(fn [[inputs public-key]]
|
||||
(get inputs (chat.models/profile-chat-topic public-key))))
|
||||
|
||||
(re-frame/reg-sub
|
||||
:chats/timeline-chat-input-text
|
||||
:<- [:chats/timeline-chat-input]
|
||||
(fn [input]
|
||||
(:input-text input)))
|
||||
|
||||
(re-frame/reg-sub
|
||||
:chats/current-chat-input-text
|
||||
:<- [:chats/current-chat-inputs]
|
||||
@ -794,13 +806,11 @@
|
||||
:chats/current-chat
|
||||
:<- [:chats/current-raw-chat]
|
||||
:<- [:multiaccount/public-key]
|
||||
:<- [:inactive-chat-id]
|
||||
:<- [:communities/current-community]
|
||||
(fn [[{:keys [group-chat] :as current-chat}
|
||||
my-public-key
|
||||
inactive-chat-id
|
||||
community]]
|
||||
(when (and current-chat (= (:chat-id current-chat) inactive-chat-id))
|
||||
(when current-chat
|
||||
(cond-> current-chat
|
||||
(chat.models/public-chat? current-chat)
|
||||
(assoc :show-input? true)
|
||||
@ -846,27 +856,24 @@
|
||||
(chat.models/public-chat? current-chat)))
|
||||
|
||||
(re-frame/reg-sub
|
||||
:chats/current-chat-messages
|
||||
:chats/chat-messages
|
||||
:<- [::messages]
|
||||
:<- [:chats/current-chat-id]
|
||||
(fn [[messages chat-id]]
|
||||
(fn [messages [_ chat-id]]
|
||||
(get messages chat-id {})))
|
||||
|
||||
(re-frame/reg-sub
|
||||
:chats/message-reactions
|
||||
:<- [:multiaccount/public-key]
|
||||
:<- [::reactions]
|
||||
:<- [:chats/current-chat-id]
|
||||
(fn [[current-public-key reactions current-chat-id] [_ message-id chat-id]]
|
||||
(fn [[current-public-key reactions] [_ message-id chat-id]]
|
||||
(models.reactions/message-reactions
|
||||
current-public-key
|
||||
(get-in reactions [(or chat-id current-chat-id) message-id]))))
|
||||
(get-in reactions [chat-id message-id]))))
|
||||
|
||||
(re-frame/reg-sub
|
||||
:chats/messages-gaps
|
||||
:<- [:mailserver/gaps]
|
||||
:<- [:chats/current-chat-id]
|
||||
(fn [[gaps chat-id]]
|
||||
(fn [gaps [_ chat-id]]
|
||||
(sort-by :from (vals (get gaps chat-id)))))
|
||||
|
||||
(re-frame/reg-sub
|
||||
@ -878,8 +885,7 @@
|
||||
(re-frame/reg-sub
|
||||
:chats/range
|
||||
:<- [:mailserver/ranges]
|
||||
:<- [:chats/current-chat-id]
|
||||
(fn [[ranges chat-id]]
|
||||
(fn [ranges [_ chat-id]]
|
||||
(get ranges chat-id)))
|
||||
|
||||
(re-frame/reg-sub
|
||||
@ -893,21 +899,25 @@
|
||||
(re-frame/reg-sub
|
||||
:chats/all-loaded?
|
||||
:<- [::pagination-info]
|
||||
:<- [:chats/current-chat-id]
|
||||
(fn [[pagination-info chat-id]]
|
||||
(fn [pagination-info [_ chat-id]]
|
||||
(get-in pagination-info [chat-id :all-loaded?])))
|
||||
|
||||
(re-frame/reg-sub
|
||||
:chats/loading-messages?
|
||||
:<- [::pagination-info]
|
||||
(fn [pagination-info [_ chat-id]]
|
||||
(get-in pagination-info [chat-id :loading-messages?])))
|
||||
|
||||
(re-frame/reg-sub
|
||||
:chats/public?
|
||||
:<- [:chats/current-raw-chat]
|
||||
(fn [chat]
|
||||
(:public? chat)))
|
||||
:<- [::chats]
|
||||
(fn [chats [_ chat-id]]
|
||||
(get-in chats [chat-id :public?])))
|
||||
|
||||
(re-frame/reg-sub
|
||||
:chats/message-list
|
||||
:<- [::message-lists]
|
||||
:<- [:chats/current-chat-id]
|
||||
(fn [[message-lists chat-id]]
|
||||
(fn [message-lists [_ chat-id]]
|
||||
(get message-lists chat-id)))
|
||||
|
||||
(defn hydrate-messages
|
||||
@ -920,21 +930,23 @@
|
||||
message-list))
|
||||
|
||||
(re-frame/reg-sub
|
||||
:chats/current-chat-no-messages?
|
||||
:<- [:chats/current-chat-messages]
|
||||
:chats/chat-no-messages?
|
||||
(fn [[_ chat-id] _]
|
||||
(re-frame/subscribe [:chats/chat-messages chat-id]))
|
||||
(fn [messages]
|
||||
(empty? messages)))
|
||||
|
||||
(re-frame/reg-sub
|
||||
:chats/current-chat-messages-stream
|
||||
:<- [:chats/message-list]
|
||||
:<- [:chats/current-chat-messages]
|
||||
:<- [:chats/messages-gaps]
|
||||
:<- [:chats/range]
|
||||
:<- [:chats/all-loaded?]
|
||||
:<- [:chats/public?]
|
||||
:chats/chat-messages-stream
|
||||
(fn [[_ chat-id] _]
|
||||
[(re-frame/subscribe [:chats/message-list chat-id])
|
||||
(re-frame/subscribe [:chats/chat-messages chat-id])
|
||||
(re-frame/subscribe [:chats/messages-gaps chat-id])
|
||||
(re-frame/subscribe [:chats/range chat-id])
|
||||
(re-frame/subscribe [:chats/all-loaded? chat-id])
|
||||
(re-frame/subscribe [:chats/public? chat-id])])
|
||||
(fn [[message-list messages messages-gaps range all-loaded? public?]]
|
||||
;;TODO (perf) we need to move all these to status-go
|
||||
;;TODO (perf)
|
||||
(-> (models.message-list/->seq message-list)
|
||||
(chat.db/add-datemarks)
|
||||
(hydrate-messages messages)
|
||||
@ -942,13 +954,17 @@
|
||||
|
||||
(re-frame/reg-sub
|
||||
:chats/timeline-messages-stream
|
||||
:<- [:chats/message-list]
|
||||
:<- [:chats/current-chat-messages]
|
||||
:<- [:chats/current-raw-chat]
|
||||
(fn [[message-list messages {:keys [timeline?]}]]
|
||||
(when timeline?
|
||||
(-> (models.message-list/->seq message-list)
|
||||
(hydrate-messages messages)))))
|
||||
:<- [:chats/message-list constants/timeline-chat-id]
|
||||
:<- [:chats/chat-messages constants/timeline-chat-id]
|
||||
(fn [[message-list messages]]
|
||||
(-> (models.message-list/->seq message-list)
|
||||
(hydrate-messages messages))))
|
||||
|
||||
(re-frame/reg-sub
|
||||
:chats/current-profile-chat
|
||||
:<- [:contacts/current-contact-identity]
|
||||
(fn [identity]
|
||||
(chat.models/profile-chat-topic identity)))
|
||||
|
||||
(re-frame/reg-sub
|
||||
:chats/photo-path
|
||||
@ -997,6 +1013,12 @@
|
||||
(fn [{:keys [metadata]}]
|
||||
(:sending-image metadata)))
|
||||
|
||||
(re-frame/reg-sub
|
||||
:chats/timeline-sending-image
|
||||
:<- [:chats/timeline-chat-input]
|
||||
(fn [{:keys [metadata]}]
|
||||
(:sending-image metadata)))
|
||||
|
||||
(re-frame/reg-sub
|
||||
:public-chat.new/topic-error-message
|
||||
:<- [:public-group-topic]
|
||||
@ -1035,7 +1057,7 @@
|
||||
(re-frame/reg-sub
|
||||
:group-chat/inviter-info
|
||||
(fn [[_ chat-id] _]
|
||||
[(re-frame/subscribe [::chat chat-id])
|
||||
[(re-frame/subscribe [:chat-by-id chat-id])
|
||||
(re-frame/subscribe [:multiaccount/public-key])])
|
||||
(fn [[chat my-public-key]]
|
||||
{:joined? (group-chats.db/joined? my-public-key chat)
|
||||
@ -2028,9 +2050,8 @@
|
||||
|
||||
(re-frame/reg-sub
|
||||
:chats/fetching-gap-in-progress?
|
||||
:<- [:chats/current-chat-id]
|
||||
:<- [:mailserver/fetching-gaps-in-progress]
|
||||
(fn [[chat-id gaps] [_ ids]]
|
||||
(fn [gaps [_ ids chat-id]]
|
||||
(seq (select-keys (get gaps chat-id) ids))))
|
||||
|
||||
(re-frame/reg-sub
|
||||
|
@ -7,44 +7,19 @@
|
||||
[status-im.communities.core :as models.communities]
|
||||
[status-im.pairing.core :as models.pairing]
|
||||
[status-im.transport.filters.core :as models.filters]
|
||||
[status-im.data-store.messages :as data-store.messages]
|
||||
[status-im.data-store.reactions :as data-store.reactions]
|
||||
[status-im.data-store.contacts :as data-store.contacts]
|
||||
[status-im.data-store.chats :as data-store.chats]
|
||||
[status-im.data-store.invitations :as data-store.invitations]
|
||||
[status-im.group-chats.core :as models.group]
|
||||
[status-im.utils.fx :as fx]
|
||||
[status-im.utils.types :as types]))
|
||||
|
||||
(fx/defn handle-chats [cofx chats]
|
||||
(models.chat/ensure-chats cofx chats))
|
||||
|
||||
(fx/defn handle-contacts [cofx contacts]
|
||||
(models.contact/ensure-contacts cofx contacts))
|
||||
|
||||
(fx/defn handle-message [cofx message]
|
||||
(models.message/receive-one cofx message))
|
||||
|
||||
(fx/defn handle-community [cofx community]
|
||||
(models.communities/handle-community cofx community))
|
||||
|
||||
(fx/defn handle-request-to-join-community [cofx request]
|
||||
(models.communities/handle-request-to-join cofx request))
|
||||
|
||||
(fx/defn handle-reactions [cofx reactions]
|
||||
(models.reactions/receive-signal cofx reactions))
|
||||
|
||||
(fx/defn handle-invitations [cofx invitations]
|
||||
(models.group/handle-invitations cofx invitations))
|
||||
|
||||
(fx/defn handle-filters [cofx filters]
|
||||
(models.filters/handle-filters cofx filters))
|
||||
|
||||
(fx/defn handle-filters-removed [cofx filters]
|
||||
(models.filters/handle-filters-removed cofx filters))
|
||||
[status-im.utils.types :as types]
|
||||
[status-im.constants :as constants]
|
||||
[status-im.multiaccounts.model :as multiaccounts.model]
|
||||
[clojure.string :as string]))
|
||||
|
||||
(fx/defn process-response
|
||||
{:events [::process]}
|
||||
{:events [:process-response]}
|
||||
[cofx ^js response-js]
|
||||
(let [^js communities (.-communities response-js)
|
||||
^js requests-to-join-community (.-requestsToJoinCommunity response-js)
|
||||
@ -56,76 +31,142 @@
|
||||
^js filters (.-filters response-js)
|
||||
^js removed-filters (.-removedFilters response-js)
|
||||
^js invitations (.-invitations response-js)]
|
||||
|
||||
(cond
|
||||
|
||||
(seq chats)
|
||||
(do
|
||||
(js-delete response-js "chats")
|
||||
(fx/merge cofx
|
||||
{:utils/dispatch-later [{:ms 20 :dispatch [:process-response response-js]}]}
|
||||
(models.chat/ensure-chats (map #(-> %
|
||||
(data-store.chats/<-rpc)
|
||||
;;TODO why here?
|
||||
(dissoc :unviewed-messages-count))
|
||||
(types/js->clj chats)))))
|
||||
|
||||
(seq messages)
|
||||
(models.message/receive-many cofx response-js)
|
||||
|
||||
(seq installations)
|
||||
(let [installations-clj (types/js->clj installations)]
|
||||
(js-delete response-js "installations")
|
||||
(fx/merge cofx
|
||||
{:utils/dispatch-later [{:ms 20 :dispatch [::process response-js]}]}
|
||||
{:utils/dispatch-later [{:ms 20 :dispatch [:process-response response-js]}]}
|
||||
(models.pairing/handle-installations installations-clj)))
|
||||
|
||||
(seq contacts)
|
||||
(let [contacts-clj (types/js->clj contacts)]
|
||||
(js-delete response-js "contacts")
|
||||
(fx/merge cofx
|
||||
{:utils/dispatch-later [{:ms 20 :dispatch [::process response-js]}]}
|
||||
(handle-contacts (map data-store.contacts/<-rpc contacts-clj))))
|
||||
{:utils/dispatch-later [{:ms 20 :dispatch [:process-response response-js]}]}
|
||||
(models.contact/ensure-contacts (map data-store.contacts/<-rpc contacts-clj))))
|
||||
|
||||
(seq communities)
|
||||
(let [community (.pop communities)]
|
||||
(fx/merge cofx
|
||||
{:utils/dispatch-later [{:ms 20 :dispatch [::process response-js]}]}
|
||||
(handle-community (types/js->clj community))))
|
||||
{:utils/dispatch-later [{:ms 20 :dispatch [:process-response response-js]}]}
|
||||
(models.communities/handle-community (types/js->clj community))))
|
||||
(seq requests-to-join-community)
|
||||
(let [request (.pop requests-to-join-community)]
|
||||
(fx/merge cofx
|
||||
{:utils/dispatch-later [{:ms 20 :dispatch [::process response-js]}]}
|
||||
(handle-request-to-join-community (types/js->clj request))))
|
||||
(seq chats)
|
||||
(let [chats-clj (types/js->clj chats)]
|
||||
(js-delete response-js "chats")
|
||||
(fx/merge cofx
|
||||
{:utils/dispatch-later [{:ms 20 :dispatch [::process response-js]}]}
|
||||
(handle-chats (map #(-> %
|
||||
(data-store.chats/<-rpc)
|
||||
(dissoc :unviewed-messages-count))
|
||||
chats-clj))))
|
||||
|
||||
(seq messages)
|
||||
(let [message (.pop messages)]
|
||||
(fx/merge cofx
|
||||
{:utils/dispatch-later [{:ms 20 :dispatch [::process response-js]}]}
|
||||
(handle-message (-> message (types/js->clj) (data-store.messages/<-rpc)))))
|
||||
(models.communities/handle-request-to-join (types/js->clj request))))
|
||||
|
||||
(seq emoji-reactions)
|
||||
(let [reactions (types/js->clj emoji-reactions)]
|
||||
(js-delete response-js "emojiReactions")
|
||||
(fx/merge cofx
|
||||
{:utils/dispatch-later [{:ms 20 :dispatch [::process response-js]}]}
|
||||
(handle-reactions (map data-store.reactions/<-rpc reactions))))
|
||||
{:utils/dispatch-later [{:ms 20 :dispatch [:process-response response-js]}]}
|
||||
(models.reactions/receive-signal (map data-store.reactions/<-rpc reactions))))
|
||||
|
||||
(seq invitations)
|
||||
(let [invitations (types/js->clj invitations)]
|
||||
(js-delete response-js "invitations")
|
||||
(fx/merge cofx
|
||||
{:utils/dispatch-later [{:ms 20 :dispatch [::process response-js]}]}
|
||||
(handle-invitations (map data-store.invitations/<-rpc invitations))))
|
||||
{:utils/dispatch-later [{:ms 20 :dispatch [:process-response response-js]}]}
|
||||
(models.group/handle-invitations (map data-store.invitations/<-rpc invitations))))
|
||||
(seq filters)
|
||||
(let [filters (types/js->clj filters)]
|
||||
(js-delete response-js "filters")
|
||||
(fx/merge cofx
|
||||
{:utils/dispatch-later [{:ms 20 :dispatch [::process response-js]}]}
|
||||
(handle-filters filters)))
|
||||
{:utils/dispatch-later [{:ms 20 :dispatch [:process-response response-js]}]}
|
||||
(models.filters/handle-filters filters)))
|
||||
|
||||
(seq removed-filters)
|
||||
(let [removed-filters (types/js->clj removed-filters)]
|
||||
(js-delete response-js "removedFilters")
|
||||
(fx/merge cofx
|
||||
{:utils/dispatch-later [{:ms 20 :dispatch [::process response-js]}]}
|
||||
(handle-filters-removed filters))))))
|
||||
{:utils/dispatch-later [{:ms 20 :dispatch [:process-response response-js]}]}
|
||||
(models.filters/handle-filters-removed filters))))))
|
||||
|
||||
(defn group-by-and-update-unviewed-counts
|
||||
"group messages by current chat, profile updates, transactions and update unviewed counters in db for not curent chats"
|
||||
[{:keys [current-chat-id db] :as acc} ^js message-js]
|
||||
(let [chat-id (.-localChatId message-js)
|
||||
message-type (.-messageType message-js)
|
||||
from (.-from message-js)
|
||||
new (.-new message-js)
|
||||
current (= current-chat-id chat-id)
|
||||
profile (models.chat/profile-chat? {:db db} chat-id)
|
||||
tx-hash (and (.-commandParameters message-js) (.-commandParameters.transactionHash message-js))]
|
||||
(cond-> acc
|
||||
current
|
||||
(update :messages conj message-js)
|
||||
|
||||
profile
|
||||
(update :statuses conj message-js)
|
||||
|
||||
;;update counter
|
||||
(and (not current)
|
||||
new
|
||||
(not profile)
|
||||
(not (= message-type constants/message-type-private-group-system-message))
|
||||
(not (= from (multiaccounts.model/current-public-key {:db db}))))
|
||||
(update-in [:db :chats chat-id :unviewed-messages-count] inc)
|
||||
|
||||
;;conj incoming transaction for :watch-tx
|
||||
(not (string/blank? tx-hash))
|
||||
(update :transactions conj tx-hash)
|
||||
|
||||
:always
|
||||
(update :chats conj chat-id))))
|
||||
|
||||
(defn sort-js-messages!
|
||||
"sort messages, so we can start process latest first,in that case we only need to process frist 20 and drop others"
|
||||
[response-js messages]
|
||||
(if (seq messages)
|
||||
(set! (.-messages response-js)
|
||||
(.sort (to-array messages)
|
||||
(fn [a b]
|
||||
(- (.-clock b) (.-clock a)))))
|
||||
(js-delete response-js "messages")))
|
||||
|
||||
(fx/defn sanitize-messages-and-process-response
|
||||
"before processing we want to filter and sort messages, so we can process first only messages which will be showed"
|
||||
{:events [:sanitize-messages-and-process-response]}
|
||||
[{:keys [db] :as cofx} ^js response-js]
|
||||
(let [current-chat-id (:current-chat-id db)
|
||||
{:keys [db messages transactions chats statuses]}
|
||||
(reduce group-by-and-update-unviewed-counts
|
||||
{:db db :chats #{} :transactions #{} :statuses [] :messages []
|
||||
:current-chat-id current-chat-id}
|
||||
(.-messages response-js))]
|
||||
(sort-js-messages! response-js messages)
|
||||
(fx/merge cofx
|
||||
{:db db
|
||||
:utils/dispatch-later (concat []
|
||||
(when (seq statuses)
|
||||
[{:ms 100 :dispatch [:process-statuses statuses]}])
|
||||
(when (seq transactions)
|
||||
(for [transaction-hash transactions]
|
||||
{:ms 100 :dispatch [:watch-tx transaction-hash]}))
|
||||
(when (seq chats)
|
||||
[{:ms 100 :dispatch [:chat/join-times-messages-checked chats]}]))}
|
||||
(process-response response-js))))
|
||||
|
||||
(fx/defn remove-hash
|
||||
[{:keys [db] :as cofx} envelope-hash]
|
||||
[{:keys [db]} envelope-hash]
|
||||
{:db (update db :transport/message-envelopes dissoc envelope-hash)})
|
||||
|
||||
(fx/defn check-confirmations
|
||||
@ -166,7 +207,7 @@
|
||||
|
||||
(fx/defn set-message-envelope-hash
|
||||
"message-type is used for tracking"
|
||||
[{:keys [db] :as cofx} chat-id message-id message-type messages-count]
|
||||
[{:keys [db] :as cofx} chat-id message-id message-type]
|
||||
;; Check first if the confirmation has already arrived
|
||||
(let [statuses (get-in db [:transport/message-confirmations message-id])
|
||||
check-confirmations-fx (map
|
||||
@ -180,5 +221,15 @@
|
||||
{:chat-id chat-id
|
||||
:message-type message-type})
|
||||
(update-in [:transport/message-ids->confirmations message-id]
|
||||
#(or % {:pending-confirmations messages-count})))})]
|
||||
#(or % {:pending-confirmations 1})))})]
|
||||
(apply fx/merge cofx (conj check-confirmations-fx add-envelope-data))))
|
||||
|
||||
(fx/defn transport-message-sent
|
||||
{:events [:transport/message-sent]}
|
||||
[cofx response-js]
|
||||
(let [set-hash-fxs (map (fn [{:keys [localChatId id messageType]}]
|
||||
(set-message-envelope-hash localChatId id messageType))
|
||||
(types/js->clj (.-messages response-js)))]
|
||||
(apply fx/merge cofx
|
||||
(conj set-hash-fxs
|
||||
#(sanitize-messages-and-process-response % response-js)))))
|
||||
|
@ -26,26 +26,25 @@
|
||||
:sticker sticker
|
||||
:contentType content-type})
|
||||
|
||||
(fx/defn send-chat-messages [cofx messages]
|
||||
{::json-rpc/call
|
||||
[{:method (json-rpc/call-ext-method "sendChatMessages")
|
||||
:params [(mapv build-message messages)]
|
||||
:on-success
|
||||
#(re-frame/dispatch [:transport/message-sent % 1])
|
||||
:on-failure #(log/error "failed to send a message" %)}]})
|
||||
(fx/defn send-chat-messages [_ messages]
|
||||
{::json-rpc/call [{:method (json-rpc/call-ext-method "sendChatMessages")
|
||||
:params [(mapv build-message messages)]
|
||||
:js-response true
|
||||
:on-success #(re-frame/dispatch [:transport/message-sent %])
|
||||
:on-failure #(log/error "failed to send a message" %)}]})
|
||||
|
||||
(fx/defn send-reaction [cofx {:keys [message-id chat-id emoji-id]}]
|
||||
(fx/defn send-reaction [_ {:keys [message-id chat-id emoji-id]}]
|
||||
{::json-rpc/call [{:method (json-rpc/call-ext-method
|
||||
"sendEmojiReaction")
|
||||
:params [chat-id message-id emoji-id]
|
||||
:on-success
|
||||
#(re-frame/dispatch [:transport/reaction-sent %])
|
||||
:js-response true
|
||||
:on-success #(re-frame/dispatch [:sanitize-messages-and-process-response %])
|
||||
:on-failure #(log/error "failed to send a reaction" %)}]})
|
||||
|
||||
(fx/defn send-retract-reaction [cofx {:keys [emoji-reaction-id] :as reaction}]
|
||||
(fx/defn send-retract-reaction [_ {:keys [emoji-reaction-id] :as reaction}]
|
||||
{::json-rpc/call [{:method (json-rpc/call-ext-method
|
||||
"sendEmojiReactionRetraction")
|
||||
:params [emoji-reaction-id]
|
||||
:on-success
|
||||
#(re-frame/dispatch [:transport/retraction-sent %])
|
||||
:js-response true
|
||||
:on-success #(re-frame/dispatch [:sanitize-messages-and-process-response %])
|
||||
:on-failure #(log/error "failed to send a reaction retraction" %)}]})
|
||||
|
@ -49,47 +49,47 @@
|
||||
update? (atom nil)
|
||||
current-obj (reagent/atom nil)]
|
||||
(reagent/create-class
|
||||
{:component-will-mount (fn [args]
|
||||
(let [[_ obj _ _] (.-argv (.-props args))]
|
||||
(when @clear-timeout (js/clearTimeout @clear-timeout))
|
||||
(when (or (not= obj @current-obj) @update?)
|
||||
(cond
|
||||
@update?
|
||||
(do (reset! update? false)
|
||||
(show-panel-anim bottom-anim-value alpha-value))
|
||||
{:UNSAFE_componentWillMount (fn [args]
|
||||
(let [[_ obj _ _] (.-argv (.-props args))]
|
||||
(when @clear-timeout (js/clearTimeout @clear-timeout))
|
||||
(when (or (not= obj @current-obj) @update?)
|
||||
(cond
|
||||
@update?
|
||||
(do (reset! update? false)
|
||||
(show-panel-anim bottom-anim-value alpha-value))
|
||||
|
||||
(and @current-obj obj)
|
||||
(do (reset! update? true)
|
||||
(js/setTimeout #(reset! current-obj obj) 600)
|
||||
(hide-panel-anim bottom-anim-value alpha-value (- window-height)))
|
||||
(and @current-obj obj)
|
||||
(do (reset! update? true)
|
||||
(js/setTimeout #(reset! current-obj obj) 600)
|
||||
(hide-panel-anim bottom-anim-value alpha-value (- window-height)))
|
||||
|
||||
obj
|
||||
(do (reset! current-obj obj)
|
||||
(show-panel-anim bottom-anim-value alpha-value))
|
||||
obj
|
||||
(do (reset! current-obj obj)
|
||||
(show-panel-anim bottom-anim-value alpha-value))
|
||||
|
||||
:else
|
||||
(do (reset! clear-timeout (js/setTimeout #(reset! current-obj nil) 600))
|
||||
(hide-panel-anim bottom-anim-value alpha-value (- window-height)))))))
|
||||
:component-will-update (fn [_ [_ obj _ _]]
|
||||
(when @clear-timeout (js/clearTimeout @clear-timeout))
|
||||
(when (or (not= obj @current-obj) @update?)
|
||||
(cond
|
||||
@update?
|
||||
(do (reset! update? false)
|
||||
(show-panel-anim bottom-anim-value alpha-value))
|
||||
:else
|
||||
(do (reset! clear-timeout (js/setTimeout #(reset! current-obj nil) 600))
|
||||
(hide-panel-anim bottom-anim-value alpha-value (- window-height)))))))
|
||||
:UNSAFE_componentWillUpdate (fn [_ [_ obj _ _]]
|
||||
(when @clear-timeout (js/clearTimeout @clear-timeout))
|
||||
(when (or (not= obj @current-obj) @update?)
|
||||
(cond
|
||||
@update?
|
||||
(do (reset! update? false)
|
||||
(show-panel-anim bottom-anim-value alpha-value))
|
||||
|
||||
(and @current-obj obj)
|
||||
(do (reset! update? true)
|
||||
(js/setTimeout #(reset! current-obj obj) 600)
|
||||
(hide-panel-anim bottom-anim-value alpha-value (- window-height)))
|
||||
(and @current-obj obj)
|
||||
(do (reset! update? true)
|
||||
(js/setTimeout #(reset! current-obj obj) 600)
|
||||
(hide-panel-anim bottom-anim-value alpha-value (- window-height)))
|
||||
|
||||
obj
|
||||
(do (reset! current-obj obj)
|
||||
(show-panel-anim bottom-anim-value alpha-value))
|
||||
obj
|
||||
(do (reset! current-obj obj)
|
||||
(show-panel-anim bottom-anim-value alpha-value))
|
||||
|
||||
:else
|
||||
(do (reset! clear-timeout (js/setTimeout #(reset! current-obj nil) 600))
|
||||
(hide-panel-anim bottom-anim-value alpha-value (- window-height))))))
|
||||
:else
|
||||
(do (reset! clear-timeout (js/setTimeout #(reset! current-obj nil) 600))
|
||||
(hide-panel-anim bottom-anim-value alpha-value (- window-height))))))
|
||||
:reagent-render (fn []
|
||||
(when @current-obj
|
||||
[react/keyboard-avoiding-view {:style {:position :absolute :top 0 :bottom 0 :left 0 :right 0}}
|
||||
|
@ -1,6 +1,5 @@
|
||||
(ns status-im.ui.components.connectivity.view
|
||||
(:require [re-frame.core :as re-frame]
|
||||
[reagent.core :as reagent]
|
||||
[status-im.i18n.i18n :as i18n]
|
||||
[status-im.ui.components.animation :as animation]
|
||||
[status-im.ui.components.colors :as colors]
|
||||
@ -67,7 +66,7 @@
|
||||
|
||||
(defview loading-indicator []
|
||||
(letsubs [ui-status-properties [:connectivity/ui-status-properties]
|
||||
window-width (reagent/atom 0)]
|
||||
window-width [:dimensions/window-width]]
|
||||
(when (:loading-indicator? ui-status-properties)
|
||||
[loading-indicator-anim @window-width])))
|
||||
|
||||
|
@ -79,7 +79,7 @@
|
||||
(def tabs
|
||||
(reagent/adapt-react-class
|
||||
(fn [props]
|
||||
(let [{:keys [navigate index route state popToTop]} (bean props)
|
||||
(let [{:keys [navigate index route popToTop]} (bean props)
|
||||
{:keys [keyboard-shown]
|
||||
:or {keyboard-shown false}} (when platform/android? (rn/use-keyboard))
|
||||
{:keys [bottom]} (safe-area/use-safe-area)
|
||||
@ -105,10 +105,7 @@
|
||||
:label title
|
||||
:on-press #(if (= (str index) (str route-index))
|
||||
(popToTop)
|
||||
(let [view-id (navigation/get-index-route-name route-index (bean state))]
|
||||
(re-frame/dispatch-sync [:screens/tab-will-change view-id])
|
||||
(reagent/flush)
|
||||
(navigate (name nav-stack))))
|
||||
(navigate (name nav-stack)))
|
||||
:accessibility-label accessibility-label
|
||||
:count-subscription count-subscription
|
||||
:active? (= (str index) (str route-index))
|
||||
|
@ -40,41 +40,41 @@
|
||||
alpha-value (anim/create-value 0)
|
||||
current-permission (reagent/atom nil)
|
||||
update? (reagent/atom nil)]
|
||||
{:component-will-update (fn [_ [_ _ {:keys [requested-permission]}]]
|
||||
(cond
|
||||
@update?
|
||||
;; the component has been updated with a new permission, we show the panel
|
||||
(do (reset! update? false)
|
||||
(show-panel-anim bottom-anim-value alpha-value))
|
||||
{:UNSAFE_componentWillUpdate (fn [_ [_ _ {:keys [requested-permission]}]]
|
||||
(cond
|
||||
@update?
|
||||
;; the component has been updated with a new permission, we show the panel
|
||||
(do (reset! update? false)
|
||||
(show-panel-anim bottom-anim-value alpha-value))
|
||||
|
||||
(and @current-permission requested-permission)
|
||||
;; a permission has been accepted/denied by the user, and there is
|
||||
;; another permission that needs to be processed by the user
|
||||
;; we hide the processed permission with an animation and update
|
||||
;; `current-permission` with a delay so that the information is still
|
||||
;; available during the animation
|
||||
(do (reset! update? true)
|
||||
(js/setTimeout #(reset! current-permission
|
||||
(permission-details requested-permission))
|
||||
600)
|
||||
(hide-panel-anim bottom-anim-value alpha-value))
|
||||
(and @current-permission requested-permission)
|
||||
;; a permission has been accepted/denied by the user, and there is
|
||||
;; another permission that needs to be processed by the user
|
||||
;; we hide the processed permission with an animation and update
|
||||
;; `current-permission` with a delay so that the information is still
|
||||
;; available during the animation
|
||||
(do (reset! update? true)
|
||||
(js/setTimeout #(reset! current-permission
|
||||
(permission-details requested-permission))
|
||||
600)
|
||||
(hide-panel-anim bottom-anim-value alpha-value))
|
||||
|
||||
requested-permission
|
||||
;; the dapp is asking for a permission, we put it in current-permission
|
||||
;; and start the show-animation
|
||||
(do (reset! current-permission
|
||||
(get browser.permissions/supported-permissions
|
||||
requested-permission))
|
||||
(show-panel-anim bottom-anim-value alpha-value))
|
||||
requested-permission
|
||||
;; the dapp is asking for a permission, we put it in current-permission
|
||||
;; and start the show-animation
|
||||
(do (reset! current-permission
|
||||
(get browser.permissions/supported-permissions
|
||||
requested-permission))
|
||||
(show-panel-anim bottom-anim-value alpha-value))
|
||||
|
||||
:else
|
||||
;; a permission has been accepted/denied by the user, and there is
|
||||
;; no other permission that needs to be processed by the user
|
||||
;; we hide the processed permission with an animation and update
|
||||
;; `current-permission` with a delay so that the information is still
|
||||
;; available during the animation
|
||||
(do (js/setTimeout #(reset! current-permission nil) 500)
|
||||
(hide-panel-anim bottom-anim-value alpha-value))))}
|
||||
:else
|
||||
;; a permission has been accepted/denied by the user, and there is
|
||||
;; no other permission that needs to be processed by the user
|
||||
;; we hide the processed permission with an animation and update
|
||||
;; `current-permission` with a delay so that the information is still
|
||||
;; available during the animation
|
||||
(do (js/setTimeout #(reset! current-permission nil) 500)
|
||||
(hide-panel-anim bottom-anim-value alpha-value))))}
|
||||
(when @current-permission
|
||||
(let [{:keys [title description type icon]} @current-permission]
|
||||
[react/view styles/permissions-panel-container
|
||||
|
@ -146,37 +146,36 @@
|
||||
(let [mentionable-users @(re-frame/subscribe [:chats/mentionable-users])
|
||||
timeout-id (atom nil)
|
||||
last-text-change (atom nil)]
|
||||
[rn/view {:style (styles/text-input-wrapper)}
|
||||
[rn/text-input
|
||||
{:style (styles/text-input)
|
||||
:ref text-input-ref
|
||||
:max-font-size-multiplier 1
|
||||
:accessibility-label :chat-message-input
|
||||
:text-align-vertical :center
|
||||
:multiline true
|
||||
:editable (not cooldown-enabled?)
|
||||
:blur-on-submit false
|
||||
:auto-focus false
|
||||
:on-focus #(set-active-panel nil)
|
||||
:max-length chat.constants/max-text-size
|
||||
:placeholder-text-color (:text-02 @colors/theme)
|
||||
:placeholder (if cooldown-enabled?
|
||||
(i18n/label :cooldown/text-input-disabled)
|
||||
(i18n/label :t/type-a-message))
|
||||
:underline-color-android :transparent
|
||||
:auto-capitalize :sentences
|
||||
:on-selection-change (partial on-selection-change timeout-id last-text-change mentionable-users)
|
||||
:on-change (partial on-change
|
||||
on-text-change last-text-change timeout-id mentionable-users)
|
||||
:on-text-input (partial on-text-input mentionable-users)}
|
||||
(for [[idx [type text]] (map-indexed
|
||||
(fn [idx item]
|
||||
[idx item])
|
||||
input-with-mentions)]
|
||||
^{:key (str idx "_" type "_" text)}
|
||||
[rn/text (when (= type :mention)
|
||||
{:style {:color "#0DA4C9"}})
|
||||
text])]]))
|
||||
[rn/text-input
|
||||
{:style (styles/text-input)
|
||||
:ref text-input-ref
|
||||
:max-font-size-multiplier 1
|
||||
:accessibility-label :chat-message-input
|
||||
:text-align-vertical :center
|
||||
:multiline true
|
||||
:editable (not cooldown-enabled?)
|
||||
:blur-on-submit false
|
||||
:auto-focus false
|
||||
:on-focus #(set-active-panel nil)
|
||||
:max-length chat.constants/max-text-size
|
||||
:placeholder-text-color (:text-02 @colors/theme)
|
||||
:placeholder (if cooldown-enabled?
|
||||
(i18n/label :cooldown/text-input-disabled)
|
||||
(i18n/label :t/type-a-message))
|
||||
:underline-color-android :transparent
|
||||
:auto-capitalize :sentences
|
||||
:on-selection-change (partial on-selection-change timeout-id last-text-change mentionable-users)
|
||||
:on-change (partial on-change
|
||||
on-text-change last-text-change timeout-id mentionable-users)
|
||||
:on-text-input (partial on-text-input mentionable-users)}
|
||||
(for [[idx [type text]] (map-indexed
|
||||
(fn [idx item]
|
||||
[idx item])
|
||||
input-with-mentions)]
|
||||
^{:key (str idx "_" type "_" text)}
|
||||
[rn/text (when (= type :mention)
|
||||
{:style {:color "#0DA4C9"}})
|
||||
text])]))
|
||||
|
||||
(defn mention-item
|
||||
[[public-key {:keys [alias name nickname] :as user}] _ _ text-input-ref]
|
||||
|
@ -38,6 +38,8 @@
|
||||
(merge typography/font-regular
|
||||
typography/base
|
||||
{:flex 1
|
||||
:min-height 34
|
||||
:max-height 144
|
||||
:margin 0
|
||||
:border-width 0
|
||||
:flex-shrink 1
|
||||
|
@ -7,7 +7,7 @@
|
||||
[status-im.ui.screens.chat.styles.input.gap :as style]))
|
||||
|
||||
(defn on-press
|
||||
[ids first-gap? idx list-ref]
|
||||
[ids first-gap? idx list-ref chat-id]
|
||||
(fn []
|
||||
(when (and list-ref @list-ref)
|
||||
(.scrollToIndex ^js @list-ref
|
||||
@ -15,24 +15,25 @@
|
||||
:viewOffset 20
|
||||
:viewPosition 0.5}))
|
||||
(if first-gap?
|
||||
(re-frame/dispatch [:chat.ui/fetch-more])
|
||||
(re-frame/dispatch [:chat.ui/fill-gaps ids]))))
|
||||
(re-frame/dispatch [:chat.ui/fetch-more chat-id])
|
||||
(re-frame/dispatch [:chat.ui/fill-gaps ids chat-id]))))
|
||||
|
||||
(views/defview gap
|
||||
[{:keys [gaps first-gap?]} idx list-ref timeline]
|
||||
(views/letsubs [range [:chats/range]
|
||||
{:keys [might-have-join-time-messages?]} [:chats/current-raw-chat]
|
||||
[{:keys [gaps first-gap?]} idx list-ref timeline chat-id]
|
||||
(views/letsubs [range [:chats/range chat-id]
|
||||
{:keys [might-have-join-time-messages?]} [:chat-by-id chat-id]
|
||||
in-progress? [:chats/fetching-gap-in-progress?
|
||||
(if first-gap?
|
||||
[:first-gap]
|
||||
(:ids gaps))]
|
||||
(:ids gaps))
|
||||
chat-id]
|
||||
connected? [:mailserver/connected?]]
|
||||
(let [ids (:ids gaps)]
|
||||
(when-not (and first-gap? might-have-join-time-messages?)
|
||||
[react/view {:style style/gap-container}
|
||||
[react/touchable-highlight
|
||||
{:on-press (when (and connected? (not in-progress?))
|
||||
(on-press ids first-gap? idx list-ref))
|
||||
(on-press ids first-gap? idx list-ref chat-id))
|
||||
:style style/touchable}
|
||||
[react/view {:style style/label-container}
|
||||
(if in-progress?
|
||||
|
@ -120,7 +120,7 @@
|
||||
outgoing colors/mention-outgoing
|
||||
:else colors/mention-incoming)}
|
||||
:on-press (when-not (= content-type constants/content-type-system-text)
|
||||
#(re-frame/dispatch [:chat.ui/show-profile-without-adding-contact literal]))}
|
||||
#(re-frame/dispatch [:chat.ui/show-profile literal]))}
|
||||
[mention-element literal]])
|
||||
"status-tag"
|
||||
(conj acc [react/text-class
|
||||
@ -286,17 +286,16 @@
|
||||
[react/view (style/message-author-userpic outgoing)
|
||||
(when first-in-group?
|
||||
[react/touchable-highlight {:on-press #(do (when modal (close-modal))
|
||||
(re-frame/dispatch [:chat.ui/show-profile-without-adding-contact from]))}
|
||||
(re-frame/dispatch [:chat.ui/show-profile from]))}
|
||||
[photos/member-photo from identicon]])])
|
||||
[react/view {:style (style/message-author-wrapper outgoing display-photo?)}
|
||||
(when display-username?
|
||||
[react/touchable-opacity {:style style/message-author-touchable
|
||||
:on-press #(do (when modal (close-modal))
|
||||
(re-frame/dispatch [:chat.ui/show-profile-without-adding-contact from]))}
|
||||
(re-frame/dispatch [:chat.ui/show-profile from]))}
|
||||
[message-author-name from {:modal modal}]])
|
||||
;;MESSAGE CONTENT
|
||||
[react/view
|
||||
content]
|
||||
content
|
||||
[link-preview/link-preview-wrapper (:links (:content message)) outgoing false]]]
|
||||
; delivery status
|
||||
[react/view (style/delivery-status outgoing)
|
||||
@ -475,7 +474,7 @@
|
||||
(on-long-press
|
||||
(when-not outgoing
|
||||
[{:on-press #(when pack
|
||||
(re-frame/dispatch [:chat.ui/show-profile-without-adding-contact from]))
|
||||
(re-frame/dispatch [:chat.ui/show-profile from]))
|
||||
:label (i18n/label :t/view-details)}])))})
|
||||
[react/image {:style {:margin 10 :width 140 :height 140}
|
||||
;;TODO (perf) move to event
|
||||
@ -512,7 +511,7 @@
|
||||
(defn chat-message [message space-keeper]
|
||||
[reactions/with-reaction-picker
|
||||
{:message message
|
||||
:reactions @(re-frame/subscribe [:chats/message-reactions (:message-id message)])
|
||||
:reactions @(re-frame/subscribe [:chats/message-reactions (:message-id message) (:chat-id message)])
|
||||
:picker-on-open (fn []
|
||||
(space-keeper true))
|
||||
:picker-on-close (fn []
|
||||
|
@ -62,35 +62,35 @@
|
||||
(reset! position pos)
|
||||
(reset! visible true))]
|
||||
[:<>
|
||||
[animated/view {:style {:opacity (animated/mix animation 1 0)}}
|
||||
[rn/view {:ref ref
|
||||
:collapsable false}
|
||||
[render message {:modal false
|
||||
:on-long-press (fn [act]
|
||||
(when (or (not outgoing)
|
||||
(and outgoing (= outgoing-status :sent)))
|
||||
(reset! actions act)
|
||||
(get-picker-position ref on-open)))}]]
|
||||
[rn/view {:ref ref
|
||||
:style {:opacity (if @visible 0 1)}
|
||||
:collapsable false}
|
||||
[render message {:modal false
|
||||
:on-long-press (fn [act]
|
||||
(when (or (not outgoing)
|
||||
(and outgoing (= outgoing-status :sent)))
|
||||
(reset! actions act)
|
||||
(get-picker-position ref on-open)))}]
|
||||
[reaction-row/message-reactions message reactions timeline]]
|
||||
[rn/modal {:visible @visible
|
||||
:on-request-close on-close
|
||||
:on-show (fn []
|
||||
(js/requestAnimationFrame
|
||||
#(animated/set-value animated-state 1)))
|
||||
:transparent true}
|
||||
[reaction-picker/modal {:outgoing (:outgoing message)
|
||||
:display-photo (:display-photo? message)
|
||||
:animation animation
|
||||
:spring spring-animation
|
||||
:top (:top @position)
|
||||
:message-height (:height @position)
|
||||
:on-close on-close
|
||||
:actions @actions
|
||||
:own-reactions own-reactions
|
||||
:timeline timeline
|
||||
:send-emoji (fn [emoji]
|
||||
(on-close)
|
||||
(js/setTimeout #(on-emoji-press emoji)
|
||||
reaction-picker/animation-duration))}
|
||||
[render message {:modal true
|
||||
:close-modal on-close}]]]]))))
|
||||
(when @visible
|
||||
[rn/modal {:on-request-close on-close
|
||||
:on-show (fn []
|
||||
(js/requestAnimationFrame
|
||||
#(animated/set-value animated-state 1)))
|
||||
:transparent true}
|
||||
[reaction-picker/modal {:outgoing (:outgoing message)
|
||||
:display-photo (:display-photo? message)
|
||||
:animation animation
|
||||
:spring spring-animation
|
||||
:top (:top @position)
|
||||
:message-height (:height @position)
|
||||
:on-close on-close
|
||||
:actions @actions
|
||||
:own-reactions own-reactions
|
||||
:timeline timeline
|
||||
:send-emoji (fn [emoji]
|
||||
(on-close)
|
||||
(js/setTimeout #(on-emoji-press emoji)
|
||||
reaction-picker/animation-duration))}
|
||||
[render message {:modal true
|
||||
:close-modal on-close}]]])]))))
|
||||
|
@ -2,5 +2,7 @@
|
||||
|
||||
(defonce first-not-visible-item (atom nil))
|
||||
|
||||
(defn reset []
|
||||
(defonce scrolling (atom nil))
|
||||
|
||||
(defn reset-visible-item []
|
||||
(reset! first-not-visible-item nil))
|
||||
|
@ -82,8 +82,8 @@
|
||||
(defview stickers-paging-panel [installed-packs selected-pack]
|
||||
(letsubs [ref (atom nil)
|
||||
width [:dimensions/window-width]]
|
||||
{:component-will-update (fn [_ [_ installed-packs selected-pack]]
|
||||
(update-scroll-position @ref installed-packs selected-pack width true))
|
||||
{:UNSAFE_componentWillUpdate (fn [_ [_ installed-packs selected-pack]]
|
||||
(update-scroll-position @ref installed-packs selected-pack width true))
|
||||
:component-did-mount #(update-scroll-position @ref installed-packs selected-pack width false)}
|
||||
[react/scroll-view {:style {:flex 1}
|
||||
:horizontal true
|
||||
|
@ -39,18 +39,19 @@
|
||||
chat-type
|
||||
chat-name
|
||||
public?]}]
|
||||
[react/view {:style st/toolbar-container}
|
||||
[react/view {:margin-right 10}
|
||||
[chat-icon.screen/chat-icon-view-toolbar chat-id group-chat chat-name color]]
|
||||
[react/view {:style st/chat-name-view}
|
||||
(if group-chat
|
||||
[react/text {:style st/chat-name-text
|
||||
:number-of-lines 1
|
||||
:accessibility-label :chat-name-text}
|
||||
chat-name]
|
||||
[one-to-one-name chat-id])
|
||||
(when-not group-chat
|
||||
[contact-indicator chat-id])
|
||||
(when (and group-chat (not invitation-admin) (not= chat-type constants/community-chat-type))
|
||||
[group-last-activity {:contacts contacts
|
||||
:public? public?}])]])
|
||||
(when chat-id
|
||||
[react/view {:style st/toolbar-container}
|
||||
[react/view {:margin-right 10}
|
||||
[chat-icon.screen/chat-icon-view-toolbar chat-id group-chat chat-name color]]
|
||||
[react/view {:style st/chat-name-view}
|
||||
(if group-chat
|
||||
[react/text {:style st/chat-name-text
|
||||
:number-of-lines 1
|
||||
:accessibility-label :chat-name-text}
|
||||
chat-name]
|
||||
[one-to-one-name chat-id])
|
||||
(when-not group-chat
|
||||
[contact-indicator chat-id])
|
||||
(when (and group-chat (not invitation-admin) (not= chat-type constants/community-chat-type))
|
||||
[group-last-activity {:contacts contacts
|
||||
:public? public?}])]]))
|
||||
|
@ -11,7 +11,6 @@
|
||||
[status-im.ui.screens.chat.sheets :as sheets]
|
||||
[quo.animated :as animated]
|
||||
[quo.react-native :as rn]
|
||||
[quo.platform :as platform]
|
||||
[status-im.ui.screens.chat.audio-message.views :as audio-message]
|
||||
[quo.react :as quo.react]
|
||||
[status-im.ui.screens.chat.message.message :as message]
|
||||
@ -20,7 +19,6 @@
|
||||
[status-im.ui.screens.chat.toolbar-content :as toolbar-content]
|
||||
[status-im.ui.screens.chat.image.views :as image]
|
||||
[status-im.ui.screens.chat.state :as state]
|
||||
[status-im.utils.debounce :as debounce]
|
||||
[status-im.ui.screens.chat.extensions.views :as extensions]
|
||||
[status-im.ui.components.topbar :as topbar]
|
||||
[status-im.ui.screens.chat.group :as chat.group]
|
||||
@ -32,20 +30,20 @@
|
||||
[status-im.ui.components.toolbar :as toolbar]
|
||||
[quo.core :as quo]
|
||||
[clojure.string :as string]
|
||||
[status-im.constants :as constants]))
|
||||
[status-im.constants :as constants]
|
||||
[status-im.utils.platform :as platform]
|
||||
[status-im.utils.utils :as utils]))
|
||||
|
||||
(defn topbar []
|
||||
(let [current-chat @(re-frame/subscribe [:current-chat/metadata])]
|
||||
[topbar/topbar
|
||||
{:content [toolbar-content/toolbar-content-view current-chat]
|
||||
:navigation {:on-press #(re-frame/dispatch [:navigate-to :home])}
|
||||
:right-accessories [{:icon :main-icons/more
|
||||
:accessibility-label :chat-menu-button
|
||||
:on-press
|
||||
#(re-frame/dispatch [:bottom-sheet/show-sheet
|
||||
{:content (fn []
|
||||
[sheets/actions current-chat])
|
||||
:height 256}])}]}]))
|
||||
(defn topbar [current-chat]
|
||||
[topbar/topbar
|
||||
{:content [toolbar-content/toolbar-content-view current-chat]
|
||||
:navigation {:on-press #(re-frame/dispatch [:close-chat (:chat-id current-chat)])}
|
||||
:right-accessories [{:icon :main-icons/more
|
||||
:accessibility-label :chat-menu-button
|
||||
:on-press #(re-frame/dispatch [:bottom-sheet/show-sheet
|
||||
{:content (fn []
|
||||
[sheets/actions current-chat])
|
||||
:height 256}])}]}])
|
||||
|
||||
(defn invitation-requests [chat-id admins]
|
||||
(let [current-pk @(re-frame/subscribe [:multiaccount/public-key])
|
||||
@ -153,60 +151,7 @@
|
||||
first-not-visible (aget (.-data ^js (.-props ^js @messages-list-ref)) (inc index))]
|
||||
(when (and first-not-visible
|
||||
(= :message (:type first-not-visible)))
|
||||
first-not-visible)))))
|
||||
(debounce/debounce-and-dispatch [:chat.ui/message-visibility-changed e] 5000))
|
||||
|
||||
(defn render-fn [{:keys [outgoing type] :as message} idx _ {:keys [group-chat public? current-public-key space-keeper]}]
|
||||
[react/view {:style (when platform/android? {:scaleY -1})}
|
||||
(if (= type :datemark)
|
||||
[message-datemark/chat-datemark (:value message)]
|
||||
(if (= type :gap)
|
||||
[gap/gap message idx messages-list-ref false]
|
||||
; message content
|
||||
[message/chat-message
|
||||
(assoc message
|
||||
:incoming-group (and group-chat (not outgoing))
|
||||
:group-chat group-chat
|
||||
:public? public?
|
||||
:current-public-key current-public-key)
|
||||
space-keeper]))])
|
||||
|
||||
(defn messages-view
|
||||
[{:keys [chat bottom-space pan-responder space-keeper]}]
|
||||
(let [{:keys [group-chat chat-id chat-type public? invitation-admin]} chat
|
||||
|
||||
messages @(re-frame/subscribe [:chats/current-chat-messages-stream])
|
||||
no-messages? @(re-frame/subscribe [:chats/current-chat-no-messages?])
|
||||
current-public-key @(re-frame/subscribe [:multiaccount/public-key])]
|
||||
[list/flat-list
|
||||
(merge
|
||||
pan-responder
|
||||
{:key-fn #(or (:message-id %) (:value %))
|
||||
:ref #(reset! messages-list-ref %)
|
||||
:header (when (= chat-type constants/private-group-chat-type)
|
||||
[react/view {:style (when platform/android? {:scaleY -1})}
|
||||
[chat.group/group-chat-footer chat-id invitation-admin]])
|
||||
:footer [react/view {:style (when platform/android? {:scaleY -1})}
|
||||
[chat-intro-header-container chat no-messages?]
|
||||
(when (= chat-type constants/one-to-one-chat-type)
|
||||
[invite.chat/reward-messages])]
|
||||
:data messages
|
||||
;;TODO https://github.com/facebook/react-native/issues/30034
|
||||
:inverted (when platform/ios? true)
|
||||
:style (when platform/android? {:scaleY -1})
|
||||
:render-data {:group-chat group-chat
|
||||
:public? public?
|
||||
:current-public-key current-public-key
|
||||
:space-keeper space-keeper}
|
||||
:render-fn render-fn
|
||||
:on-viewable-items-changed on-viewable-items-changed
|
||||
:on-end-reached #(re-frame/dispatch [:chat.ui/load-more-messages])
|
||||
:on-scroll-to-index-failed #() ;;don't remove this
|
||||
:content-container-style {:padding-top (+ bottom-space 16)
|
||||
:padding-bottom 16}
|
||||
:scrollIndicatorInsets {:top bottom-space}
|
||||
:keyboardDismissMode "interactive"
|
||||
:keyboard-should-persist-taps :handled})]))
|
||||
first-not-visible))))))
|
||||
|
||||
(defn bottom-sheet [input-bottom-sheet]
|
||||
(case input-bottom-sheet
|
||||
@ -258,72 +203,155 @@
|
||||
:on-press #(re-frame/dispatch [:send-group-chat-membership-request])}
|
||||
(i18n/label :t/request-membership)]}])]))
|
||||
|
||||
(defn get-space-keeper-ios [bottom-space panel-space active-panel text-input-ref]
|
||||
(fn [state]
|
||||
;; NOTE: Only iOs now because we use soft input resize screen on android
|
||||
(when platform/ios?
|
||||
(cond
|
||||
(and state
|
||||
(< @bottom-space @panel-space)
|
||||
(not @active-panel))
|
||||
(reset! bottom-space @panel-space)
|
||||
|
||||
(and (not state)
|
||||
(< @panel-space @bottom-space))
|
||||
(do
|
||||
(some-> ^js (quo.react/current-ref text-input-ref) .focus)
|
||||
(reset! panel-space @bottom-space)
|
||||
(reset! bottom-space 0))))))
|
||||
|
||||
(defn get-set-active-panel [active-panel]
|
||||
(fn [panel]
|
||||
(rn/configure-next
|
||||
(:ease-opacity-200 rn/custom-animations))
|
||||
(reset! active-panel panel)
|
||||
(reagent/flush)
|
||||
(when panel
|
||||
(js/setTimeout #(react/dismiss-keyboard!) 100))))
|
||||
|
||||
(defn list-footer [{:keys [chat-id chat-type] :as chat}]
|
||||
(let [loading-messages? @(re-frame/subscribe [:chats/loading-messages? chat-id])
|
||||
no-messages? @(re-frame/subscribe [:chats/chat-no-messages? chat-id])
|
||||
all-loaded? @(re-frame/subscribe [:chats/all-loaded? chat-id])]
|
||||
[react/view {:style (when platform/android? {:scaleY -1})}
|
||||
(if (or loading-messages? (not chat-id) (not all-loaded?))
|
||||
[react/view {:height 324 :align-items :center :justify-content :center}
|
||||
[react/activity-indicator {:animating true}]]
|
||||
[chat-intro-header-container chat no-messages?])
|
||||
(when (= chat-type constants/one-to-one-chat-type)
|
||||
[invite.chat/reward-messages])]))
|
||||
|
||||
(defn list-header [{:keys [chat-id chat-type invitation-admin]}]
|
||||
(when (= chat-type constants/private-group-chat-type)
|
||||
[react/view {:style (when platform/android? {:scaleY -1})}
|
||||
[chat.group/group-chat-footer chat-id invitation-admin]]))
|
||||
|
||||
(defn render-fn [{:keys [outgoing type] :as message}
|
||||
idx
|
||||
_
|
||||
{:keys [group-chat public? current-public-key space-keeper chat-id]}]
|
||||
[react/view {:style (when platform/android? {:scaleY -1})}
|
||||
(if (= type :datemark)
|
||||
[message-datemark/chat-datemark (:value message)]
|
||||
(if (= type :gap)
|
||||
[gap/gap message idx messages-list-ref false chat-id]
|
||||
; message content
|
||||
[message/chat-message
|
||||
(assoc message
|
||||
:incoming-group (and group-chat (not outgoing))
|
||||
:group-chat group-chat
|
||||
:public? public?
|
||||
:current-public-key current-public-key)
|
||||
space-keeper]))])
|
||||
|
||||
(defn messages-view
|
||||
[{:keys [chat bottom-space pan-responder space-keeper]}]
|
||||
(let [{:keys [group-chat chat-id public?]} chat
|
||||
messages @(re-frame/subscribe [:chats/chat-messages-stream chat-id])
|
||||
current-public-key @(re-frame/subscribe [:multiaccount/public-key])]
|
||||
[list/flat-list
|
||||
(merge
|
||||
pan-responder
|
||||
{:key-fn #(or (:message-id %) (:value %))
|
||||
:ref #(reset! messages-list-ref %)
|
||||
:header [list-header chat]
|
||||
:footer [list-footer chat]
|
||||
:data messages
|
||||
:render-data {:group-chat group-chat
|
||||
:public? public?
|
||||
:current-public-key current-public-key
|
||||
:space-keeper space-keeper
|
||||
:chat-id chat-id}
|
||||
:render-fn render-fn
|
||||
:on-viewable-items-changed on-viewable-items-changed
|
||||
;;TODO this is not really working in pair with inserting new messages because we stop inserting new messages
|
||||
;;if they outside the viewarea, but we load more here because end is reached,so its slowdown UI because we
|
||||
;;load and render 20 messages more, but we can't prevent this , because otherwise :on-end-reached will work wrong
|
||||
:on-end-reached (fn []
|
||||
(if @state/scrolling
|
||||
(re-frame/dispatch [:chat.ui/load-more-messages chat-id])
|
||||
(utils/set-timeout #(re-frame/dispatch [:chat.ui/load-more-messages chat-id])
|
||||
(if platform/low-device? 500 200))))
|
||||
|
||||
:on-scroll-to-index-failed #() ;;don't remove this
|
||||
:content-container-style {:padding-top (+ bottom-space 16)
|
||||
:padding-bottom 16}
|
||||
:scroll-indicator-insets {:top bottom-space} ;;ios only
|
||||
:keyboard-dismiss-mode :interactive
|
||||
:keyboard-should-persist-taps :handled
|
||||
:onMomentumScrollBegin #(reset! state/scrolling true)
|
||||
:onMomentumScrollEnd #(reset! state/scrolling false)
|
||||
;;TODO https://github.com/facebook/react-native/issues/30034
|
||||
:inverted (when platform/ios? true)
|
||||
:style (when platform/android? {:scaleY -1})})]))
|
||||
|
||||
(defn chat []
|
||||
(let [bottom-space (reagent/atom 0)
|
||||
panel-space (reagent/atom 0)
|
||||
panel-space (reagent/atom 52)
|
||||
active-panel (reagent/atom nil)
|
||||
position-y (animated/value 0)
|
||||
pan-state (animated/value 0)
|
||||
text-input-ref (quo.react/create-ref)
|
||||
on-update (partial reset! panel-space)
|
||||
on-update #(when-not (zero? %) (reset! panel-space %))
|
||||
pan-responder (accessory/create-pan-responder position-y pan-state)
|
||||
space-keeper (fn [state]
|
||||
;; NOTE: Only iOs now because we use soft input resize screen on android
|
||||
(when platform/ios?
|
||||
(cond
|
||||
(and state
|
||||
(< @bottom-space @panel-space)
|
||||
(not @active-panel))
|
||||
(reset! bottom-space @panel-space)
|
||||
|
||||
(and (not state)
|
||||
(< @panel-space @bottom-space))
|
||||
(do
|
||||
(some-> ^js (quo.react/current-ref text-input-ref) .focus)
|
||||
(reset! panel-space @bottom-space)
|
||||
(reset! bottom-space 0)))))
|
||||
set-active-panel (fn [panel]
|
||||
(rn/configure-next
|
||||
(:ease-opacity-200 rn/custom-animations))
|
||||
(reset! active-panel panel)
|
||||
(reagent/flush)
|
||||
(when panel
|
||||
(js/setTimeout #(react/dismiss-keyboard!) 100)))
|
||||
space-keeper (get-space-keeper-ios bottom-space panel-space active-panel text-input-ref)
|
||||
set-active-panel (get-set-active-panel active-panel)
|
||||
on-text-change #(re-frame/dispatch [:chat.ui/set-chat-input-text %])]
|
||||
(fn []
|
||||
(let [{:keys [chat-id show-input? group-chat admins invitation-admin] :as current-chat}
|
||||
@(re-frame/subscribe [:chats/current-chat])]
|
||||
(when current-chat
|
||||
[react/view {:style {:flex 1}}
|
||||
[connectivity/loading-indicator]
|
||||
[topbar]
|
||||
[react/view {:style {:flex 1}}
|
||||
[:<>
|
||||
[connectivity/loading-indicator]
|
||||
[topbar current-chat]
|
||||
[:<>
|
||||
(when current-chat
|
||||
(if group-chat
|
||||
[invitation-requests chat-id admins]
|
||||
[add-contact-bar chat-id])
|
||||
[messages-view {:chat current-chat
|
||||
:bottom-space (max @bottom-space @panel-space)
|
||||
:pan-responder pan-responder
|
||||
:space-keeper space-keeper}]]
|
||||
(when (and group-chat invitation-admin)
|
||||
[accessory/view {:y position-y
|
||||
:on-update-inset on-update}
|
||||
[invitation-bar chat-id]])
|
||||
;; NOTE(rasom): on android we have to place `autocomplete-mentions`
|
||||
;; outside `accessory/view` because otherwise :keyboardShouldPersistTaps
|
||||
;; :always doesn't work and keyboard is hidden on pressing suggestion.
|
||||
;; Scrolling of suggestions doesn't work neither in this case.
|
||||
(when platform/android?
|
||||
[components/autocomplete-mentions text-input-ref])
|
||||
(when show-input?
|
||||
[accessory/view {:y position-y
|
||||
:pan-state pan-state
|
||||
:has-panel (boolean @active-panel)
|
||||
:on-close #(set-active-panel nil)
|
||||
:on-update-inset on-update}
|
||||
[components/chat-toolbar
|
||||
{:active-panel @active-panel
|
||||
:set-active-panel set-active-panel
|
||||
:text-input-ref text-input-ref
|
||||
:on-text-change on-text-change}]
|
||||
[bottom-sheet @active-panel]])])))))
|
||||
[add-contact-bar chat-id]))
|
||||
;;MESSAGES LIST
|
||||
[messages-view {:chat current-chat
|
||||
:bottom-space (max @bottom-space @panel-space)
|
||||
:pan-responder pan-responder
|
||||
:space-keeper space-keeper}]]
|
||||
(when (and group-chat invitation-admin)
|
||||
[accessory/view {:y position-y
|
||||
:on-update-inset on-update}
|
||||
[invitation-bar chat-id]])
|
||||
;; NOTE(rasom): on android we have to place `autocomplete-mentions`
|
||||
;; outside `accessory/view` because otherwise :keyboardShouldPersistTaps
|
||||
;; :always doesn't work and keyboard is hidden on pressing suggestion.
|
||||
;; Scrolling of suggestions doesn't work neither in this case.
|
||||
(when platform/android?
|
||||
[components/autocomplete-mentions text-input-ref])
|
||||
(when show-input?
|
||||
[accessory/view {:y position-y
|
||||
:pan-state pan-state
|
||||
:has-panel (boolean @active-panel)
|
||||
:on-close #(set-active-panel nil)
|
||||
:on-update-inset on-update}
|
||||
[components/chat-toolbar
|
||||
{:active-panel @active-panel
|
||||
:set-active-panel set-active-panel
|
||||
:text-input-ref text-input-ref
|
||||
:on-text-change on-text-change}]
|
||||
[bottom-sheet @active-panel]])]))))
|
||||
|
@ -169,9 +169,8 @@
|
||||
(when (or (seq items) @search-active? (seq search-filter))
|
||||
[search-input-wrapper search-filter items])
|
||||
[referral-item/list-item]
|
||||
(when
|
||||
(and (empty? items)
|
||||
(or @search-active? (seq search-filter)))
|
||||
(when (and (empty? items)
|
||||
(or @search-active? (seq search-filter)))
|
||||
[start-suggestion search-filter])]
|
||||
:footer (if (and (not hide-home-tooltip?) (not @search-active?))
|
||||
[home-tooltip-view]
|
||||
@ -194,9 +193,9 @@
|
||||
|
||||
(defn home []
|
||||
[react/keyboard-avoiding-view {:style styles/home-container}
|
||||
[topbar/topbar {:title (i18n/label :t/chat)
|
||||
:navigation :none
|
||||
:right-component [connectivity/connectivity-button]}]
|
||||
[topbar/topbar {:title (i18n/label :t/chat)
|
||||
:navigation :none
|
||||
:right-component [connectivity/connectivity-button]}]
|
||||
[connectivity/loading-indicator]
|
||||
[chats-list]
|
||||
[plus-button]])
|
||||
|
@ -174,13 +174,10 @@
|
||||
[message-content-text {:content (:content last-message)
|
||||
:content-type (:content-type last-message)}]]
|
||||
[unviewed-indicator home-item]]
|
||||
:on-press #(do
|
||||
(re-frame/dispatch [:dismiss-keyboard])
|
||||
(re-frame/dispatch [:chat.ui/navigate-to-chat chat-id])
|
||||
(re-frame/dispatch [:search/home-filter-changed nil])
|
||||
(if public?
|
||||
(re-frame/dispatch [:chat.ui/mark-public-all-read chat-id])
|
||||
(re-frame/dispatch [:chat.ui/mark-messages-seen :chat])))
|
||||
:on-press (fn []
|
||||
(re-frame/dispatch [:dismiss-keyboard])
|
||||
(re-frame/dispatch [:chat.ui/navigate-to-chat chat-id])
|
||||
(re-frame/dispatch [:search/home-filter-changed nil]))
|
||||
:on-long-press #(re-frame/dispatch [:bottom-sheet/show-sheet
|
||||
{:content (fn []
|
||||
[sheets/actions home-item])}])}]))
|
||||
|
@ -69,7 +69,7 @@
|
||||
"hardwareBackPress"
|
||||
request-close)))]
|
||||
(reagent/create-class
|
||||
{:component-will-update
|
||||
{:UNSAFE_componentWillUpdate
|
||||
(fn [_ [_ popover _]]
|
||||
(when @clear-timeout (js/clearTimeout @clear-timeout))
|
||||
(cond
|
||||
|
@ -20,8 +20,7 @@
|
||||
[clojure.string :as string]
|
||||
[quo.components.list.item :as list-item]
|
||||
[status-im.ui.components.list.views :as list]
|
||||
[status-im.ui.screens.status.views :as status.views]
|
||||
[status-im.ui.screens.chat.views :as chat.views])
|
||||
[status-im.ui.screens.status.views :as status.views])
|
||||
(:require-macros [status-im.utils.views :as views]))
|
||||
|
||||
(defn actions
|
||||
@ -156,60 +155,54 @@
|
||||
:number-of-lines 2}
|
||||
label]]])
|
||||
|
||||
(defn status []
|
||||
(let [messages @(re-frame/subscribe [:chats/current-chat-messages-stream])
|
||||
no-messages? @(re-frame/subscribe [:chats/current-chat-no-messages?])
|
||||
{:keys [profile-public-key]} @(re-frame/subscribe [:chats/current-raw-chat])]
|
||||
(when profile-public-key
|
||||
[list/flat-list
|
||||
{:key-fn #(or (:message-id %) (:value %))
|
||||
:header (when no-messages?
|
||||
[react/view {:padding-horizontal 32 :margin-top 32}
|
||||
[react/view (styles/updates-descr-cont)
|
||||
[react/text {:style {:color colors/gray :line-height 22}}
|
||||
(i18n/label :t/status-updates-descr)]]])
|
||||
:ref #(reset! status.views/messages-list-ref %)
|
||||
:on-viewable-items-changed chat.views/on-viewable-items-changed
|
||||
:on-end-reached #(re-frame/dispatch [:chat.ui/load-more-messages])
|
||||
:on-scroll-to-index-failed #() ;;don't remove this
|
||||
:render-fn status.views/render-message
|
||||
:data messages}])))
|
||||
|
||||
(views/defview profile []
|
||||
(views/letsubs [{:keys [public-key name ens-verified]
|
||||
:as contact} [:contacts/current-contact]]
|
||||
(let [muted? (:muted @(re-frame/subscribe [:chats/chat public-key]))
|
||||
[first-name second-name] (multiaccounts/contact-two-names contact true)
|
||||
on-share #(re-frame/dispatch [:show-popover (merge
|
||||
{:view :share-chat-key
|
||||
:address public-key}
|
||||
(when (and ens-verified name)
|
||||
{:ens-name name}))])]
|
||||
(when contact
|
||||
[react/view {:flex 1}
|
||||
[quo/animated-header
|
||||
{:use-insets false
|
||||
:right-accessories [{:icon :main-icons/share
|
||||
:accessibility-label :share-button
|
||||
:on-press on-share}]
|
||||
:left-accessories [{:icon :main-icons/close
|
||||
:accessibility-label :back-button
|
||||
:on-press #(re-frame/dispatch [:navigate-back])}]
|
||||
:extended-header (profile-header/extended-header
|
||||
{:on-press on-share
|
||||
:bottom-separator false
|
||||
:title first-name
|
||||
:photo (multiaccounts/displayed-photo contact)
|
||||
:monospace (not ens-verified)
|
||||
:subtitle second-name})}
|
||||
[react/view {:height 1 :background-color colors/gray-lighter :margin-top 8}]
|
||||
[nickname-settings contact]
|
||||
[react/view {:height 1 :background-color colors/gray-lighter}]
|
||||
[react/view {:padding-top 17 :flex-direction :row :align-items :stretch :flex 1}
|
||||
(for [{:keys [label] :as action} (actions contact muted?)
|
||||
:when label]
|
||||
^{:key label}
|
||||
[button-item action])]
|
||||
[react/view {:height 1 :background-color colors/gray-lighter :margin-top 16}]
|
||||
[status]]]))))
|
||||
(defn profile []
|
||||
(let [{:keys [public-key name ens-verified] :as contact} @(re-frame/subscribe [:contacts/current-contact])
|
||||
current-chat-id @(re-frame/subscribe [:chats/current-profile-chat])
|
||||
messages @(re-frame/subscribe [:chats/chat-messages-stream current-chat-id])
|
||||
no-messages? @(re-frame/subscribe [:chats/chat-no-messages? current-chat-id])
|
||||
muted? (:muted @(re-frame/subscribe [:chats/chat public-key]))
|
||||
[first-name second-name] (multiaccounts/contact-two-names contact true)
|
||||
on-share #(re-frame/dispatch [:show-popover (merge
|
||||
{:view :share-chat-key
|
||||
:address public-key}
|
||||
(when (and ens-verified name)
|
||||
{:ens-name name}))])]
|
||||
(when contact
|
||||
[:<>
|
||||
[quo/header {:right-accessories [{:icon :main-icons/share
|
||||
:accessibility-label :share-button
|
||||
:on-press on-share}]
|
||||
:left-accessories [{:icon :main-icons/close
|
||||
:accessibility-label :back-button
|
||||
:on-press #(re-frame/dispatch [:navigate-back])}]}]
|
||||
[list/flat-list
|
||||
{:key-fn #(or (:message-id %) (:value %))
|
||||
:header [:<>
|
||||
[(profile-header/extended-header
|
||||
{:on-press on-share
|
||||
:bottom-separator false
|
||||
:title first-name
|
||||
:photo (multiaccounts/displayed-photo contact)
|
||||
:monospace (not ens-verified)
|
||||
:subtitle second-name})]
|
||||
[react/view {:height 1 :background-color colors/gray-lighter :margin-top 8}]
|
||||
[nickname-settings contact]
|
||||
[react/view {:height 1 :background-color colors/gray-lighter}]
|
||||
[react/view {:padding-top 17 :flex-direction :row :align-items :stretch :flex 1}
|
||||
(for [{:keys [label] :as action} (actions contact muted?)
|
||||
:when label]
|
||||
^{:key label}
|
||||
[button-item action])]
|
||||
[react/view {:height 1 :background-color colors/gray-lighter :margin-top 16}]
|
||||
(when no-messages?
|
||||
[react/view {:padding-horizontal 32 :margin-top 32}
|
||||
[react/view (styles/updates-descr-cont)
|
||||
[react/text {:style {:color colors/gray :line-height 22}}
|
||||
(i18n/label :t/status-updates-descr)]]])]
|
||||
:ref #(reset! status.views/messages-list-ref %)
|
||||
:on-end-reached #(re-frame/dispatch [:chat.ui/load-more-messages current-chat-id])
|
||||
:on-scroll-to-index-failed #() ;;don't remove this
|
||||
:render-data {:chat-id current-chat-id}
|
||||
:render-fn status.views/render-message
|
||||
:data messages}]])))
|
||||
|
||||
|
@ -25,8 +25,8 @@
|
||||
(defonce communities-stack (navigation/create-stack))
|
||||
|
||||
(defn chat-stack []
|
||||
[stack {:initial-route-name :home
|
||||
:header-mode :none}
|
||||
[stack {:initial-route-name :home
|
||||
:header-mode :none}
|
||||
[{:name :home
|
||||
:style {:padding-bottom tabbar.styles/tabs-diff}
|
||||
:component home/home}
|
||||
|
@ -9,6 +9,7 @@
|
||||
[stack {:initial-route-name :status
|
||||
:header-mode :none}
|
||||
[{:name :status
|
||||
:on-focus [:init-timeline-chat]
|
||||
:insets {:top true}
|
||||
:style {:padding-bottom tabbar.styles/tabs-diff}
|
||||
:component status.views/timeline}]])
|
||||
:component status.views/timeline}]])
|
||||
|
@ -19,16 +19,16 @@
|
||||
[react/view styles/buttons
|
||||
[pressable/pressable {:type :scale
|
||||
:accessibility-label :take-picture
|
||||
:on-press #(re-frame/dispatch [:chat.ui/show-image-picker-camera])}
|
||||
:on-press #(re-frame/dispatch [:chat.ui/show-image-picker-camera-timeline])}
|
||||
[icons/icon :main-icons/camera]]
|
||||
[react/view {:style {:padding-top 8}}
|
||||
[pressable/pressable {:on-press #(re-frame/dispatch [:chat.ui/open-image-picker])
|
||||
[pressable/pressable {:on-press #(re-frame/dispatch [:chat.ui/open-image-picker-timeline])
|
||||
:accessibility-label :open-gallery
|
||||
:type :scale}
|
||||
[icons/icon :main-icons/gallery]]]])
|
||||
|
||||
(defn image-preview [uri]
|
||||
[react/touchable-highlight {:on-press #(re-frame/dispatch [:chat.ui/camera-roll-pick uri])}
|
||||
[react/touchable-highlight {:on-press #(re-frame/dispatch [:chat.ui/camera-roll-pick-timeline uri])}
|
||||
[react/image {:style styles/image
|
||||
:source {:uri uri}}]])
|
||||
|
||||
@ -52,8 +52,8 @@
|
||||
scroll (reagent/atom nil)
|
||||
autoscroll? (reagent/atom false)
|
||||
scroll-height (reagent/atom nil)
|
||||
input-text (re-frame/subscribe [:chats/current-chat-input-text])
|
||||
sending-image (re-frame/subscribe [:chats/sending-image])]
|
||||
input-text (re-frame/subscribe [:chats/timeline-chat-input-text])
|
||||
sending-image (re-frame/subscribe [:chats/timeline-sending-image])]
|
||||
(fn []
|
||||
(let [{:keys [uri]} (first (vals @sending-image))]
|
||||
[kb-presentation/keyboard-avoiding-view {:style {:flex 1}}
|
||||
@ -83,7 +83,7 @@
|
||||
@scroll-height
|
||||
-40)]
|
||||
(.scrollTo @scroll #js {:y height :animated true})))
|
||||
:on-change-text #(re-frame/dispatch [:chat.ui/set-chat-input-text %])
|
||||
:on-change-text #(re-frame/dispatch [:chat.ui/set-timeline-input-text %])
|
||||
:default-value @input-text
|
||||
:placeholder (i18n/label :t/whats-on-your-mind)}]
|
||||
(when uri
|
||||
|
@ -11,7 +11,6 @@
|
||||
[status-im.ui.components.list.views :as list]
|
||||
[status-im.i18n.i18n :as i18n]
|
||||
[status-im.ui.screens.status.styles :as styles]
|
||||
[status-im.ui.screens.chat.views :as chat.views]
|
||||
[status-im.ui.components.plus-button :as components.plus-button]
|
||||
[status-im.ui.screens.chat.image.preview.views :as preview]
|
||||
[status-im.ui.screens.chat.photos :as photos]
|
||||
@ -65,7 +64,7 @@
|
||||
:background-color :transparent
|
||||
:border-color colors/black-transparent}]
|
||||
(when show-close?
|
||||
[react/touchable-highlight {:on-press #(re-frame/dispatch [:chat.ui/cancel-sending-image])
|
||||
[react/touchable-highlight {:on-press #(re-frame/dispatch [:chat.ui/cancel-sending-image-timeline])
|
||||
:accessibility-label :cancel-send-image
|
||||
:style {:right 4 :top 12 :position :absolute}}
|
||||
[react/view {:width 24
|
||||
@ -110,7 +109,7 @@
|
||||
{:border-radius 16}))
|
||||
[react/touchable-highlight
|
||||
{:on-press #(do (when modal (close-modal))
|
||||
(re-frame/dispatch [:chat.ui/show-profile-without-adding-contact from]))}
|
||||
(re-frame/dispatch [:chat.ui/show-profile from]))}
|
||||
[react/view {:padding-top 2 :padding-right 8}
|
||||
(if outgoing
|
||||
[photos/account-photo account]
|
||||
@ -120,7 +119,7 @@
|
||||
:justify-content :space-between}
|
||||
[react/touchable-highlight
|
||||
{:on-press #(do (when modal (close-modal))
|
||||
(re-frame/dispatch [:chat.ui/show-profile-without-adding-contact from]))}
|
||||
(re-frame/dispatch [:chat.ui/show-profile from]))}
|
||||
(if outgoing
|
||||
[message/message-my-name {:profile? true :you? false}]
|
||||
[message/message-author-name from {:profile? true}])]
|
||||
@ -134,14 +133,14 @@
|
||||
[message/render-parsed-text (assoc message :outgoing false) (:parsed-text content)]])
|
||||
[link-preview/link-preview-wrapper (:links content) outgoing true]]]]))
|
||||
|
||||
(defn render-message [{:keys [type] :as message} idx _ {:keys [timeline account]}]
|
||||
(defn render-message [{:keys [type] :as message} idx _ {:keys [timeline account chat-id]}]
|
||||
(if (= type :datemark)
|
||||
nil
|
||||
(if (= type :gap)
|
||||
(if timeline
|
||||
nil
|
||||
[gap/gap message idx messages-list-ref true])
|
||||
; message content
|
||||
[gap/gap message idx messages-list-ref true chat-id])
|
||||
;; for timeline for reactions we need to use :from as chat-id
|
||||
(let [chat-id (chat/profile-chat-topic (:from message))]
|
||||
[react/view (merge {:accessibility-label :chat-item}
|
||||
(when (:last-in-group? message)
|
||||
@ -152,7 +151,7 @@
|
||||
[reactions/with-reaction-picker
|
||||
{:message message
|
||||
:timeline true
|
||||
:reactions @(re-frame/subscribe [:chats/message-reactions (:message-id message)])
|
||||
:reactions @(re-frame/subscribe [:chats/message-reactions (:message-id message) constants/timeline-chat-id])
|
||||
:picker-on-open (fn [])
|
||||
:picker-on-close (fn [])
|
||||
:send-emoji (fn [{:keys [emoji-id]}]
|
||||
@ -182,37 +181,38 @@
|
||||
|
||||
(defn timeline []
|
||||
(let [messages @(re-frame/subscribe [:chats/timeline-messages-stream])
|
||||
no-messages? @(re-frame/subscribe [:chats/current-chat-no-messages?])
|
||||
loading-messages? @(re-frame/subscribe [:chats/loading-messages? constants/timeline-chat-id])
|
||||
no-messages? @(re-frame/subscribe [:chats/chat-no-messages? constants/timeline-chat-id])
|
||||
account @(re-frame/subscribe [:multiaccount])]
|
||||
[react/view {:flex 1}
|
||||
;;TODO implement in the next iteration
|
||||
#_[tabs]
|
||||
[react/view {:height 1
|
||||
:background-color colors/gray-lighter}]
|
||||
(if no-messages?
|
||||
[react/view {:padding-horizontal 32
|
||||
:margin-top 64}
|
||||
[react/image {:style {:width 140
|
||||
:height 140
|
||||
:align-self :center}
|
||||
:source {:uri (contenthash/url image-hash)}}]
|
||||
[react/view (styles/descr-container)
|
||||
[react/text {:style {:color colors/gray
|
||||
:line-height 22}}
|
||||
(if (= :timeline (:tab @state))
|
||||
(i18n/label :t/statuses-descr)
|
||||
(i18n/label :t/statuses-my-status-descr))]]]
|
||||
[list/flat-list
|
||||
{:key-fn #(or (:message-id %) (:value %))
|
||||
:render-data {:timeline (= :timeline (:tab @state))
|
||||
:account account}
|
||||
:render-fn render-message
|
||||
:data messages
|
||||
:on-viewable-items-changed chat.views/on-viewable-items-changed
|
||||
:on-end-reached #(re-frame/dispatch [:chat.ui/load-more-messages])
|
||||
;;don't remove :on-scroll-to-index-failed
|
||||
:on-scroll-to-index-failed #()
|
||||
:header [react/view {:height 8}]
|
||||
:footer [react/view {:height 68}]}])
|
||||
(if loading-messages?
|
||||
[react/view {:flex 1 :align-items :center :justify-content :center}
|
||||
[react/activity-indicator {:animating true}]]
|
||||
(if no-messages?
|
||||
[react/view {:padding-horizontal 32
|
||||
:margin-top 64}
|
||||
[react/image {:style {:width 140
|
||||
:height 140
|
||||
:align-self :center}
|
||||
:source {:uri (contenthash/url image-hash)}}]
|
||||
[react/view (styles/descr-container)
|
||||
[react/text {:style {:color colors/gray
|
||||
:line-height 22}}
|
||||
(if (= :timeline (:tab @state))
|
||||
(i18n/label :t/statuses-descr)
|
||||
(i18n/label :t/statuses-my-status-descr))]]]
|
||||
[list/flat-list
|
||||
{:key-fn #(or (:message-id %) (:value %))
|
||||
:render-data {:timeline (= :timeline (:tab @state))
|
||||
:account account}
|
||||
:render-fn render-message
|
||||
:data messages
|
||||
:on-end-reached #(re-frame/dispatch [:chat.ui/load-more-messages constants/timeline-chat-id])
|
||||
;;don't remove :on-scroll-to-index-failed
|
||||
:on-scroll-to-index-failed #()
|
||||
:header [react/view {:height 8}]
|
||||
:footer [react/view {:height 68}]}]))
|
||||
[components.plus-button/plus-button
|
||||
{:on-press #(re-frame/dispatch [:navigate-to :my-status])}]]))
|
||||
|
@ -33,3 +33,5 @@
|
||||
|
||||
(defn android-version>= [v]
|
||||
(and android? (>= version v)))
|
||||
|
||||
(def low-device? (and android? (< version 29)))
|
||||
|
@ -23,6 +23,13 @@
|
||||
(catch js/Error _
|
||||
(when (string? json) json)))))
|
||||
|
||||
(defn json->js [json]
|
||||
(when-not (= json "undefined")
|
||||
(try
|
||||
(.parse js/JSON json)
|
||||
(catch js/Error _
|
||||
(when (string? json) json)))))
|
||||
|
||||
(def serialize clj->json)
|
||||
(defn deserialize [o] (try (json->clj o)
|
||||
(catch :default _ nil)))
|
||||
|
@ -98,7 +98,8 @@
|
||||
:utils/dispatch-later
|
||||
(fn [params]
|
||||
(doseq [{:keys [ms dispatch]} params]
|
||||
(set-timeout #(re-frame/dispatch dispatch) ms))))
|
||||
(when (and ms dispatch)
|
||||
(set-timeout #(re-frame/dispatch dispatch) ms)))))
|
||||
|
||||
(defn clear-timeout [id]
|
||||
(.clearTimeout background-timer id))
|
||||
|
@ -383,7 +383,8 @@
|
||||
(when-not (= symbol :ETH)
|
||||
address)
|
||||
from-address]
|
||||
:on-success #(re-frame/dispatch [:transport/message-sent % 1])}]})))
|
||||
:js-response true
|
||||
:on-success #(re-frame/dispatch [:transport/message-sent %])}]})))
|
||||
|
||||
(fx/defn accept-request-transaction-button-clicked-from-command
|
||||
{:events [:wallet.ui/accept-request-transaction-button-clicked-from-command]}
|
||||
|
Loading…
x
Reference in New Issue
Block a user