From 6aae0b76b94843ab31c158eb94b9f3895a0edfd0 Mon Sep 17 00:00:00 2001 From: bitsikka Date: Wed, 27 Feb 2019 18:43:56 +0545 Subject: [PATCH] [#7454] fix add basic copy to public chat empty screen state + related issues/feature regarding chat message-views intro screens for all chats types [#7454] fix add basic copy to public chat empty screen state + chat messages-views intro screens for all chats Signed-off-by: Igor Mandrigin --- src/status_im/chat/db.cljs | 8 +- src/status_im/chat/models.cljs | 33 ++- src/status_im/chat/models/message.cljs | 61 +++-- src/status_im/chat/subs.cljs | 51 +++- src/status_im/events.cljs | 5 + src/status_im/group_chats/db.cljs | 10 + src/status_im/mailserver/core.cljs | 70 ++++- .../ui/components/chat_icon/screen.cljs | 6 + .../ui/screens/chat/styles/main.cljs | 75 ++++-- src/status_im/ui/screens/chat/utils.cljs | 2 +- src/status_im/ui/screens/chat/views.cljs | 255 +++++++++++------- src/status_im/ui/screens/events.cljs | 6 + src/status_im/ui/screens/home/views.cljs | 6 +- test/cljs/status_im/test/mailserver/core.cljs | 181 +++++++++++++ translations/en.json | 5 + 15 files changed, 612 insertions(+), 162 deletions(-) diff --git a/src/status_im/chat/db.cljs b/src/status_im/chat/db.cljs index 37047f23e4..2d6e20deeb 100644 --- a/src/status_im/chat/db.cljs +++ b/src/status_im/chat/db.cljs @@ -17,10 +17,16 @@ (let [pending-invite-inviter-name (group-chats.db/get-pending-invite-inviter-name contacts chat - current-public-key)] + current-public-key) + inviter-name + (group-chats.db/get-inviter-name contacts + chat + current-public-key)] (cond-> chat pending-invite-inviter-name (assoc :pending-invite-inviter-name pending-invite-inviter-name) + inviter-name + (assoc :inviter-name inviter-name) :always (assoc :chat-name (group-chat-name chat)))) (let [{contact-name :name :as contact} diff --git a/src/status_im/chat/models.cljs b/src/status_im/chat/models.cljs index 68738d1834..001ab283cf 100644 --- a/src/status_im/chat/models.cljs +++ b/src/status_im/chat/models.cljs @@ -56,6 +56,22 @@ [{:keys [current-chat-id] :as db} ui-element] (update-in db [:chat-ui-props current-chat-id ui-element] not)) +(defn join-time-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-time-messages-checked + dissociates these two fileds via this function, thereby signalling that the + public chat is not fresh anymore." + [{:keys [chats] :as db} chat-id] + (if (:might-have-join-time-messages? (get chats chat-id)) + (-> db + (update-in [:chats chat-id] dissoc :join-time-mail-request-id) + (update-in [:chats chat-id] dissoc :might-have-join-time-messages?)) + db)) + (defn- create-new-chat [chat-id {:keys [db now]}] (let [name (get-in db [:contacts/contacts chat-id :name])] @@ -84,14 +100,15 @@ "Adds new public group chat to db & realm" [cofx topic] (upsert-chat cofx - {:chat-id topic - :is-active true - :name topic - :group-chat true - :contacts #{} - :public? true - :unviewed-messages-count 0 - :loaded-unviewed-messages-ids #{}})) + {:chat-id topic + :is-active true + :name topic + :group-chat true + :contacts #{} + :public? true + :might-have-join-time-messages? true + :unviewed-messages-count 0 + :loaded-unviewed-messages-ids #{}})) (fx/defn add-group-chat "Adds new private group chat to db & realm" diff --git a/src/status_im/chat/models/message.cljs b/src/status_im/chat/models/message.cljs index 544733dd2d..b8e25ce785 100644 --- a/src/status_im/chat/models/message.cljs +++ b/src/status_im/chat/models/message.cljs @@ -16,6 +16,7 @@ [status-im.chat.models.message-content :as message-content] [status-im.chat.commands.receiving :as commands-receiving] [status-im.chat.db :as chat.db] + [status-im.mailserver.core :as mailserver] [status-im.utils.clocks :as utils.clocks] [status-im.utils.money :as money] [status-im.utils.types :as types] @@ -287,35 +288,51 @@ (apply fx/merge cofx (map (partial update-last-message (:chats db)) chat-ids))) +(fx/defn declare-syncd-public-chats! + [{:keys [db] :as cofx} chat-ids] + (apply fx/merge cofx + (map (partial chat-model/join-time-messages-checked db) chat-ids))) + +(defn- chat-ids->never-synced-public-chat-ids [chats chat-ids] + (let [never-synced-public-chat-ids (mailserver/chats->never-synced-public-chats chats)] + (when (seq never-synced-public-chat-ids) + (-> never-synced-public-chat-ids + (select-keys (vec chat-ids)) + keys)))) + (fx/defn receive-many [{:keys [now] :as cofx} messages] - (let [valid-messages (keep #(when-let [chat-id (extract-chat-id cofx %)] - (assoc % :chat-id chat-id)) messages) - filtered-messages (filter-messages cofx valid-messages) - deduped-messages (:messages filtered-messages) - old-id->message (:by-old-message-id filtered-messages) - chat->message (group-by :chat-id deduped-messages) - chat-ids (keys chat->message) - chats-fx-fns (map (fn [chat-id] - (let [unviewed-messages-count - (calculate-unviewed-messages-count - cofx - chat-id - (get chat->message chat-id))] - (chat-model/upsert-chat - {:chat-id chat-id - :is-active true - :timestamp now - :unviewed-messages-count unviewed-messages-count}))) - chat-ids) - messages-fx-fns (map #(add-received-message old-id->message %) deduped-messages) - groups-fx-fns (map #(update-group-messages chat->message %) chat-ids)] + (let [valid-messages (keep #(when-let [chat-id (extract-chat-id cofx %)] + (assoc % :chat-id chat-id)) messages) + filtered-messages (filter-messages cofx valid-messages) + deduped-messages (:messages filtered-messages) + old-id->message (:by-old-message-id filtered-messages) + chat->message (group-by :chat-id deduped-messages) + chat-ids (keys chat->message) + never-synced-public-chat-ids (chat-ids->never-synced-public-chat-ids + (get-in cofx [:db :chats]) chat-ids) + chats-fx-fns (map (fn [chat-id] + (let [unviewed-messages-count + (calculate-unviewed-messages-count + cofx + chat-id + (get chat->message chat-id))] + (chat-model/upsert-chat + {:chat-id chat-id + :is-active true + :timestamp now + :unviewed-messages-count unviewed-messages-count}))) + chat-ids) + messages-fx-fns (map #(add-received-message old-id->message %) deduped-messages) + groups-fx-fns (map #(update-group-messages chat->message %) chat-ids)] (apply fx/merge cofx (concat chats-fx-fns messages-fx-fns groups-fx-fns (when platform/desktop? [(chat-model/update-dock-badge-label)]) - [(update-last-messages chat-ids)])))) + [(update-last-messages chat-ids)] + (when (seq never-synced-public-chat-ids) + [(declare-syncd-public-chats! never-synced-public-chat-ids)]))))) (defn system-message [{:keys [now] :as cofx} {:keys [clock-value chat-id content from]}] (let [{:keys [last-clock-value]} (get-in cofx [:db :chats chat-id]) diff --git a/src/status_im/chat/subs.cljs b/src/status_im/chat/subs.cljs index e3dfb0a9dd..b3facb584a 100644 --- a/src/status_im/chat/subs.cljs +++ b/src/status_im/chat/subs.cljs @@ -7,8 +7,10 @@ [status-im.chat.db :as chat.db] [status-im.models.transactions :as transactions] [status-im.utils.platform :as platform] - [status-im.ui.components.bottom-bar.styles :as tabs-styles] - [status-im.ui.components.bottom-bar.styles :as tabs.styles])) + [status-im.utils.universal-links.core :as links] + [status-im.ui.components.bottom-bar.styles :as tabs.styles] + [status-im.ui.components.toolbar.styles :as toolbar.styles] + [status-im.ui.screens.chat.stickers.styles :as stickers.styles])) (re-frame/reg-sub ::chats :chats) (re-frame/reg-sub ::access-scope->command-id :access-scope->command-id) @@ -53,6 +55,32 @@ (fn [chats [_ chat-id]] (get chats chat-id))) +(re-frame/reg-sub + :chats/content-layout-height + :<- [:get :content-layout-height] + :<- [:chats/current-chat-ui-prop :input-height] + :<- [:chats/current-chat-ui-prop :input-focused?] + :<- [:get :keyboard-height] + :<- [:chats/current-chat-ui-prop :show-stickers?] + (fn [[home-content-layout-height input-height input-focused? kheight stickers?]] + (- (+ home-content-layout-height tabs.styles/tabs-height) + (if platform/iphone-x? + (* 2 toolbar.styles/toolbar-height) + toolbar.styles/toolbar-height) + (if input-height input-height 0) + (if stickers? + (stickers.styles/stickers-panel-height) + kheight) + (if input-focused? + (cond + platform/iphone-x? 0 + platform/ios? tabs.styles/tabs-diff + :else 0) + (cond + platform/iphone-x? (* 2 tabs.styles/minimized-tabs-height) + platform/ios? tabs.styles/tabs-height + :else tabs.styles/minimized-tabs-height))))) + (re-frame/reg-sub :chats/current-chat-ui-props :<- [::chat-ui-props] @@ -100,7 +128,13 @@ :<- [:chats/active-chats] :<- [:chats/current-chat-id] (fn [[chats current-chat-id]] - (get chats current-chat-id))) + (let [current-chat (get chats current-chat-id) + messages (:messages current-chat)] + (if (empty? messages) + (assoc current-chat + :universal-link + (links/generate-link :public-chat :external current-chat-id)) + current-chat)))) (re-frame/reg-sub :chats/current-chat-message @@ -143,6 +177,17 @@ (chat.db/messages-with-datemarks-and-statuses messages message-statuses referenced-messages) chat.db/messages-stream))) +(re-frame/reg-sub + :chats/current-chat-intro-status + :<- [:chats/current-chat] + :<- [:chats/current-chat-messages] + (fn [[{:keys [might-have-join-time-messages?]} messages]] + (if might-have-join-time-messages? + :loading + (if (empty? messages) + :empty + :messages)))) + (re-frame/reg-sub :chats/available-commands :<- [::get-commands-for-chat] diff --git a/src/status_im/events.cljs b/src/status_im/events.cljs index 17884ee3ec..4785c408dc 100644 --- a/src/status_im/events.cljs +++ b/src/status_im/events.cljs @@ -729,6 +729,11 @@ (fn [{:keys [db]} [_ kvs]] {:db (chat/set-chat-ui-props db kvs)})) +(handlers/register-handler-fx + :chat.ui/join-time-messages-checked + (fn [{:keys [db]} [_ chat-id]] + {:db (chat/join-time-messages-checked db chat-id)})) + (handlers/register-handler-fx :chat.ui/show-message-details (fn [{:keys [db]} [_ details]] diff --git a/src/status_im/group_chats/db.cljs b/src/status_im/group_chats/db.cljs index d5c695d75e..e62b9a651a 100644 --- a/src/status_im/group_chats/db.cljs +++ b/src/status_im/group_chats/db.cljs @@ -44,3 +44,13 @@ (let [inviter-pk (get-inviter-pk my-public-key chat)] (get-in contacts [inviter-pk :name] (gfycat/generate-gfy inviter-pk))))) + +(defn get-inviter-name + "when the chat is a private group chat in which the user has been + invited and didn't accept the invitation yet, return inviter-name" + [contacts chat my-public-key] + (when (and (models.chat/group-chat? chat) + (joined? my-public-key chat)) + (let [inviter-pk (get-inviter-pk my-public-key chat)] + (get-in contacts [inviter-pk :name] + (gfycat/generate-gfy inviter-pk))))) diff --git a/src/status_im/mailserver/core.cljs b/src/status_im/mailserver/core.cljs index 196d20263a..1d9ecf0350 100644 --- a/src/status_im/mailserver/core.cljs +++ b/src/status_im/mailserver/core.cljs @@ -277,9 +277,26 @@ (log/debug "Adjusting mailserver request" "from:" from "adjusted-from:" adjusted-from) adjusted-from)) -(fx/defn handle-request-success [{:keys [db]} {:keys [request-id]}] +(defn chats->never-synced-public-chats [chats] + (into {} (filter (fn [[k v]] (:might-have-join-time-messages? v)) chats))) + +(fx/defn handle-request-success [{{:keys [chats] :as db} :db} + {:keys [request-id topics]}] (when (:mailserver/current-request db) - {:db (assoc-in db [:mailserver/current-request :request-id] request-id)})) + (let [by-topic-never-synced-chats (reduce-kv + #(assoc %1 (transport.utils/get-topic %2) %3) + {} + (chats->never-synced-public-chats chats)) + never-synced-chats-in-this-request (select-keys by-topic-never-synced-chats (vec topics))] + (if (seq never-synced-chats-in-this-request) + {:db (-> db + ((fn [db] (reduce + (fn [db chat] + (assoc-in db [:chats (:chat-id chat) :join-time-mail-request-id] request-id)) + db + (vals never-synced-chats-in-this-request)))) + (assoc-in [:mailserver/current-request :request-id] request-id))} + {:db (assoc-in db [:mailserver/current-request :request-id] request-id)})))) (defn request-messages! [web3 {:keys [sym-key-id address]} {:keys [topics cursor to from] :as request}] ;; Add some room to from, unless we break day boundaries so that messages that have @@ -304,7 +321,7 @@ (if-not error (do (log/info "mailserver: messages request success for topic " topics "from" from "to" to) - (re-frame/dispatch [:mailserver.callback/request-success {:request-id request-id}])) + (re-frame/dispatch [:mailserver.callback/request-success {:request-id request-id :topics topics}])) (do (log/error "mailserver: messages request error for topic " topics ": " error) (utils/set-timeout #(re-frame/dispatch [:mailserver.callback/resend-request {:request-id nil}]) @@ -521,22 +538,47 @@ (fx/defn handle-request-error [{:keys [db]} error] {:db (-> db - (assoc :mailserver/request-error error) + (assoc :mailserver/request-error error) (dissoc :mailserver/current-request :mailserver/pending-requests))}) (fx/defn handle-request-completed - [cofx event] + [{{:keys [chats] :as db} :db :as cofx} + {:keys [requestID lastEnvelopeHash cursor errorMessage] :as event}] (when (accounts.db/logged-in? cofx) - (let [error (:errorMessage event)] - (if (empty? error) - (fx/merge - cofx - {:mailserver/increase-limit []} - (update-mailserver-topics {:request-id (:requestID event) - :cursor (:cursor event)})) - - (handle-request-error cofx error))))) + (if (empty? errorMessage) + (let [never-synced-chats-in-request + (->> (chats->never-synced-public-chats chats) + (filter (fn [[k v]] (= requestID (:join-time-mail-request-id v)))) + keys)] + (if (seq never-synced-chats-in-request) + (if (= lastEnvelopeHash + "0x0000000000000000000000000000000000000000000000000000000000000000") + (fx/merge + cofx + {:mailserver/increase-limit [] + :dispatch-n (map + #(identity [:chat.ui/join-time-messages-checked %]) + never-synced-chats-in-request)} + (update-mailserver-topics {:request-id requestID + :cursor cursor})) + (fx/merge + cofx + {:mailserver/increase-limit [] + :dispatch-later (vec + (map + #(identity + {:ms 1000 + :dispatch [:chat.ui/join-time-messages-checked %]}) + never-synced-chats-in-request))} + (update-mailserver-topics {:request-id requestID + :cursor cursor}))) + (fx/merge + cofx + {:mailserver/increase-limit []} + (update-mailserver-topics {:request-id requestID + :cursor cursor})))) + (handle-request-error cofx errorMessage)))) (fx/defn show-request-error-popup [{:keys [db]}] diff --git a/src/status_im/ui/components/chat_icon/screen.cljs b/src/status_im/ui/components/chat_icon/screen.cljs index fa53fab93c..c9e2730b86 100644 --- a/src/status_im/ui/components/chat_icon/screen.cljs +++ b/src/status_im/ui/components/chat_icon/screen.cljs @@ -122,6 +122,12 @@ :default-chat-icon (styles/default-chat-icon-profile colors/default-chat-color size) :default-chat-icon-text styles/default-chat-icon-text}]) +(defn chat-intro-icon-view [icon-text chat-id styles] + (let [photo-path (re-frame.core/subscribe [:contacts/chat-photo chat-id])] + (if-not (string/blank? @photo-path) + [photos/photo @photo-path styles] + [default-chat-icon icon-text styles]))) + (defn profile-icon-view [photo-path name color edit? size override-styles] (let [styles (merge {:container {:width size :height size} :online-view styles/online-view-profile diff --git a/src/status_im/ui/screens/chat/styles/main.cljs b/src/status_im/ui/screens/chat/styles/main.cljs index 7e8bfde163..e6f3c48ecb 100644 --- a/src/status_im/ui/screens/chat/styles/main.cljs +++ b/src/status_im/ui/screens/chat/styles/main.cljs @@ -191,9 +191,6 @@ {:opacity opacity :flex 1}) -(def empty-chat-container-one-to-one - {:margin-top 10}) - (def empty-chat-container {:flex 1 :flex-direction :column @@ -202,17 +199,53 @@ :padding-vertical 50 :margin-right 6}) -(def empty-chat-text - {:color colors/gray - :width 280 - :text-align :center}) +(defn intro-header-container + [height status no-messages] + (let [adjusted-height (if (< height 280) 324 height)] + (if (or no-messages (= status (or :loading :empty))) + {:flex 1 + :flex-direction :column + :justify-content :center + :align-items :center + :height adjusted-height + :padding-horizontal 32} + {:flex 1 + :flex-direction :column + :justify-content :center + :align-items :center + :padding-horizontal 32}))) -(def empty-chat-text-name - {:margin-bottom 5}) +(defn intro-header-icon [diameter color] + {:width diameter + :height diameter + :align-items :center + :justify-content :center + :border-radius (/ diameter 2) + :background-color color}) -(def join-button - {:margin-top 24 - :margin-bottom 15}) +(def intro-header-icon-text + {:color colors/white + :font-size 52 + :font-weight "700" + :opacity 0.8 + :line-height 72}) + +(def intro-header-chat-name + {:font-size 22 + :font-weight "700" + :line-height 28 + :margin-bottom 8 + :color colors/black}) + +(def intro-header-description-container + {:flex-wrap :wrap + :align-items :flex-start + :flex-direction :row}) + +(def intro-header-description + {:color colors/gray + :line-height 22 + :text-align :center}) (def group-chat-icon {:color colors/white @@ -220,18 +253,20 @@ :font-weight "700"}) (def group-chat-join-footer - {:position :absolute - :justify-content :center - :margin-bottom 30 - :bottom 0}) - -(def group-chat-join-name - {:typography :header}) + {:flex 1 + :justify-content :center}) (def group-chat-join-container {:flex 1 :align-items :center :justify-content :center}) +(def group-chat-join-name + {:typography :header}) + +(def join-button + {:margin-bottom 15}) + (def decline-chat - {:color colors/blue}) + {:color colors/blue + :margin-bottom 40}) diff --git a/src/status_im/ui/screens/chat/utils.cljs b/src/status_im/ui/screens/chat/utils.cljs index fc6f74e7c0..52013a6467 100644 --- a/src/status_im/ui/screens/chat/utils.cljs +++ b/src/status_im/ui/screens/chat/utils.cljs @@ -47,7 +47,7 @@ colors/blue (if outgoing colors/white colors/blue)) :text-decoration-line :underline} - :on-press #(re-frame/dispatch [:chat.ui/start-public-chat (subs text 1)])})}) + :on-press #(re-frame/dispatch [:chat.ui/start-public-chat (subs text 1) {:navigation-reset? true}])})}) (defn- lookup-props [text-chunk message kind] (let [prop (get styling->prop kind) diff --git a/src/status_im/ui/screens/chat/views.cljs b/src/status_im/ui/screens/chat/views.cljs index 440ed551ad..eccbf676e2 100644 --- a/src/status_im/ui/screens/chat/views.cljs +++ b/src/status_im/ui/screens/chat/views.cljs @@ -44,26 +44,25 @@ (list-selection/show {:title chat-name :options (actions/actions group-chat? chat-id public?)})) -(defview chat-toolbar [public? modal?] - (letsubs [{:keys [chat-name group-chat chat-id contact]} [:chats/current-chat]] - [react/view - [status-bar/status-bar (when modal? {:type :modal-white})] - [toolbar/toolbar - {:chat? true} - (if modal? - [toolbar/nav-button - (toolbar.actions/close toolbar.actions/default-handler)] - toolbar/nav-back-home) - [toolbar-content/toolbar-content-view] - (when-not modal? - [toolbar/actions [{:icon :main-icons/more - :icon-opts {:color :black - :accessibility-label :chat-menu-button} - :handler #(on-options chat-id chat-name group-chat public?)}]])] - [connectivity/connectivity-view] - (when (and (not group-chat) - (not (contact.db/added? contact))) - [add-contact-bar chat-id])])) +(defn chat-toolbar [{:keys [chat-name group-chat chat-id contact]} public? modal?] + [react/view + [status-bar/status-bar (when modal? {:type :modal-white})] + [toolbar/toolbar + {:chat? true} + (if modal? + [toolbar/nav-button + (toolbar.actions/close toolbar.actions/default-handler)] + toolbar/nav-back-home) + [toolbar-content/toolbar-content-view] + (when-not modal? + [toolbar/actions [{:icon :main-icons/more + :icon-opts {:color :black + :accessibility-label :chat-menu-button} + :handler #(on-options chat-id chat-name group-chat public?)}]])] + [connectivity/connectivity-view] + (when (and (not group-chat) + (not (contact.db/added? contact))) + [add-contact-bar chat-id])]) (defmulti message-row (fn [{{:keys [type]} :row}] type)) @@ -101,22 +100,6 @@ [react/animated-view {:style (style/message-view-animated opacity)} message-view]]])) -(defn empty-chat-container - [] - [react/view style/empty-chat-container - [react/text {:style style/empty-chat-text} - (i18n/label :t/empty-chat-description)]]) - -(defn empty-chat-container-one-to-one - [contact-name] - [react/view style/empty-chat-container - [vector-icons/icon :tiny-icons/tiny-lock] - [react/nested-text {:style style/empty-chat-text} - [{:style style/empty-chat-container-one-to-one} - (i18n/label :t/empty-chat-description-one-to-one)] - [{:style style/empty-chat-text-name} - contact-name]]]) - (defn join-chat-button [chat-id] [buttons/secondary-button {:style style/join-button :on-press #(re-frame/dispatch [:group-chats.ui/join-pressed chat-id])} @@ -129,6 +112,14 @@ [react/text {:style style/decline-chat} (i18n/label :t/group-chat-decline-invitation)]]) +(defn group-chat-footer + [chat-id] + [react/view {:style style/group-chat-join-footer} + [react/view {:style style/group-chat-join-container} + [join-chat-button chat-id] + [decline-chat chat-id]]]) + +;; TODO this is now used only in Desktop - unnecessary for mobile (defn group-chat-join-section [inviter-name {:keys [name group-chat color chat-id]}] [react/view style/empty-chat-container @@ -138,15 +129,95 @@ [react/view {:style style/group-chat-join-container} [react/view [react/text {:style style/group-chat-join-name} name]] - [react/text {:style style/empty-chat-text} - [react/text style/empty-chat-container-one-to-one - (i18n/label :t/join-group-chat-description {:username inviter-name - :group-name name})]] + [react/text {:style style/intro-header-description} + (i18n/label :t/join-group-chat-description {:username inviter-name + :group-name name})] [join-chat-button chat-id] [decline-chat chat-id]]]]) +;; TODO refactor this big view into chunks +(defview chat-intro-header-container + [{:keys [group-chat name pending-invite-inviter-name + inviter-name color chat-id chat-name public? + universal-link]} no-messages] + (letsubs [intro-status [:chats/current-chat-intro-status] + height [:chats/content-layout-height] + input-height [:chats/current-chat-ui-prop :input-height]] + (let [icon-text (if public? chat-id name) + intro-name (if public? chat-name name)] + ;; TODO This when check ought to be unnecessary but for now it prevents + ;; jerky motion when fresh chat is created, when input-height can be null + ;; affecting the calculation of content-layout-height to be briefly adjusted + (when (or input-height pending-invite-inviter-name) + [react/touchable-without-feedback + {:style {:flex 1 + :align-items :flex-start} + :on-press (fn [_] + (re-frame/dispatch + [:chat.ui/set-chat-ui-props {:messages-focused? true + :show-stickers? false}]) + (react/dismiss-keyboard!))} + [react/view + (style/intro-header-container height intro-status no-messages) + ;; Icon section + [react/view {:style {:margin-top 42 + :margin-bottom 24}} + [chat-icon.screen/chat-intro-icon-view + icon-text chat-id + {:default-chat-icon (style/intro-header-icon 120 color) + :default-chat-icon-text style/intro-header-icon-text + :size 120}]] + ;; Chat title section + [react/text {:style style/intro-header-chat-name} intro-name] + ;; Description section + (if group-chat + (cond + (= intro-status :loading) + [react/view {:style (merge style/intro-header-description-container + {:margin-bottom 36 + :height 44})} + [react/text {:style style/intro-header-description} + (i18n/label :t/loading)] + [react/activity-indicator {:animating true + :size :small + :color colors/gray}]] + + (= intro-status :empty) + (when public? + [react/nested-text {:style (merge style/intro-header-description + {:margin-bottom 36})} + (i18n/label :t/empty-chat-description-public) + [{:style {:color colors/blue} + :on-press #(list-selection/open-share + {:message + (i18n/label + :t/share-public-chat-text {:link universal-link})})} + (i18n/label :t/empty-chat-description-public-share-this)]]) + + (= intro-status :messages) + (when (not public?) + (if pending-invite-inviter-name + [react/nested-text {:style style/intro-header-description} + [{:style {:color :black}} pending-invite-inviter-name] + (i18n/label :t/join-group-chat-description + {:username "" + :group-name intro-name})] + (if (not= inviter-name "Unknown") + [react/nested-text {:style style/intro-header-description} + (i18n/label :t/joined-group-chat-description + {:username "" + :group-name intro-name}) + [{:style {:color :black}} inviter-name]] + [react/text {:style style/intro-header-description} + (i18n/label :t/created-group-chat-description + {:group-name intro-name})])))) + [react/nested-text {:style (merge style/intro-header-description + {:margin-bottom 36})} + (i18n/label :t/empty-chat-description-one-to-one) + [{} intro-name]])]])))) + (defview messages-view - [{:keys [group-chat name pending-invite-inviter-name messages-initialized?] :as chat} + [{:keys [group-chat chat-id pending-invite-inviter-name] :as chat} modal?] (letsubs [messages [:chats/current-chat-messages-stream] current-public-key [:account/public-key]] @@ -157,63 +228,63 @@ (re-frame/dispatch [:chat.ui/set-chat-ui-props {:messages-focused? true :input-focused? false}]))} - (cond - pending-invite-inviter-name - [group-chat-join-section pending-invite-inviter-name chat] - - (and (empty? messages) - messages-initialized?) - (if group-chat - [empty-chat-container] - [empty-chat-container-one-to-one name]) - - :else - [list/flat-list {:data messages - :key-fn #(or (:message-id %) (:value %)) - :render-fn (fn [message] - [message-row {:group-chat group-chat - :modal? modal? - :current-public-key current-public-key - :row message}]) - :inverted true - :onEndReached #(re-frame/dispatch [:chat.ui/load-more-messages]) - :enableEmptySections true - :keyboardShouldPersistTaps :handled}]))) - -(defview messages-view-wrapper [modal?] - (letsubs [chat [:chats/current-chat]] - [messages-view chat modal?])) + (let [no-messages (empty? messages) + flat-list-conf + {:data messages + :footer [chat-intro-header-container chat no-messages] + :key-fn #(or (:message-id %) (:value %)) + :render-fn (fn [message] + [message-row + {:group-chat group-chat + :modal? modal? + :current-public-key current-public-key + :row message}]) + :inverted true + :onEndReached #(re-frame/dispatch + [:chat.ui/load-more-messages]) + :enableEmptySections true + :keyboardShouldPersistTaps :handled} + group-header {:header [group-chat-footer chat-id]}] + (if pending-invite-inviter-name + [list/flat-list (merge flat-list-conf group-header)] + [list/flat-list flat-list-conf])))) (defn show-input-container? [my-public-key current-chat] (or (not (models.chat/group-chat? current-chat)) (group-chats.db/joined? my-public-key current-chat))) (defview chat-root [modal?] - (letsubs [{:keys [public?] :as current-chat} [:chats/current-chat] - my-public-key [:account/public-key] - show-bottom-info? [:chats/current-chat-ui-prop :show-bottom-info?] - show-message-options? [:chats/current-chat-ui-prop :show-message-options?] - show-stickers? [:chats/current-chat-ui-prop :show-stickers?]] - ;; this scroll-view is a hack that allows us to use on-blur and on-focus on Android - ;; more details here: https://github.com/facebook/react-native/issues/11071 - [react/scroll-view {:scroll-enabled false - :style style/scroll-root - :content-container-style style/scroll-root - :keyboard-should-persist-taps :handled} - [react/view {:style style/chat-view - :on-layout (fn [e] - (re-frame/dispatch [:set :layout-height (-> e .-nativeEvent .-layout .-height)]))} - [chat-toolbar public? modal?] - [messages-view-animation - [messages-view-wrapper modal?]] - (when (show-input-container? my-public-key current-chat) - [input/container]) - (when show-stickers? - [stickers/stickers-view]) - (when show-bottom-info? - [bottom-info/bottom-info-view]) - (when show-message-options? - [message-options/view])]])) + (letsubs [{:keys [public? chat-id] :as current-chat} [:chats/current-chat] + current-chat-id [:chats/current-chat-id] + my-public-key [:account/public-key] + show-bottom-info? [:chats/current-chat-ui-prop :show-bottom-info?] + show-message-options? [:chats/current-chat-ui-prop :show-message-options?] + show-stickers? [:chats/current-chat-ui-prop :show-stickers?]] + ;; this check of current-chat-id is necessary only because in a fresh public chat creation sometimes + ;; this component renders before current-chat-id is set to current chat-id. Hence further down in sub + ;; components (e.g. chat-toolbar) there can be a brief visual inconsistancy like showing 'add contact' + ;; in public chat + (when (= chat-id current-chat-id) + ;; this scroll-view is a hack that allows us to use on-blur and on-focus on Android + ;; more details here: https://github.com/facebook/react-native/issues/11071 + [react/scroll-view {:scroll-enabled false + :style style/scroll-root + :content-container-style style/scroll-root + :keyboard-should-persist-taps :handled} + [react/view {:style style/chat-view + :on-layout (fn [e] + (re-frame/dispatch [:set :layout-height (-> e .-nativeEvent .-layout .-height)]))} + [chat-toolbar current-chat public? modal?] + [messages-view-animation + [messages-view current-chat modal?]] + (when (show-input-container? my-public-key current-chat) + [input/container]) + (when show-stickers? + [stickers/stickers-view]) + (when show-bottom-info? + [bottom-info/bottom-info-view]) + (when show-message-options? + [message-options/view])]]))) (defview chat [] [chat-root false]) diff --git a/src/status_im/ui/screens/events.cljs b/src/status_im/ui/screens/events.cljs index b9b716c5ec..a0a1810e48 100644 --- a/src/status_im/ui/screens/events.cljs +++ b/src/status_im/ui/screens/events.cljs @@ -120,6 +120,12 @@ (fn [{:keys [db]} [_ k v]] {:db (assoc db k v)})) +(handlers/register-handler-fx + :set-once + (fn [{:keys [db]} [_ k v]] + (when-not (get db k) + {:db (assoc db k v)}))) + (handlers/register-handler-fx :set-in (fn [{:keys [db]} [_ path v]] diff --git a/src/status_im/ui/screens/home/views.cljs b/src/status_im/ui/screens/home/views.cljs index a21501f80c..925e37ee15 100644 --- a/src/status_im/ui/screens/home/views.cljs +++ b/src/status_im/ui/screens/home/views.cljs @@ -118,7 +118,11 @@ (when loading? (utils/set-timeout #(re-frame/dispatch [:init-rest-of-chats]) 100))))} [react/view {:flex 1} [status-bar/status-bar {:type :main}] - [react/keyboard-avoiding-view {:style {:flex 1}} + [react/keyboard-avoiding-view {:style {:flex 1} + :on-layout (fn [e] + (re-frame/dispatch + [:set-once :content-layout-height + (-> e .-nativeEvent .-layout .-height)]))} [toolbar/toolbar nil nil [toolbar/content-title (i18n/label :t/chat)]] [les-debug-info] (cond loading? diff --git a/test/cljs/status_im/test/mailserver/core.cljs b/test/cljs/status_im/test/mailserver/core.cljs index f0ed57d34b..e8f681c1a3 100644 --- a/test/cljs/status_im/test/mailserver/core.cljs +++ b/test/cljs/status_im/test/mailserver/core.cljs @@ -375,6 +375,187 @@ (is (not (mailserver/resend-request {:db {:mailserver/current-request {:request-id "a"}}} {:request-id "b"})))))) +(def cofx-no-pub-topic + {:db + {:account/account {:public-key "me"} + :chats + {"chat-id-1" {:is-active true + :messages {} + :might-have-join-time-messages? true + :group-chat true + :public? true + :chat-id "chat-id-1"} + "chat-id-2" {:is-active true + :messages {} + :group-chat true + :public? true + :chat-id "chat-id-2"}}}}) + +(def cofx-single-pub-topic + {:db + {:account/account {:public-key "me"} + :chats + {"chat-id-1" {:is-active true + :messages {} + :join-time-mail-request-id "a" + :might-have-join-time-messages? true + :group-chat true + :public? true + :chat-id "chat-id-1"} + "chat-id-2" {:is-active true + :messages {} + :group-chat true + :public? true + :chat-id "chat-id-2"}}}}) + +(def cofx-multiple-pub-topic + {:db + {:account/account {:public-key "me"} + :chats + {"chat-id-1" {:is-active true + :messages {} + :join-time-mail-request-id "a" + :might-have-join-time-messages? true + :group-chat true + :public? true + :chat-id "chat-id-1"} + "chat-id-2" {:is-active true + :messages {} + :join-time-mail-request-id "a" + :might-have-join-time-messages? true + :group-chat true + :public? true + :chat-id "chat-id-2"} + "chat-id-3" {:is-active true + :messages {} + :group-chat true + :public? true + :chat-id "chat-id-3"} + "chat-id-4" {:is-active true + :messages {} + :join-time-mail-request-id "a" + :might-have-join-time-messages? true + :group-chat true + :public? true + :chat-id "chat-id-4"}}}}) + +(def mailserver-completed-event + {:requestID "a" + :lastEnvelopeHash "0xC0FFEE" + :cursor "" + :errorMessage ""}) + +(def mailserver-completed-event-zero-for-envelope + {:requestID "a" + :lastEnvelopeHash "0x0000000000000000000000000000000000000000000000000000000000000000" + :cursor "" + :errorMessage ""}) + +(deftest test-public-chat-related-handling-of-request-completed + (testing "Request does not include any public chat topic" + (testing "It does not dispatch any event" + (is (not (or (contains? + (mailserver/handle-request-completed cofx-no-pub-topic mailserver-completed-event) + :dispatch-n) + (contains? + (mailserver/handle-request-completed cofx-no-pub-topic mailserver-completed-event) + :dispatch-later))))) + (testing "It has :mailserver/increase-limit effect" + (is (contains? (mailserver/handle-request-completed cofx-no-pub-topic mailserver-completed-event) + :mailserver/increase-limit)))) + (testing "Request includes one public chat topic" + (testing "Event has non-zero envelope" + (let [handeled-effects (mailserver/handle-request-completed + cofx-single-pub-topic + mailserver-completed-event)] + (testing "It has no :dispatch-n event" + (is (not (contains? + handeled-effects + :dispatch-n)))) + (testing "It has one :dispatch-later event" + (is (= 1 (count (get + handeled-effects + :dispatch-later))))) + (testing "The :dispatch-later event is :chat.ui/join-time-messages-checked" + (is (= :chat.ui/join-time-messages-checked + (-> (get + handeled-effects + :dispatch-later) + first + :dispatch + first)))) + (testing "The :dispatch-later event argument is the chat-id/topic that the request included" + (is (= "chat-id-1" + (-> (get + handeled-effects + :dispatch-later) + first + :dispatch + second)))) + (testing "It has :mailserver/increase-limit effect" + (is (contains? handeled-effects + :mailserver/increase-limit))))) + (testing "Event has zero-valued envelope" + (let [handeled-effects (mailserver/handle-request-completed + cofx-single-pub-topic + mailserver-completed-event-zero-for-envelope)] + (testing "It has one :dispatch-n event" + (is (= 1 (count (get + handeled-effects + :dispatch-n))))) + (testing "It has no :dispatch-later event" + (is (not (contains? + handeled-effects + :dispatch-later)))) + (testing "The :dispatch-n event is :chat.ui/join-time-messages-checked" + (is (= :chat.ui/join-time-messages-checked + (-> (get + handeled-effects + :dispatch-n) + first + first)))) + (testing "The :dispatch-n event argument is the chat-id/topic that the request included" + (is (= "chat-id-1" + (-> (get + handeled-effects + :dispatch-n) + first + second)))) + (testing "It has :mailserver/increase-limit effect" + (is (contains? handeled-effects + :mailserver/increase-limit)))))) + (testing "Request includes multiple public chat topics (3)" + (testing "Event has non-zero envelope" + (let [handeled-effects (mailserver/handle-request-completed + cofx-multiple-pub-topic + mailserver-completed-event)] + (testing "It has no :dispatch-n event" + (is (not (contains? + handeled-effects + :dispatch-n)))) + (testing "It has one :dispatch-later event" + (is (= 3 (count (get + handeled-effects + :dispatch-later))))) + (testing "It has :mailserver/increase-limit effect" + (is (contains? handeled-effects + :mailserver/increase-limit))))) + (testing "Event has zero-valued envelope" + (let [handeled-effects (mailserver/handle-request-completed + cofx-multiple-pub-topic + mailserver-completed-event-zero-for-envelope)] + (testing "It has one :dispatch-n event" + (is (= 3 (count (get + handeled-effects + :dispatch-n))))) + (testing "It has no :dispatch-later event" + (is (not (contains? + handeled-effects + :dispatch-later)))) + (testing "It has :mailserver/increase-limit effect" + (is (contains? handeled-effects + :mailserver/increase-limit))))))) + (deftest peers-summary-change (testing "Mailserver added, sym-key doesn't exist" (let [result (peers-summary-change-result false true false)] diff --git a/translations/en.json b/translations/en.json index d9737ccfbc..8e32886e83 100644 --- a/translations/en.json +++ b/translations/en.json @@ -233,7 +233,9 @@ "other": "You can select {{count}} more participants" }, "no-more-participants-available": "You can't add anymore participants", + "created-group-chat-description": "You created the group {{group-name}}", "join-group-chat-description": "{{username}} invited you to join the group {{group-name}}", + "joined-group-chat-description": "You've joined {{group-name}} from invitation by {{username}}", "join-group-chat": "Join group", "create-group-chat": "Create group chat", "group-chat-decline-invitation": "Decline invitation", @@ -279,7 +281,10 @@ "currency-display-name-mzn": "Mozambique Metical", "block": "Block", "wallet-set-up-title": "Set up your wallet", + "loading": "Loading... ", "empty-chat-description": "There are no messages \nin this chat yet", + "empty-chat-description-public": "It's been quiet here for the last 24h. Start the conversation or ", + "empty-chat-description-public-share-this": "share this chat.", "camera-access-error": "To grant the required camera permission, please go to your system settings and make sure that Status > Camera is selected.", "wallet-invalid-address": "Invalid address: \n {{data}}", "wallet-invalid-address-checksum": "Error in address: \n {{data}}",