[#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 <i@mandrigin.ru>
This commit is contained in:
bitsikka 2019-02-27 18:43:56 +05:45 committed by Igor Mandrigin
parent c9a8fb2684
commit 6aae0b76b9
No known key found for this signature in database
GPG Key ID: 4A0EDDE26E66BC8B
15 changed files with 612 additions and 162 deletions

View File

@ -17,10 +17,16 @@
(let [pending-invite-inviter-name (let [pending-invite-inviter-name
(group-chats.db/get-pending-invite-inviter-name contacts (group-chats.db/get-pending-invite-inviter-name contacts
chat chat
current-public-key)] current-public-key)
inviter-name
(group-chats.db/get-inviter-name contacts
chat
current-public-key)]
(cond-> chat (cond-> chat
pending-invite-inviter-name pending-invite-inviter-name
(assoc :pending-invite-inviter-name pending-invite-inviter-name) (assoc :pending-invite-inviter-name pending-invite-inviter-name)
inviter-name
(assoc :inviter-name inviter-name)
:always :always
(assoc :chat-name (group-chat-name chat)))) (assoc :chat-name (group-chat-name chat))))
(let [{contact-name :name :as contact} (let [{contact-name :name :as contact}

View File

@ -56,6 +56,22 @@
[{:keys [current-chat-id] :as db} ui-element] [{:keys [current-chat-id] :as db} ui-element]
(update-in db [:chat-ui-props current-chat-id ui-element] not)) (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 (defn- create-new-chat
[chat-id {:keys [db now]}] [chat-id {:keys [db now]}]
(let [name (get-in db [:contacts/contacts chat-id :name])] (let [name (get-in db [:contacts/contacts chat-id :name])]
@ -84,14 +100,15 @@
"Adds new public group chat to db & realm" "Adds new public group chat to db & realm"
[cofx topic] [cofx topic]
(upsert-chat cofx (upsert-chat cofx
{:chat-id topic {:chat-id topic
:is-active true :is-active true
:name topic :name topic
:group-chat true :group-chat true
:contacts #{} :contacts #{}
:public? true :public? true
:unviewed-messages-count 0 :might-have-join-time-messages? true
:loaded-unviewed-messages-ids #{}})) :unviewed-messages-count 0
:loaded-unviewed-messages-ids #{}}))
(fx/defn add-group-chat (fx/defn add-group-chat
"Adds new private group chat to db & realm" "Adds new private group chat to db & realm"

View File

@ -16,6 +16,7 @@
[status-im.chat.models.message-content :as message-content] [status-im.chat.models.message-content :as message-content]
[status-im.chat.commands.receiving :as commands-receiving] [status-im.chat.commands.receiving :as commands-receiving]
[status-im.chat.db :as chat.db] [status-im.chat.db :as chat.db]
[status-im.mailserver.core :as mailserver]
[status-im.utils.clocks :as utils.clocks] [status-im.utils.clocks :as utils.clocks]
[status-im.utils.money :as money] [status-im.utils.money :as money]
[status-im.utils.types :as types] [status-im.utils.types :as types]
@ -287,35 +288,51 @@
(apply fx/merge cofx (apply fx/merge cofx
(map (partial update-last-message (:chats db)) chat-ids))) (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 (fx/defn receive-many
[{:keys [now] :as cofx} messages] [{:keys [now] :as cofx} messages]
(let [valid-messages (keep #(when-let [chat-id (extract-chat-id cofx %)] (let [valid-messages (keep #(when-let [chat-id (extract-chat-id cofx %)]
(assoc % :chat-id chat-id)) messages) (assoc % :chat-id chat-id)) messages)
filtered-messages (filter-messages cofx valid-messages) filtered-messages (filter-messages cofx valid-messages)
deduped-messages (:messages filtered-messages) deduped-messages (:messages filtered-messages)
old-id->message (:by-old-message-id filtered-messages) old-id->message (:by-old-message-id filtered-messages)
chat->message (group-by :chat-id deduped-messages) chat->message (group-by :chat-id deduped-messages)
chat-ids (keys chat->message) chat-ids (keys chat->message)
chats-fx-fns (map (fn [chat-id] never-synced-public-chat-ids (chat-ids->never-synced-public-chat-ids
(let [unviewed-messages-count (get-in cofx [:db :chats]) chat-ids)
(calculate-unviewed-messages-count chats-fx-fns (map (fn [chat-id]
cofx (let [unviewed-messages-count
chat-id (calculate-unviewed-messages-count
(get chat->message chat-id))] cofx
(chat-model/upsert-chat chat-id
{:chat-id chat-id (get chat->message chat-id))]
:is-active true (chat-model/upsert-chat
:timestamp now {:chat-id chat-id
:unviewed-messages-count unviewed-messages-count}))) :is-active true
chat-ids) :timestamp now
messages-fx-fns (map #(add-received-message old-id->message %) deduped-messages) :unviewed-messages-count unviewed-messages-count})))
groups-fx-fns (map #(update-group-messages chat->message %) chat-ids)] 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 (apply fx/merge cofx (concat chats-fx-fns
messages-fx-fns messages-fx-fns
groups-fx-fns groups-fx-fns
(when platform/desktop? (when platform/desktop?
[(chat-model/update-dock-badge-label)]) [(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]}] (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]) (let [{:keys [last-clock-value]} (get-in cofx [:db :chats chat-id])

View File

@ -7,8 +7,10 @@
[status-im.chat.db :as chat.db] [status-im.chat.db :as chat.db]
[status-im.models.transactions :as transactions] [status-im.models.transactions :as transactions]
[status-im.utils.platform :as platform] [status-im.utils.platform :as platform]
[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.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 ::chats :chats)
(re-frame/reg-sub ::access-scope->command-id :access-scope->command-id) (re-frame/reg-sub ::access-scope->command-id :access-scope->command-id)
@ -53,6 +55,32 @@
(fn [chats [_ chat-id]] (fn [chats [_ chat-id]]
(get 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 (re-frame/reg-sub
:chats/current-chat-ui-props :chats/current-chat-ui-props
:<- [::chat-ui-props] :<- [::chat-ui-props]
@ -100,7 +128,13 @@
:<- [:chats/active-chats] :<- [:chats/active-chats]
:<- [:chats/current-chat-id] :<- [:chats/current-chat-id]
(fn [[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 (re-frame/reg-sub
:chats/current-chat-message :chats/current-chat-message
@ -143,6 +177,17 @@
(chat.db/messages-with-datemarks-and-statuses messages message-statuses referenced-messages) (chat.db/messages-with-datemarks-and-statuses messages message-statuses referenced-messages)
chat.db/messages-stream))) 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 (re-frame/reg-sub
:chats/available-commands :chats/available-commands
:<- [::get-commands-for-chat] :<- [::get-commands-for-chat]

View File

@ -729,6 +729,11 @@
(fn [{:keys [db]} [_ kvs]] (fn [{:keys [db]} [_ kvs]]
{:db (chat/set-chat-ui-props 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 (handlers/register-handler-fx
:chat.ui/show-message-details :chat.ui/show-message-details
(fn [{:keys [db]} [_ details]] (fn [{:keys [db]} [_ details]]

View File

@ -44,3 +44,13 @@
(let [inviter-pk (get-inviter-pk my-public-key chat)] (let [inviter-pk (get-inviter-pk my-public-key chat)]
(get-in contacts [inviter-pk :name] (get-in contacts [inviter-pk :name]
(gfycat/generate-gfy inviter-pk))))) (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)))))

View File

@ -277,9 +277,26 @@
(log/debug "Adjusting mailserver request" "from:" from "adjusted-from:" adjusted-from) (log/debug "Adjusting mailserver request" "from:" from "adjusted-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) (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}] (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 ;; Add some room to from, unless we break day boundaries so that messages that have
@ -304,7 +321,7 @@
(if-not error (if-not error
(do (do
(log/info "mailserver: messages request success for topic " topics "from" from "to" to) (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 (do
(log/error "mailserver: messages request error for topic " topics ": " error) (log/error "mailserver: messages request error for topic " topics ": " error)
(utils/set-timeout #(re-frame/dispatch [:mailserver.callback/resend-request {:request-id nil}]) (utils/set-timeout #(re-frame/dispatch [:mailserver.callback/resend-request {:request-id nil}])
@ -521,22 +538,47 @@
(fx/defn handle-request-error (fx/defn handle-request-error
[{:keys [db]} error] [{:keys [db]} error]
{:db (-> db {:db (-> db
(assoc :mailserver/request-error error) (assoc :mailserver/request-error error)
(dissoc :mailserver/current-request (dissoc :mailserver/current-request
:mailserver/pending-requests))}) :mailserver/pending-requests))})
(fx/defn handle-request-completed (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) (when (accounts.db/logged-in? cofx)
(let [error (:errorMessage event)] (if (empty? errorMessage)
(if (empty? error) (let [never-synced-chats-in-request
(fx/merge (->> (chats->never-synced-public-chats chats)
cofx (filter (fn [[k v]] (= requestID (:join-time-mail-request-id v))))
{:mailserver/increase-limit []} keys)]
(update-mailserver-topics {:request-id (:requestID event) (if (seq never-synced-chats-in-request)
:cursor (:cursor event)})) (if (= lastEnvelopeHash
"0x0000000000000000000000000000000000000000000000000000000000000000")
(handle-request-error cofx error))))) (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 (fx/defn show-request-error-popup
[{:keys [db]}] [{:keys [db]}]

View File

@ -122,6 +122,12 @@
:default-chat-icon (styles/default-chat-icon-profile colors/default-chat-color size) :default-chat-icon (styles/default-chat-icon-profile colors/default-chat-color size)
:default-chat-icon-text styles/default-chat-icon-text}]) :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] (defn profile-icon-view [photo-path name color edit? size override-styles]
(let [styles (merge {:container {:width size :height size} (let [styles (merge {:container {:width size :height size}
:online-view styles/online-view-profile :online-view styles/online-view-profile

View File

@ -191,9 +191,6 @@
{:opacity opacity {:opacity opacity
:flex 1}) :flex 1})
(def empty-chat-container-one-to-one
{:margin-top 10})
(def empty-chat-container (def empty-chat-container
{:flex 1 {:flex 1
:flex-direction :column :flex-direction :column
@ -202,17 +199,53 @@
:padding-vertical 50 :padding-vertical 50
:margin-right 6}) :margin-right 6})
(def empty-chat-text (defn intro-header-container
{:color colors/gray [height status no-messages]
:width 280 (let [adjusted-height (if (< height 280) 324 height)]
:text-align :center}) (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 (defn intro-header-icon [diameter color]
{:margin-bottom 5}) {:width diameter
:height diameter
:align-items :center
:justify-content :center
:border-radius (/ diameter 2)
:background-color color})
(def join-button (def intro-header-icon-text
{:margin-top 24 {:color colors/white
:margin-bottom 15}) :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 (def group-chat-icon
{:color colors/white {:color colors/white
@ -220,18 +253,20 @@
:font-weight "700"}) :font-weight "700"})
(def group-chat-join-footer (def group-chat-join-footer
{:position :absolute {:flex 1
:justify-content :center :justify-content :center})
:margin-bottom 30
:bottom 0})
(def group-chat-join-name
{:typography :header})
(def group-chat-join-container (def group-chat-join-container
{:flex 1 {:flex 1
:align-items :center :align-items :center
:justify-content :center}) :justify-content :center})
(def group-chat-join-name
{:typography :header})
(def join-button
{:margin-bottom 15})
(def decline-chat (def decline-chat
{:color colors/blue}) {:color colors/blue
:margin-bottom 40})

View File

@ -47,7 +47,7 @@
colors/blue colors/blue
(if outgoing colors/white colors/blue)) (if outgoing colors/white colors/blue))
:text-decoration-line :underline} :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] (defn- lookup-props [text-chunk message kind]
(let [prop (get styling->prop kind) (let [prop (get styling->prop kind)

View File

@ -44,26 +44,25 @@
(list-selection/show {:title chat-name (list-selection/show {:title chat-name
:options (actions/actions group-chat? chat-id public?)})) :options (actions/actions group-chat? chat-id public?)}))
(defview chat-toolbar [public? modal?] (defn chat-toolbar [{:keys [chat-name group-chat chat-id contact]} public? modal?]
(letsubs [{:keys [chat-name group-chat chat-id contact]} [:chats/current-chat]] [react/view
[react/view [status-bar/status-bar (when modal? {:type :modal-white})]
[status-bar/status-bar (when modal? {:type :modal-white})] [toolbar/toolbar
[toolbar/toolbar {:chat? true}
{:chat? true} (if modal?
(if modal? [toolbar/nav-button
[toolbar/nav-button (toolbar.actions/close toolbar.actions/default-handler)]
(toolbar.actions/close toolbar.actions/default-handler)] toolbar/nav-back-home)
toolbar/nav-back-home) [toolbar-content/toolbar-content-view]
[toolbar-content/toolbar-content-view] (when-not modal?
(when-not modal? [toolbar/actions [{:icon :main-icons/more
[toolbar/actions [{:icon :main-icons/more :icon-opts {:color :black
:icon-opts {:color :black :accessibility-label :chat-menu-button}
:accessibility-label :chat-menu-button} :handler #(on-options chat-id chat-name group-chat public?)}]])]
:handler #(on-options chat-id chat-name group-chat public?)}]])] [connectivity/connectivity-view]
[connectivity/connectivity-view] (when (and (not group-chat)
(when (and (not group-chat) (not (contact.db/added? contact)))
(not (contact.db/added? contact))) [add-contact-bar chat-id])])
[add-contact-bar chat-id])]))
(defmulti message-row (fn [{{:keys [type]} :row}] type)) (defmulti message-row (fn [{{:keys [type]} :row}] type))
@ -101,22 +100,6 @@
[react/animated-view {:style (style/message-view-animated opacity)} [react/animated-view {:style (style/message-view-animated opacity)}
message-view]]])) 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] (defn join-chat-button [chat-id]
[buttons/secondary-button {:style style/join-button [buttons/secondary-button {:style style/join-button
:on-press #(re-frame/dispatch [:group-chats.ui/join-pressed chat-id])} :on-press #(re-frame/dispatch [:group-chats.ui/join-pressed chat-id])}
@ -129,6 +112,14 @@
[react/text {:style style/decline-chat} [react/text {:style style/decline-chat}
(i18n/label :t/group-chat-decline-invitation)]]) (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 (defn group-chat-join-section
[inviter-name {:keys [name group-chat color chat-id]}] [inviter-name {:keys [name group-chat color chat-id]}]
[react/view style/empty-chat-container [react/view style/empty-chat-container
@ -138,15 +129,95 @@
[react/view {:style style/group-chat-join-container} [react/view {:style style/group-chat-join-container}
[react/view [react/view
[react/text {:style style/group-chat-join-name} name]] [react/text {:style style/group-chat-join-name} name]]
[react/text {:style style/empty-chat-text} [react/text {:style style/intro-header-description}
[react/text style/empty-chat-container-one-to-one (i18n/label :t/join-group-chat-description {:username inviter-name
(i18n/label :t/join-group-chat-description {:username inviter-name :group-name name})]
:group-name name})]]
[join-chat-button chat-id] [join-chat-button chat-id]
[decline-chat 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 (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?] modal?]
(letsubs [messages [:chats/current-chat-messages-stream] (letsubs [messages [:chats/current-chat-messages-stream]
current-public-key [:account/public-key]] current-public-key [:account/public-key]]
@ -157,63 +228,63 @@
(re-frame/dispatch [:chat.ui/set-chat-ui-props (re-frame/dispatch [:chat.ui/set-chat-ui-props
{:messages-focused? true {:messages-focused? true
:input-focused? false}]))} :input-focused? false}]))}
(cond (let [no-messages (empty? messages)
pending-invite-inviter-name flat-list-conf
[group-chat-join-section pending-invite-inviter-name chat] {:data messages
:footer [chat-intro-header-container chat no-messages]
(and (empty? messages) :key-fn #(or (:message-id %) (:value %))
messages-initialized?) :render-fn (fn [message]
(if group-chat [message-row
[empty-chat-container] {:group-chat group-chat
[empty-chat-container-one-to-one name]) :modal? modal?
:current-public-key current-public-key
:else :row message}])
[list/flat-list {:data messages :inverted true
:key-fn #(or (:message-id %) (:value %)) :onEndReached #(re-frame/dispatch
:render-fn (fn [message] [:chat.ui/load-more-messages])
[message-row {:group-chat group-chat :enableEmptySections true
:modal? modal? :keyboardShouldPersistTaps :handled}
:current-public-key current-public-key group-header {:header [group-chat-footer chat-id]}]
:row message}]) (if pending-invite-inviter-name
:inverted true [list/flat-list (merge flat-list-conf group-header)]
:onEndReached #(re-frame/dispatch [:chat.ui/load-more-messages]) [list/flat-list flat-list-conf]))))
:enableEmptySections true
:keyboardShouldPersistTaps :handled}])))
(defview messages-view-wrapper [modal?]
(letsubs [chat [:chats/current-chat]]
[messages-view chat modal?]))
(defn show-input-container? [my-public-key current-chat] (defn show-input-container? [my-public-key current-chat]
(or (not (models.chat/group-chat? current-chat)) (or (not (models.chat/group-chat? current-chat))
(group-chats.db/joined? my-public-key current-chat))) (group-chats.db/joined? my-public-key current-chat)))
(defview chat-root [modal?] (defview chat-root [modal?]
(letsubs [{:keys [public?] :as current-chat} [:chats/current-chat] (letsubs [{:keys [public? chat-id] :as current-chat} [:chats/current-chat]
my-public-key [:account/public-key] current-chat-id [:chats/current-chat-id]
show-bottom-info? [:chats/current-chat-ui-prop :show-bottom-info?] my-public-key [:account/public-key]
show-message-options? [:chats/current-chat-ui-prop :show-message-options?] show-bottom-info? [:chats/current-chat-ui-prop :show-bottom-info?]
show-stickers? [:chats/current-chat-ui-prop :show-stickers?]] show-message-options? [:chats/current-chat-ui-prop :show-message-options?]
;; this scroll-view is a hack that allows us to use on-blur and on-focus on Android show-stickers? [:chats/current-chat-ui-prop :show-stickers?]]
;; more details here: https://github.com/facebook/react-native/issues/11071 ;; this check of current-chat-id is necessary only because in a fresh public chat creation sometimes
[react/scroll-view {:scroll-enabled false ;; this component renders before current-chat-id is set to current chat-id. Hence further down in sub
:style style/scroll-root ;; components (e.g. chat-toolbar) there can be a brief visual inconsistancy like showing 'add contact'
:content-container-style style/scroll-root ;; in public chat
:keyboard-should-persist-taps :handled} (when (= chat-id current-chat-id)
[react/view {:style style/chat-view ;; this scroll-view is a hack that allows us to use on-blur and on-focus on Android
:on-layout (fn [e] ;; more details here: https://github.com/facebook/react-native/issues/11071
(re-frame/dispatch [:set :layout-height (-> e .-nativeEvent .-layout .-height)]))} [react/scroll-view {:scroll-enabled false
[chat-toolbar public? modal?] :style style/scroll-root
[messages-view-animation :content-container-style style/scroll-root
[messages-view-wrapper modal?]] :keyboard-should-persist-taps :handled}
(when (show-input-container? my-public-key current-chat) [react/view {:style style/chat-view
[input/container]) :on-layout (fn [e]
(when show-stickers? (re-frame/dispatch [:set :layout-height (-> e .-nativeEvent .-layout .-height)]))}
[stickers/stickers-view]) [chat-toolbar current-chat public? modal?]
(when show-bottom-info? [messages-view-animation
[bottom-info/bottom-info-view]) [messages-view current-chat modal?]]
(when show-message-options? (when (show-input-container? my-public-key current-chat)
[message-options/view])]])) [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 [] (defview chat []
[chat-root false]) [chat-root false])

View File

@ -120,6 +120,12 @@
(fn [{:keys [db]} [_ k v]] (fn [{:keys [db]} [_ k v]]
{:db (assoc 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 (handlers/register-handler-fx
:set-in :set-in
(fn [{:keys [db]} [_ path v]] (fn [{:keys [db]} [_ path v]]

View File

@ -118,7 +118,11 @@
(when loading? (utils/set-timeout #(re-frame/dispatch [:init-rest-of-chats]) 100))))} (when loading? (utils/set-timeout #(re-frame/dispatch [:init-rest-of-chats]) 100))))}
[react/view {:flex 1} [react/view {:flex 1}
[status-bar/status-bar {:type :main}] [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)]] [toolbar/toolbar nil nil [toolbar/content-title (i18n/label :t/chat)]]
[les-debug-info] [les-debug-info]
(cond loading? (cond loading?

View File

@ -375,6 +375,187 @@
(is (not (mailserver/resend-request {:db {:mailserver/current-request {:request-id "a"}}} (is (not (mailserver/resend-request {:db {:mailserver/current-request {:request-id "a"}}}
{:request-id "b"})))))) {: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 (deftest peers-summary-change
(testing "Mailserver added, sym-key doesn't exist" (testing "Mailserver added, sym-key doesn't exist"
(let [result (peers-summary-change-result false true false)] (let [result (peers-summary-change-result false true false)]

View File

@ -233,7 +233,9 @@
"other": "You can select {{count}} more participants" "other": "You can select {{count}} more participants"
}, },
"no-more-participants-available": "You can't add anymore 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}}", "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", "join-group-chat": "Join group",
"create-group-chat": "Create group chat", "create-group-chat": "Create group chat",
"group-chat-decline-invitation": "Decline invitation", "group-chat-decline-invitation": "Decline invitation",
@ -279,7 +281,10 @@
"currency-display-name-mzn": "Mozambique Metical", "currency-display-name-mzn": "Mozambique Metical",
"block": "Block", "block": "Block",
"wallet-set-up-title": "Set up your wallet", "wallet-set-up-title": "Set up your wallet",
"loading": "Loading... ",
"empty-chat-description": "There are no messages \nin this chat yet", "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.", "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": "Invalid address: \n {{data}}",
"wallet-invalid-address-checksum": "Error in address: \n {{data}}", "wallet-invalid-address-checksum": "Error in address: \n {{data}}",