diff --git a/src/status_im/chat/handlers.cljs b/src/status_im/chat/handlers.cljs index 1ddb552cc0..9ec8b78858 100644 --- a/src/status_im/chat/handlers.cljs +++ b/src/status_im/chat/handlers.cljs @@ -22,8 +22,6 @@ [status-im.chat.handlers.animation :refer [update-response-height get-response-height]])) -(def delta 1) - (register-handler :set-show-actions (fn [db [_ show-actions]] (assoc db :show-actions show-actions))) @@ -54,14 +52,18 @@ (dispatch [:animate-cancel-command]) (dispatch [:cancel-command]))))) +(defn animate-set-chat-command-content [db _] + (when (commands/get-chat-command-to-msg-id db) + (dispatch [:animate-response-resize]))) + (register-handler :set-chat-command-content + (after animate-set-chat-command-content) (fn [{:keys [current-chat-id] :as db} [_ content]] (as-> db db (commands/set-chat-command-content db content) (assoc-in db [:chats current-chat-id :input-text] nil) (if (commands/get-chat-command-to-msg-id db) - (do (dispatch [:animate-response-resize]) - (update-response-height db)) + (update-response-height db) db)))) (defn update-input-text @@ -82,13 +84,6 @@ (fn [db [_ input]] (assoc db :message-input input))) -(register-handler :prepare-message-input - (u/side-effect! - (fn [db _] - (when-let [message-input (:message-input db)] - (.clear message-input) - (.focus message-input))))) - (register-handler :blur-message-input (u/side-effect! (fn [db _] @@ -135,7 +130,21 @@ (defn add-message-to-db [db chat-id message] (let [messages [:chats chat-id :messages]] - (update-in db messages conj message))) + (update-in db messages conj (assoc message :chat-id chat-id + :new? true)))) + +(defn set-message-shown + [db chat-id msg-id] + (update-in db [:chats chat-id :messages] (fn [messages] + (map (fn [msg] + (if (= msg-id (:msg-id msg)) + (assoc msg :new? false) + msg)) + messages)))) + +(register-handler :set-message-shown + (fn [db [_ {:keys [chat-id msg-id]}]] + (set-message-shown db chat-id msg-id))) (defn prepare-message [{:keys [identity current-chat-id] :as db} _] diff --git a/src/status_im/chat/handlers/animation.cljs b/src/status_im/chat/handlers/animation.cljs index a7526ec19f..2859261ab3 100644 --- a/src/status_im/chat/handlers/animation.cljs +++ b/src/status_im/chat/handlers/animation.cljs @@ -10,12 +10,16 @@ (def zero-height input-height) -(register-handler :finish-animate-cancel-command - (fn [db _] - (assoc-in db [:animations :commands-input-is-switching?] false))) +(defn animation-handler + ([name handler] (animation-handler name nil handler)) + ([name middleware handler] + (register-handler name [(path :animations) middleware] handler))) -(register-handler :animate-cancel-command - (path :animations) +(animation-handler :finish-animate-cancel-command + (fn [db _] + (assoc db :commands-input-is-switching? false))) + +(animation-handler :animate-cancel-command (fn [db _] (if-not (:commands-input-is-switching? db) (assoc db @@ -26,20 +30,19 @@ :messages-offset 0) db))) -(register-handler :finish-animate-response-resize +(animation-handler :finish-animate-response-resize (fn [db _] - (let [fixed (get-in db [:animations :to-response-height])] - (-> db - (assoc-in [:animations :response-height-current] fixed) - (assoc-in [:animations :response-resize?] false))))) + (let [fixed (:to-response-height db)] + (assoc db :response-height-current fixed + :response-resize? false)))) -(register-handler :set-response-height +(animation-handler :set-response-height (fn [db [_ value]] - (assoc-in db [:animations :response-height-current] value))) + (assoc db :response-height-current value))) -(register-handler :animate-response-resize +(animation-handler :animate-response-resize (fn [db _] - (assoc-in db [:animations :response-resize?] true))) + (assoc db :response-resize? true))) (defn get-response-height [db] (let [command (commands/get-chat-command db) @@ -55,10 +58,9 @@ (defn update-response-height [db] (assoc-in db [:animations :to-response-height] (get-response-height db))) -(register-handler :finish-show-response - (after #(dispatch [:prepare-message-input])) +(animation-handler :finish-show-response (fn [db _] - (assoc-in db [:animations :commands-input-is-switching?] false))) + (assoc db :commands-input-is-switching? false))) (register-handler :animate-show-response (after #(dispatch [:animate-response-resize])) @@ -71,24 +73,22 @@ (assoc-in [:animations :messages-offset] request-info-height) (update-response-height)))) -(register-handler :set-response-max-height +(animation-handler :set-response-max-height (fn [db [_ height]] - (let [prev-height (get-in db [:animations :response-height-max])] + (let [prev-height (:response-height-max db)] (if (not= height prev-height) - (let [db (assoc-in db [:animations :response-height-max] height)] - (if (= prev-height (get-in db [:animations :to-response-height])) - (-> db - (assoc-in [:animations :to-response-height] height) - (assoc-in [:animations :response-height-current] height)) + (let [db (assoc db :response-height-max height)] + (if (= prev-height (:to-response-height db)) + (assoc db :to-response-height height + :response-height-current height) db)) db)))) -(register-handler :on-drag-response +(animation-handler :on-drag-response (fn [db [_ dy]] - (let [fixed (get-in db [:animations :to-response-height])] - (-> db - (assoc-in [:animations :response-height-current] (- fixed dy)) - (assoc-in [:animations :response-resize?] false))))) + (let [fixed (:to-response-height db)] + (assoc db :response-height-current (- fixed dy) + :response-resize? false)))) (register-handler :fix-response-height (fn [db _] diff --git a/src/status_im/chat/screen.cljs b/src/status_im/chat/screen.cljs index a2fc957b1e..feb88de74e 100644 --- a/src/status_im/chat/screen.cljs +++ b/src/status_im/chat/screen.cljs @@ -13,7 +13,7 @@ [status-im.components.chat-icon.screen :refer [chat-icon-view-action chat-icon-view-menu-item]] [status-im.chat.styles.screen :as st] - [status-im.utils.listview :refer [to-datasource]] + [status-im.utils.listview :refer [to-datasource-inverted]] [status-im.utils.utils :refer [truncate-str]] [status-im.components.invertible-scroll-view :refer [invertible-scroll-view]] [status-im.components.toolbar :refer [toolbar]] @@ -61,12 +61,12 @@ (for [member ["Geoff" "Justas"]] ^{:key member} [typing member])]) -(defn message-row [contact-by-identity group-chat] +(defn message-row [contact-by-identity group-chat messages-count] (fn [row _ idx] (let [msg (-> row (add-msg-color contact-by-identity) (assoc :group-chat group-chat) - (assoc :last-msg (zero? (js/parseInt idx))))] + (assoc :last-msg (= (js/parseInt idx) (dec messages-count))))] (list-item [chat-message msg])))) (defn on-action-selected [position] @@ -222,12 +222,12 @@ [messages [:chat :messages] contacts [:chat :contacts]] (let [contacts' (contacts-by-identity contacts)] - [list-view {:renderRow (message-row contacts' group-chat) + [list-view {:renderRow (message-row contacts' group-chat (count messages)) :renderScrollComponent #(invertible-scroll-view (js->clj %)) :onEndReached #(dispatch [:load-more-messages]) :enableEmptySections true :keyboardShouldPersistTaps true - :dataSource (to-datasource messages)}])) + :dataSource (to-datasource-inverted messages)}])) (defn messages-container-animation-logic [{:keys [to-value val]}] (fn [_] @@ -235,11 +235,11 @@ (anim/start (anim/spring val {:toValue to-value}) (fn [arg] (when (.-finished arg) - (dispatch [:set-in [:animations ::messages-offset-current] to-value]))))))) + (dispatch [:set-animation ::messages-offset-current to-value]))))))) (defn messages-container [messages] - (let [to-messages-offset (subscribe [:get-in [:animations :messages-offset]]) - cur-messages-offset (subscribe [:get-in [:animations ::messages-offset-current]]) + (let [to-messages-offset (subscribe [:animations :messages-offset]) + cur-messages-offset (subscribe [:animations ::messages-offset-current]) messages-offset (anim/create-value (or @cur-messages-offset 0)) context {:to-value to-messages-offset :val messages-offset} diff --git a/src/status_im/chat/styles/message.cljs b/src/status_im/chat/styles/message.cljs index 79e3cb24a7..464c90ddd5 100644 --- a/src/status_im/chat/styles/message.cljs +++ b/src/status_im/chat/styles/message.cljs @@ -131,20 +131,27 @@ (def command-request-message-view {:borderRadius 14 :padding 12 + :paddingRight 28 :backgroundColor color-white}) (def command-request-from-text (merge style-sub-text {:marginBottom 2})) -(defn command-request-image-view - [command] - {:position :absolute - :top 12 - :right 0 - :width 32 +(def command-request-image-touchable + {:position :absolute + :top 4 + :right -8 + :alignItems :center + :justifyContent :center + :width 48 + :height 48}) + +(defn command-request-image-view [command scale] + {:width 32 :height 32 :borderRadius 50 - :backgroundColor (:color command)}) + :backgroundColor (:color command) + :transform [{:scale scale}]}) (def command-request-image {:position :absolute @@ -309,6 +316,9 @@ (def message-date-text (assoc style-sub-text :textAlign :center)) +(defn message-container [height] + {:height height}) + (def new-message-container {:backgroundColor color-white :elevation 4}) diff --git a/src/status_im/chat/views/command.cljs b/src/status_im/chat/views/command.cljs index 7ed7faafc3..582dacfa49 100644 --- a/src/status_im/chat/views/command.cljs +++ b/src/status_im/chat/views/command.cljs @@ -32,7 +32,7 @@ [text {:style st/command-text} (:text command)]]) (defview cancel-button [] - [commands-input-is-switching? [:get-in [:animations :commands-input-is-switching?]]] + [commands-input-is-switching? [:animations :commands-input-is-switching?]] [touchable-highlight {:disabled commands-input-is-switching? :on-press cancel-command-input} [view st/cancel-container diff --git a/src/status_im/chat/views/message.cljs b/src/status_im/chat/views/message.cljs index ceb430fc76..3fe4175931 100644 --- a/src/status_im/chat/views/message.cljs +++ b/src/status_im/chat/views/message.cljs @@ -1,23 +1,28 @@ (ns status-im.chat.views.message (:require [clojure.string :as s] [re-frame.core :refer [subscribe dispatch]] + [reagent.core :as r] [status-im.components.react :refer [view - text - image - touchable-highlight]] + animated-view + text + image + touchable-highlight]] + [status-im.components.animation :as anim] + [status-im.chat.views.request-message :refer [message-content-command-request]] [status-im.chat.styles.message :as st] [status-im.models.commands :refer [parse-command-msg-content - parse-command-request]] + parse-command-request]] [status-im.resources :as res] + [status-im.utils.datetime :as time] [status-im.constants :refer [text-content-type - content-type-status - content-type-command - content-type-command-request]])) + content-type-status + content-type-command + content-type-command-request]])) -(defn message-date [{:keys [date]}] +(defn message-date [timestamp] [view {} [view st/message-date-container - [text {:style st/message-date-text} date]]]) + [text {:style st/message-date-text} (time/to-short-str timestamp)]]]) (defn contact-photo [{:keys [photo-path]}] [view st/contact-photo-container @@ -69,36 +74,6 @@ "******" content)]])))) -(defn set-chat-command [msg-id command] - (dispatch [:set-response-chat-command msg-id (:command command)])) - -(defn label [{:keys [command]}] - (->> (name command) - (str "request-"))) - -(defn message-content-command-request - [{:keys [msg-id content from incoming-group]}] - (let [commands-atom (subscribe [:get-commands])] - (fn [{:keys [msg-id content from incoming-group]}] - (let [commands @commands-atom - {:keys [command content]} (parse-command-request commands content)] - [touchable-highlight {:onPress #(set-chat-command msg-id command) - :accessibility-label (label command)} - [view st/comand-request-view - [view st/command-request-message-view - (when incoming-group - [text {:style st/command-request-from-text} - from]) - [text {:style st/style-message-text} - content]] - [view (st/command-request-image-view command) - [image {:source (:request-icon command) - :style st/command-request-image}]] - (when (:request-text command) - [view st/command-request-text-view - [text {:style st/style-sub-text} - (:request-text command)]])]])))) - (defn message-view [message content] [view (st/message-view message) @@ -185,12 +160,50 @@ (when (and outgoing delivery-status) [message-delivery-status {:delivery-status delivery-status}])])) +(defn message-container-animation-logic [{:keys [to-value val callback]}] + (fn [_] + (let [to-value @to-value] + (when (< 0 to-value) + (anim/start + (anim/spring val {:toValue to-value + :friction 4 + :tension 10}) + (fn [arg] + (when (.-finished arg) + (callback)))))))) + +(defn message-container [message & children] + (if (:new? message) + (let [layout-height (r/atom 0) + anim-value (anim/create-value 1) + anim-callback #(dispatch [:set-message-shown message]) + context {:to-value layout-height + :val anim-value + :callback anim-callback} + on-update (message-container-animation-logic context)] + (r/create-class + {:component-did-mount + on-update + :component-did-update + on-update + :reagent-render + (fn [message & children] + @layout-height + [animated-view {:style (st/message-container anim-value)} + (into [view {:onLayout (fn [event] + (let [height (.. event -nativeEvent -layout -height)] + (reset! layout-height height)))}] + children)])})) + (into [view] children))) + (defn chat-message - [{:keys [outgoing delivery-status date new-day group-chat] + [{:keys [outgoing delivery-status timestamp new-day group-chat] :as message}] - [view {} - (when new-day [message-date {:date date}]) - [view {} + [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 (if incoming-group diff --git a/src/status_im/chat/views/message_input.cljs b/src/status_im/chat/views/message_input.cljs index c8d81a30b6..4c26e15eb8 100644 --- a/src/status_im/chat/views/message_input.cljs +++ b/src/status_im/chat/views/message_input.cljs @@ -19,29 +19,29 @@ [status-im.constants :refer [response-input-hiding-duration]])) (defview send-button [{:keys [on-press accessibility-label]}] - [commands-input-is-switching? [:get-in [:animations :commands-input-is-switching?]]] + [commands-input-is-switching? [:animations :commands-input-is-switching?]] [touchable-highlight {:disabled commands-input-is-switching? :on-press on-press :accessibility-label accessibility-label} [view st/send-container [icon :send st/send-icon]]]) -(defn message-input-container-animation-logic [{:keys [to-value val]}] +(defn animation-logic [{:keys [to-value val]}] (fn [_] (let [to-value @to-value] (anim/start (anim/timing val {:toValue to-value :duration response-input-hiding-duration}) (fn [arg] (when (.-finished arg) - (dispatch [:set-in [:animations ::message-input-offset-current] to-value]))))))) + (dispatch [:set-animation ::message-input-offset-current to-value]))))))) (defn message-input-container [input] - (let [to-message-input-offset (subscribe [:get-in [:animations :message-input-offset]]) - cur-message-input-offset (subscribe [:get-in [:animations ::message-input-offset-current]]) + (let [to-message-input-offset (subscribe [:animations :message-input-offset]) + cur-message-input-offset (subscribe [:animations ::message-input-offset-current]) message-input-offset (anim/create-value (or @cur-message-input-offset 0)) context {:to-value to-message-input-offset :val message-input-offset} - on-update (message-input-container-animation-logic context)] + on-update (animation-logic context)] (r/create-class {:component-did-mount on-update @@ -60,7 +60,7 @@ input-command [:get-chat-command-content] staged-commands [:get-chat-staged-commands] typing-command? [:typing-command?] - commands-input-is-switching? [:get-in [:animations :commands-input-is-switching?]]] + commands-input-is-switching? [:animations :commands-input-is-switching?]] (let [dismiss-keyboard (not (or command typing-command?)) response? (and command to-msg-id) message-input? (or (not command) commands-input-is-switching?) @@ -99,7 +99,7 @@ input-command [:get-chat-command-content] staged-commands [:get-chat-staged-commands] typing-command? [:typing-command?] - commands-input-is-switching? [:get-in [:animations :commands-input-is-switching?]]] + commands-input-is-switching? [:animations :commands-input-is-switching?]] (let [dismiss-keyboard (not (or command typing-command?)) response? (and command to-msg-id) message-input? (or (not command) commands-input-is-switching?)] diff --git a/src/status_im/chat/views/new_message.cljs b/src/status_im/chat/views/new_message.cljs index be01687c74..3a330d0a0b 100644 --- a/src/status_im/chat/views/new_message.cljs +++ b/src/status_im/chat/views/new_message.cljs @@ -28,12 +28,10 @@ :request {:input-options {:keyboardType :numeric}} (throw (js/Error. "Uknown command type"))))]) -(defn chat-message-new [] - (let [command-atom (subscribe [:get-chat-command]) - staged-commands-atom (subscribe [:get-chat-staged-commands])] - (fn [] - (let [staged-commands @staged-commands-atom] - [view st/new-message-container - (when (and staged-commands (pos? (count staged-commands))) - [staged-commands-view staged-commands]) - [show-input @command-atom]])))) +(defview chat-message-new [] + [command [:get-chat-command] + staged-commands [:get-chat-staged-commands]] + [view st/new-message-container + (when (and staged-commands (pos? (count staged-commands))) + [staged-commands-view staged-commands]) + [show-input command]]) diff --git a/src/status_im/chat/views/plain_message.cljs b/src/status_im/chat/views/plain_message.cljs index b516c64dc9..7c637ebbd0 100644 --- a/src/status_im/chat/views/plain_message.cljs +++ b/src/status_im/chat/views/plain_message.cljs @@ -28,7 +28,20 @@ (when (message-valid? staged-commands message) (send dismiss-keyboard))) -(defn commands-button-animation-logic [{:keys [to-value val]}] +(defn prepare-message-input [message-input] + (when message-input + (.clear message-input) + (.focus message-input))) + +(defn commands-button-animation-callback [message-input] + (fn [arg to-value] + (when (.-finished arg) + (dispatch [:set-animation ::message-input-buttons-scale-current to-value]) + (when (<= to-value 0.1) + (dispatch [:finish-show-response]) + (prepare-message-input @message-input))))) + +(defn button-animation-logic [{:keys [to-value val callback]}] (fn [_] (let [to-scale @to-value minimum 0.1 @@ -37,21 +50,22 @@ :else to-scale)] (anim/start (anim/timing val {:toValue scale :duration response-input-hiding-duration}) - (fn [arg] - (when (.-finished arg) - (dispatch [:set-in [:animations ::message-input-buttons-scale-current] scale]) - (when (= to-scale minimum) - (dispatch [:finish-show-response])))))))) + (when callback + (fn [arg] + (callback arg to-scale))))))) (defn commands-button [] (let [typing-command? (subscribe [:typing-command?]) - animation? (subscribe [:get-in [:animations :commands-input-is-switching?]]) - to-scale (subscribe [:get-in [:animations :message-input-buttons-scale]]) - cur-scale (subscribe [:get-in [:animations ::message-input-buttons-scale-current]]) + message-input (subscribe [:get :message-input]) + animation? (subscribe [:animations :commands-input-is-switching?]) + to-scale (subscribe [:animations :message-input-buttons-scale]) + cur-scale (subscribe [:animations ::message-input-buttons-scale-current]) buttons-scale (anim/create-value (or @cur-scale 1)) + anim-callback (commands-button-animation-callback message-input) context {:to-value to-scale - :val buttons-scale} - on-update (commands-button-animation-logic context)] + :val buttons-scale + :callback anim-callback} + on-update (button-animation-logic context)] (r/create-class {:component-did-mount on-update @@ -70,13 +84,13 @@ [icon :list st/list-icon])]]))}))) (defn smile-button [] - (let [animation? (subscribe [:get-in [:animations :commands-input-is-switching?]]) - to-scale (subscribe [:get-in [:animations :message-input-buttons-scale]]) - cur-scale (subscribe [:get-in [:animations ::message-input-buttons-scale-current]]) + (let [animation? (subscribe [:animations :commands-input-is-switching?]) + to-scale (subscribe [:animations :message-input-buttons-scale]) + cur-scale (subscribe [:animations ::message-input-buttons-scale-current]) buttons-scale (anim/create-value (or @cur-scale 1)) context {:to-value to-scale :val buttons-scale} - on-update (commands-button-animation-logic context)] + on-update (button-animation-logic context)] (r/create-class {:component-did-mount on-update diff --git a/src/status_im/chat/views/request_message.cljs b/src/status_im/chat/views/request_message.cljs new file mode 100644 index 0000000000..2bf55c7069 --- /dev/null +++ b/src/status_im/chat/views/request_message.cljs @@ -0,0 +1,85 @@ +(ns status-im.chat.views.request-message + (:require [re-frame.core :refer [subscribe dispatch]] + [reagent.core :as r] + [status-im.components.react :refer [view + animated-view + text + image + touchable-highlight]] + [status-im.chat.styles.message :as st] + [status-im.models.commands :refer [parse-command-request]] + [status-im.components.animation :as anim])) + +(def request-message-icon-scale-delay 600) + +(defn set-chat-command [msg-id command] + (dispatch [:set-response-chat-command msg-id (:command command)])) + +(defn label [{:keys [command]}] + (->> (name command) + (str "request-"))) + +(defn request-button-animation-logic [{:keys [to-value val loop?]}] + (fn [_] + (let [loop? @loop? + minimum 1 + maximum 1.3 + to-scale (if loop? + (or @to-value maximum) + minimum)] + (anim/start + (anim/anim-sequence + [(anim/anim-delay (if loop? request-message-icon-scale-delay 0)) + (anim/spring val {:toValue to-scale})]) + (fn [arg] + (when (.-finished arg) + (dispatch [:set-animation ::request-button-scale-current to-scale]) + (when loop? + (dispatch [:set-animation ::request-button-scale (if (= to-scale minimum) + maximum + minimum)])))))))) + +(defn request-button [msg-id command] + (let [to-scale (subscribe [:animations ::request-button-scale]) + cur-scale (subscribe [:animations ::request-button-scale-current]) + scale-anim-val (anim/create-value (or @cur-scale 1)) + loop? (r/atom true) + context {:to-value to-scale + :val scale-anim-val + :loop? loop?} + on-update (request-button-animation-logic context)] + (r/create-class + {:component-did-mount + on-update + :component-did-update + on-update + :reagent-render + (fn [msg-id command] + @to-scale + [touchable-highlight {:on-press (fn [] + (reset! loop? false) + (set-chat-command msg-id command)) + :style st/command-request-image-touchable + :accessibility-label (label command)} + [animated-view {:style (st/command-request-image-view command scale-anim-val)} + [image {:source (:request-icon command) + :style st/command-request-image}]]])}))) + +(defn message-content-command-request + [{:keys [msg-id content from incoming-group]}] + (let [commands-atom (subscribe [:get-commands])] + (fn [{:keys [msg-id content from incoming-group]}] + (let [commands @commands-atom + {:keys [command content]} (parse-command-request commands content)] + [view st/comand-request-view + [view st/command-request-message-view + (when incoming-group + [text {:style st/command-request-from-text} + from]) + [text {:style st/style-message-text} + content]] + [request-button msg-id command] + (when (:request-text command) + [view st/command-request-text-view + [text {:style st/style-sub-text} + (:request-text command)]])])))) diff --git a/src/status_im/chat/views/response.cljs b/src/status_im/chat/views/response.cljs index 061403ca3a..6f745b5844 100644 --- a/src/status_im/chat/views/response.cljs +++ b/src/status_im/chat/views/response.cljs @@ -61,7 +61,7 @@ (anim/start (anim/spring val {:toValue to-value}) (fn [arg] (when (.-finished arg) - (dispatch [:set-in [:animations :response-height-current] to-value]) + (dispatch [:set-animation :response-height-current to-value]) (dispatch [:finish-animate-response-resize]) (when (= to-value input-height) (dispatch [:finish-animate-cancel-command]) @@ -69,10 +69,10 @@ (anim/set-value val @current-value)))) (defn container [& children] - (let [commands-input-is-switching? (subscribe [:get-in [:animations :commands-input-is-switching?]]) - response-resize? (subscribe [:get-in [:animations :response-resize?]]) - to-response-height (subscribe [:get-in [:animations :to-response-height]]) - cur-response-height (subscribe [:get-in [:animations :response-height-current]]) + (let [commands-input-is-switching? (subscribe [:animations :commands-input-is-switching?]) + response-resize? (subscribe [:animations :response-resize?]) + to-response-height (subscribe [:animations :to-response-height]) + cur-response-height (subscribe [:animations :response-height-current]) response-height (anim/create-value (or @cur-response-height 0)) context {:animation? (reaction (or @commands-input-is-switching? @response-resize?)) :to-value to-response-height diff --git a/src/status_im/components/animation.cljs b/src/status_im/components/animation.cljs index 677223b338..a945d44f4c 100644 --- a/src/status_im/components/animation.cljs +++ b/src/status_im/components/animation.cljs @@ -11,6 +11,12 @@ (defn spring [anim-value config] (.spring animated anim-value (clj->js config))) +(defn anim-sequence [animations] + (.sequence animated (clj->js animations))) + +(defn anim-delay [duration] + (.delay animated duration)) + (defn event [config] (.event animated (clj->js [nil, config]))) diff --git a/src/status_im/components/main_tabs.cljs b/src/status_im/components/main_tabs.cljs index 16fb5b074d..01551e75da 100644 --- a/src/status_im/components/main_tabs.cljs +++ b/src/status_im/components/main_tabs.cljs @@ -1,11 +1,16 @@ (ns status-im.components.main-tabs - (:require-macros [status-im.utils.views :refer [defview]]) + (:require-macros [reagent.ratom :refer [reaction]] + [status-im.utils.views :refer [defview]]) (:require [re-frame.core :refer [subscribe dispatch dispatch-sync]] + [reagent.core :as r] [status-im.components.react :refer [view + animated-view text-input text image - touchable-highlight]] + touchable-highlight + get-dimensions]] + [status-im.components.animation :as anim] [status-im.chats-list.screen :refer [chats-list]] [status-im.discovery.screen :refer [discovery]] [status-im.contacts.screen :refer [contact-list]] @@ -14,33 +19,91 @@ [status-im.components.styles :as common-st] [status-im.i18n :refer [label]])) +(def window-width (:width (get-dimensions "window"))) + (def tab-list [{:view-id :chat-list :title (label :t/chats) :screen chats-list - :icon :icon_tab_chats} + :icon :icon_tab_chats + :index 0} {:view-id :discovery :title (label :t/discovery) :screen discovery - :icon :icon_tab_discovery} + :icon :icon_tab_discovery + :index 1} {:view-id :contact-list :title (label :t/contacts) :screen contact-list - :icon :icon_tab_contacts}]) + :icon :icon_tab_contacts + :index 2}]) -(defn show-view? [current-view view-id] - (let [key-map {:key view-id}] - (if (= current-view view-id) - (merge st/show-tab key-map) - (merge st/hide-tab key-map)))) +(defn animation-logic [{:keys [offsets val tab-id to-tab-id]}] + (fn [_] + (when-let [offsets @offsets] + (let [from-value (:from offsets) + to-value (:to offsets) + to-tab-id @to-tab-id] + (anim/set-value val from-value) + (when to-value + (anim/start + (anim/timing val {:toValue to-value + :duration 300}) + (when (= tab-id to-tab-id) + (fn [arg] + (when (.-finished arg) + (dispatch [:on-navigated-to-tab])))))))))) -(defn tab-view [current-view {:keys [view-id screen]}] - [view (show-view? current-view view-id) +(defn get-tab-index-by-id [id] + (:index (first (filter #(= id (:view-id %)) tab-list)))) + +(defn get-offsets [tab-id from-id to-id] + (let [tab (get-tab-index-by-id tab-id) + from (get-tab-index-by-id from-id) + to (get-tab-index-by-id to-id)] + (if (or (= tab from) (= tab to)) + (cond + (or (nil? from) (= from to)) {:from 0} + (< from to) (if (= tab to) + {:from window-width, :to 0} + {:from 0, :to (- window-width)}) + (< to from) (if (= tab to) + {:from (- window-width), :to 0} + {:from 0, :to window-width})) + {:from (- window-width)}))) + +(defn tab-view-container [tab-id content] + (let [cur-tab-id (subscribe [:get :view-id]) + prev-tab-id (subscribe [:get :prev-tab-view-id]) + offsets (reaction (get-offsets tab-id @prev-tab-id @cur-tab-id)) + anim-value (anim/create-value (- window-width)) + context {:offsets offsets + :val anim-value + :tab-id tab-id + :to-tab-id cur-tab-id} + on-update (animation-logic context)] + (r/create-class + {:component-did-mount + on-update + :component-did-update + on-update + :reagent-render + (fn [tab-id content] + @offsets + [animated-view {:style (st/tab-view-container anim-value)} + content])}))) + +(defn tab-view [{:keys [view-id screen]}] + ^{:key view-id} + [tab-view-container view-id [screen]]) (defview main-tabs [] - [view-id [:get :view-id]] - [view common-st/flex - (doall (map #(tab-view view-id %1) tab-list)) - [tabs {:selected-view-id view-id - :tab-list tab-list}]]) + [view-id [:get :view-id] + tab-animation? [:get :prev-tab-view-id]] + [view {:style common-st/flex} + [view {:style common-st/flex + :pointerEvents (if tab-animation? :none :auto)} + (doall (map #(tab-view %) tab-list))] + [tabs {:selected-view-id view-id + :tab-list tab-list}]]) diff --git a/src/status_im/components/styles.cljs b/src/status_im/components/styles.cljs index 70e907c631..a1176d46bf 100644 --- a/src/status_im/components/styles.cljs +++ b/src/status_im/components/styles.cljs @@ -32,7 +32,7 @@ (def toolbar-height 56) (def flex - {:style {:flex 1}}) + {:flex 1}) (def hamburger-icon {:width 16 diff --git a/src/status_im/components/tabs/styles.cljs b/src/status_im/components/tabs/styles.cljs index f3571cf3c8..45af60e439 100644 --- a/src/status_im/components/tabs/styles.cljs +++ b/src/status_im/components/tabs/styles.cljs @@ -54,17 +54,11 @@ :justifyContent :center :alignItems :center}) -(def show-tab - {:flex 1 - :pointerEvents :auto - :position :absolute - :top 0 - :left 0 - :right 0 - :bottom tab-height}) - -(def hide-tab - {:opacity 0 - :pointerEvents :none - :overflow :hidden}) - +(defn tab-view-container [offset-x] + {:flex 1 + :position :absolute + :top 0 + :left 0 + :right 0 + :bottom tab-height + :transform [{:translateX offset-x}]}) diff --git a/src/status_im/components/tabs/tab.cljs b/src/status_im/components/tabs/tab.cljs index e2f56af6b5..9ee44dcc44 100644 --- a/src/status_im/components/tabs/tab.cljs +++ b/src/status_im/components/tabs/tab.cljs @@ -10,9 +10,9 @@ [status-im.components.tabs.styles :as st])) (defview tab [{:keys [view-id title icon selected-view-id]}] - [touchable-highlight {:style st/tab - :onPress #(dispatch [:navigate-to - view-id])} + [touchable-highlight {:style st/tab + :disabled (= view-id selected-view-id) + :onPress #(dispatch [:navigate-to-tab view-id])} [view {:style st/tab-container} [image {:source {:uri icon} :style st/tab-icon}] diff --git a/src/status_im/handlers.cljs b/src/status_im/handlers.cljs index 5d43f80031..ebf835a9c3 100644 --- a/src/status_im/handlers.cljs +++ b/src/status_im/handlers.cljs @@ -50,6 +50,10 @@ debug set-in) +(register-handler :set-animation + (fn [db [_ k v]] + (assoc-in db [:animations k] v))) + (register-handler :initialize-db (fn [_ _] (assoc app-db diff --git a/src/status_im/models/messages.cljs b/src/status_im/models/messages.cljs index 5c8c17fa5f..65de1d2476 100644 --- a/src/status_im/models/messages.cljs +++ b/src/status_im/models/messages.cljs @@ -48,9 +48,10 @@ (defn get-messages [chat-id] (->> (-> (r/get-by-field :msgs :chat-id chat-id) - (r/sorted :timestamp :asc) + (r/sorted :timestamp :desc) (r/collection->map)) (into '()) + reverse (map (fn [{:keys [content-type] :as message}] (if (command-type? content-type) (update message :content str-to-map) diff --git a/src/status_im/navigation/handlers.cljs b/src/status_im/navigation/handlers.cljs index ac5bd9ec12..aa9c1eee32 100644 --- a/src/status_im/navigation/handlers.cljs +++ b/src/status_im/navigation/handlers.cljs @@ -43,6 +43,18 @@ (assoc :view-id view-id) (assoc :navigation-stack navigation-stack')))))) +(register-handler :navigate-to-tab + (enrich preload-data!) + (fn [db [_ view-id]] + (-> db + (assoc :prev-tab-view-id (:view-id db)) + (replace-view view-id)))) + +(register-handler :on-navigated-to-tab + (enrich preload-data!) + (fn [db [_]] + (assoc db :prev-tab-view-id nil))) + (register-handler :show-group-new (debug (fn [db _] diff --git a/src/status_im/subs.cljs b/src/status_im/subs.cljs index 972c57887c..f31dc4ebe2 100644 --- a/src/status_im/subs.cljs +++ b/src/status_im/subs.cljs @@ -15,3 +15,7 @@ (register-sub :get-in (fn [db [_ path]] (reaction (get-in @db path)))) + +(register-sub :animations + (fn [db [_ k]] + (reaction (get-in @db [:animations k])))) diff --git a/src/status_im/utils/listview.cljs b/src/status_im/utils/listview.cljs index dcd63da8bd..d9ab791710 100644 --- a/src/status_im/utils/listview.cljs +++ b/src/status_im/utils/listview.cljs @@ -7,3 +7,12 @@ (defn to-datasource [items] (clone-with-rows (data-source {:rowHasChanged not=}) items)) + +(defn clone-with-rows-inverted [ds rows] + (let [rows (reduce (fn [ac el] (.push ac el) ac) + (clj->js []) (reverse rows)) + row-ids (.reverse (.map rows (fn [_ index] index)))] + (.cloneWithRows ds rows row-ids))) + +(defn to-datasource-inverted [items] + (clone-with-rows-inverted (data-source {:rowHasChanged not=}) items))