From 6ea18cd8a3e84d0b453b8384fab8b572e9a0dc16 Mon Sep 17 00:00:00 2001 From: Alexander Pantyuhov Date: Tue, 4 Oct 2016 23:58:33 +0300 Subject: [PATCH] date indicators in chat (fixes #282) Former-commit-id: dcd711f5c2a5edfc4a016788961b2dba282a5ef6 --- .../chat/handlers/receive_message.cljs | 5 +- src/status_im/chat/handlers/send_message.cljs | 1 + src/status_im/chat/screen.cljs | 49 ++++++++++++++----- src/status_im/chat/styles/datemark.cljs | 24 +++++++++ src/status_im/chat/styles/message.cljs | 8 ++- src/status_im/chat/subs.cljs | 5 +- src/status_im/chat/views/datemark.cljs | 13 +++++ src/status_im/chat/views/message.cljs | 19 +++---- src/status_im/translations/en.cljs | 1 + src/status_im/utils/datetime.cljs | 31 +++++++----- 10 files changed, 113 insertions(+), 43 deletions(-) create mode 100644 src/status_im/chat/styles/datemark.cljs create mode 100644 src/status_im/chat/views/datemark.cljs diff --git a/src/status_im/chat/handlers/receive_message.cljs b/src/status_im/chat/handlers/receive_message.cljs index 494489eb2e..8dd0c77f1d 100644 --- a/src/status_im/chat/handlers/receive_message.cljs +++ b/src/status_im/chat/handlers/receive_message.cljs @@ -4,6 +4,7 @@ [status-im.data-store.messages :as messages] [status-im.chat.utils :as cu] [status-im.commands.utils :refer [generate-hiccup]] + [status-im.utils.random :as random] [status-im.constants :refer [content-type-command-request]] [cljs.reader :refer [read-string]] [status-im.data-store.chats :as chats])) @@ -24,7 +25,7 @@ (:public-key (accounts current-account-id))) (defn receive-message - [db [_ {:keys [from group-id chat-id message-id] :as message}]] + [db [_ {:keys [from group-id chat-id message-id timestamp] :as message}]] (let [same-message (messages/get-by-id message-id) current-identity (get-current-identity db) chat-id' (or group-id chat-id from) @@ -39,7 +40,7 @@ (cu/check-author-direction previous-message) (check-preview)) :chat-id chat-id' - :timestamp (.getTime (js/Date.)))] + :timestamp (or timestamp (random/timestamp)))] (store-message message') (when-not exists? (dispatch [:add-chat chat-id' (when group-chat? {:group-chat true})])) diff --git a/src/status_im/chat/handlers/send_message.cljs b/src/status_im/chat/handlers/send_message.cljs index 0dcbe78501..18afe87c30 100644 --- a/src/status_im/chat/handlers/send_message.cljs +++ b/src/status_im/chat/handlers/send_message.cljs @@ -22,6 +22,7 @@ {:message-id (random/id) :from identity :to chat-id + :timestamp (time/now-ms) :content (assoc content :preview preview-string) :content-type content-type-command :outgoing true diff --git a/src/status_im/chat/screen.cljs b/src/status_im/chat/screen.cljs index 9da618f41e..adec449948 100644 --- a/src/status_im/chat/screen.cljs +++ b/src/status_im/chat/screen.cljs @@ -19,6 +19,7 @@ [status-im.components.invertible-scroll-view :refer [invertible-scroll-view]] [status-im.components.toolbar.view :refer [toolbar]] [status-im.chat.views.message :refer [chat-message]] + [status-im.chat.views.datemark :refer [chat-datemark]] [status-im.chat.views.suggestions :refer [suggestion-container]] [status-im.chat.views.response :refer [response-view]] [status-im.chat.views.new-message :refer [chat-message-new]] @@ -26,7 +27,8 @@ [status-im.chat.views.bottom-info :refer [bottom-info-view]] [status-im.i18n :refer [label label-pluralize]] [status-im.components.animation :as anim] - [status-im.constants :refer [console-chat-id]] + [status-im.constants :refer [console-chat-id + content-type-status]] [reagent.core :as r] [clojure.string :as str] [cljs-time.core :as t])) @@ -66,13 +68,19 @@ (for [member ["Geoff" "Justas"]] ^{:key member} [typing member])]) -(defn message-row [{:keys [contact-by-identity group-chat messages-count]}] - (fn [row _ idx] - (let [message (-> row - (add-message-color contact-by-identity) - (assoc :group-chat group-chat) - (assoc :last-message (= (js/parseInt idx) (dec messages-count))))] - (list-item [chat-message message])))) +(defmulti message-row (fn [{{:keys [type]} :row}] type)) + +(defmethod message-row :datemark + [{{:keys [value]} :row}] + (list-item [chat-datemark value])) + +(defmethod message-row :default + [{:keys [contact-by-identity group-chat messages-count row index]}] + (let [message (-> row + (add-message-color contact-by-identity) + (assoc :group-chat group-chat) + (assoc :last-message (= (js/parseInt index) (dec messages-count))))] + (list-item [chat-message message]))) (defn online-text [contact chat-id] (cond @@ -134,14 +142,31 @@ :custom-action [toolbar-action] :style (get-in platform-specific [:component-styles :toolbar])}]]) +(defn messages-with-timemarks [all-messages] + (let [messages (->> all-messages + (map #(assoc % :datemark (time/day-relative (:timestamp %)))) + (group-by :datemark) + (map (fn [[k v]] [v {:type :datemark :value k}])) + (flatten)) + remove-last? (some (fn [{:keys [content-type]}] + (= content-type content-type-status)) + messages)] + (if remove-last? + (drop-last messages) + messages))) + (defview messages-view [group-chat] [messages [:chat :messages] contacts [:chat :contacts] loaded? [:all-messages-loaded?]] - (let [contacts' (contacts-by-identity contacts)] - [list-view {:renderRow (message-row {:contact-by-identity contacts' - :group-chat group-chat - :messages-count (count messages)}) + (let [contacts' (contacts-by-identity contacts) + messages (messages-with-timemarks messages)] + [list-view {:renderRow (fn [row _ index] + (message-row {:contact-by-identity contacts' + :group-chat group-chat + :messages-count (count messages) + :row row + :index index})) :renderScrollComponent #(invertible-scroll-view (js->clj %)) :onEndReached (when-not loaded? #(dispatch [:load-more-messages])) :enableEmptySections true diff --git a/src/status_im/chat/styles/datemark.cljs b/src/status_im/chat/styles/datemark.cljs new file mode 100644 index 0000000000..2517428f95 --- /dev/null +++ b/src/status_im/chat/styles/datemark.cljs @@ -0,0 +1,24 @@ +(ns status-im.chat.styles.datemark) + +(def datemark-wrapper + {:flex 1 + :flex-direction :column + :align-items :center + :justify-content :center}) + +(def datemark + {:flex 1 + :flex-direction :column + :align-items :center + :justify-content :center + :background-color "#bbc4cb33" + :padding-left 12 + :padding-right 12 + :margin-top 8 + :margin-bottom 8 + :border-radius 12 + :height 24}) + +(def datemark-text + {:color "#838c93" + :font-size 12}) \ No newline at end of file diff --git a/src/status_im/chat/styles/message.cljs b/src/status_im/chat/styles/message.cljs index 9073224169..0740c11ef9 100644 --- a/src/status_im/chat/styles/message.cljs +++ b/src/status_im/chat/styles/message.cljs @@ -36,9 +36,13 @@ (when (and last-message (not typing)) {:paddingBottom 20})) +(def message-datemark + {:margin-top 10 + :margin-bottom -4}) + (def message-body-base - {:paddingRight 8 - :paddingLeft 8}) + {:padding-right 8 + :padding-left 8}) (defn message-body [{:keys [outgoing] :as message}] diff --git a/src/status_im/chat/subs.cljs b/src/status_im/chat/subs.cljs index 89b73abe12..418e8e243a 100644 --- a/src/status_im/chat/subs.cljs +++ b/src/status_im/chat/subs.cljs @@ -7,7 +7,9 @@ [status-im.constants :refer [response-suggesstion-resize-duration]] [status-im.chat.constants :as c] [status-im.chat.views.plain-message :as plain-message] - [status-im.chat.views.command :as command])) + [status-im.chat.views.command :as command] + [status-im.constants :refer [content-type-status]] + [status-im.utils.datetime :as time])) (register-sub :chat-properties (fn [db [_ properties]] @@ -29,7 +31,6 @@ (get-in [:chats (:current-chat-id @db) k]) (reaction)))) - (register-sub :get-chat-messages (fn [db _] (let [chat-id (:current-chat-id @db)] diff --git a/src/status_im/chat/views/datemark.cljs b/src/status_im/chat/views/datemark.cljs new file mode 100644 index 0000000000..527c6d91b3 --- /dev/null +++ b/src/status_im/chat/views/datemark.cljs @@ -0,0 +1,13 @@ +(ns status-im.chat.views.datemark + (:require [re-frame.core :refer [subscribe dispatch]] + [status-im.components.react :refer [view + text]] + [clojure.string :as str] + [status-im.i18n :refer [label]] + [status-im.chat.styles.datemark :as st])) + +(defn chat-datemark [value] + [view st/datemark-wrapper + [view st/datemark + [text {:style st/datemark-text} + (str/capitalize (or value (label :t/datetime-today)))]]]) \ No newline at end of file diff --git a/src/status_im/chat/views/message.cljs b/src/status_im/chat/views/message.cljs index 55827f9953..d35322ceb2 100644 --- a/src/status_im/chat/views/message.cljs +++ b/src/status_im/chat/views/message.cljs @@ -13,6 +13,7 @@ [status-im.chat.views.request-message :refer [message-content-command-request]] [status-im.chat.styles.message :as st] [status-im.chat.styles.command-pill :as pill-st] + [status-im.chat.views.datemark :refer [chat-datemark]] [status-im.models.commands :refer [parse-command-message-content parse-command-request]] [status-im.resources :as res] @@ -24,13 +25,6 @@ [status-im.utils.identicon :refer [identicon]] [status-im.chat.utils :as cu])) -(defn message-date [timestamp] - [view {} - [view st/message-date-container - [text {:style st/message-date-text - :font :default} - (time/to-short-str timestamp)]]]) - (defn contact-photo [photo-path] [view st/contact-photo-container [image {:source (if (s/blank? photo-path) @@ -45,7 +39,7 @@ [view st/online-dot-right]])) (defview message-content-status - [{:keys [from content]}] + [{:keys [from content datemark]}] [{chat-name :name} [:get-chat-by-id from] photo-path [:chat-photo from]] [view st/status-container @@ -57,7 +51,9 @@ (or chat-name from)] [text {:style st/status-text :font :default} - content]]) + content] + [view st/message-datemark + [chat-datemark datemark]]]) (defn message-content-audio [_] [view st/audio-container @@ -295,11 +291,8 @@ :from from :message-id message-id}]))) :reagent-render - (fn [{:keys [outgoing timestamp new-day group-chat] :as message}] + (fn [{:keys [outgoing group-chat] :as message}] [message-container message - ;; TODO there is no new-day info in message - (when new-day - [message-date timestamp]) [view (let [incoming-group (and group-chat (not outgoing))] [message-content diff --git a/src/status_im/translations/en.cljs b/src/status_im/translations/en.cljs index 7d981febbd..4eaf3ce64c 100644 --- a/src/status_im/translations/en.cljs +++ b/src/status_im/translations/en.cljs @@ -49,6 +49,7 @@ :datetime-multiple "s" :datetime-ago "ago" :datetime-yesterday "yesterday" + :datetime-today "today" ;profile :profile "Profile" diff --git a/src/status_im/utils/datetime.cljs b/src/status_im/utils/datetime.cljs index 01748b8ef2..aafe186284 100644 --- a/src/status_im/utils/datetime.cljs +++ b/src/status_im/utils/datetime.cljs @@ -18,18 +18,25 @@ (def time-zone-offset (hours (- (/ (.getTimezoneOffset (js/Date.)) 60)))) -(defn to-short-str [ms] - (let [date (from-long ms) - local (plus date time-zone-offset) - today-date (t/today) - today (date-time (t/year today-date) - (t/month today-date) - (t/day today-date)) - yesterday (plus today (days -1))] - (cond - (before? local yesterday) (unparse (formatter "dd MMM") local) - (before? local today) (label :t/datetime-yesterday) - :else (unparse (formatters :hour-minute) local)))) +(defn to-short-str + ([ms] + (to-short-str ms #(unparse (formatters :hour-minute) %))) + ([ms today-format-fn] + (let [date (from-long ms) + local (plus date time-zone-offset) + today-date (t/today) + today (date-time (t/year today-date) + (t/month today-date) + (t/day today-date)) + yesterday (plus today (days -1))] + (cond + (before? local yesterday) (unparse (formatter "dd MMM") local) + (before? local today) (label :t/datetime-yesterday) + :else (today-format-fn local))))) + +(defn day-relative [ms] + (when (> ms 0) + (to-short-str ms #(label :t/datetime-today)))) (defn format-time-ago [diff unit] (let [name (label-pluralize diff (:name unit))]