diff --git a/src/status_im/chat/handlers.cljs b/src/status_im/chat/handlers.cljs index d25304749d..abcf1e4393 100644 --- a/src/status_im/chat/handlers.cljs +++ b/src/status_im/chat/handlers.cljs @@ -51,7 +51,7 @@ (register-handler :start-cancel-command (fn [db _] (if (commands/get-chat-command-to-msg-id db) - (dispatch [:animate-cancel-command #(dispatch [:cancel-command])]) + (dispatch [:animate-cancel-command]) (dispatch [:cancel-command #(dispatch [:cancel-command])])) db)) diff --git a/src/status_im/chat/handlers/animation.cljs b/src/status_im/chat/handlers/animation.cljs index 8dddfaa967..900bfb9a19 100644 --- a/src/status_im/chat/handlers/animation.cljs +++ b/src/status_im/chat/handlers/animation.cljs @@ -1,6 +1,5 @@ (ns status-im.chat.handlers.animation (:require [re-frame.core :refer [register-handler after dispatch]] - [status-im.components.animation :as anim] [status-im.components.drag-drop :as drag] [status-im.models.commands :as commands] [status-im.handlers.content-suggestions :refer [get-content-suggestions]] @@ -15,56 +14,30 @@ (fn [db _] (assoc-in db [:animations :commands-input-is-switching?] false))) -(defn animate-cancel-command! [{{:keys [response-height-anim-value - message-input-buttons-scale - message-input-offset - messages-offset-anim-value]} :animations} - [_ on-animation-stop]] - (let [height-to-value zero-height] - (anim/start (anim/spring response-height-anim-value {:toValue height-to-value}) - (fn [] - (dispatch [:finish-animate-cancel-command]) - (on-animation-stop))) - (anim/start (anim/timing message-input-buttons-scale {:toValue 1 - :duration response-input-hiding-duration})) - (anim/start (anim/timing message-input-offset {:toValue 0 - :duration response-input-hiding-duration})) - (anim/start (anim/spring messages-offset-anim-value {:toValue 0})))) - (register-handler :animate-cancel-command - (after animate-cancel-command!) (fn [db _] (let [hiding? (get-in db [:animations :commands-input-is-switching?])] (if-not hiding? - (assoc-in db [:animations :commands-input-is-switching?] true) + (-> db + (assoc-in [:animations :commands-input-is-switching?] true) + (assoc-in [:animations :message-input-buttons-scale] 1) + (assoc-in [:animations :message-input-offset] 0) + (assoc-in [:animations :to-response-height] zero-height) + (assoc-in [:animations :messages-offset] 0)) db)))) (register-handler :finish-animate-response-resize (fn [db _] - (let [fixed (get-in db [:animations :response-height-fixed])] + (let [fixed (get-in db [:animations :to-response-height])] (-> db - (assoc-in [:animations :response-height] fixed) + (assoc-in [:animations :response-height-current] fixed) (assoc-in [:animations :response-resize?] false))))) (register-handler :set-response-height (fn [db [_ value]] - (assoc-in db [:animations :response-height] value))) - -(defn animate-response-resize! [{{height-anim-value :response-height-anim-value - from :response-height - to :response-height-fixed} :animations}] - (anim/remove-all-listeners height-anim-value) - (anim/set-value height-anim-value from) - (anim/add-listener height-anim-value - (fn [val] - (dispatch [:set-response-height (anim/value val)]))) - (anim/start (anim/spring height-anim-value {:toValue to}) - (fn [] - (anim/remove-all-listeners height-anim-value) - (dispatch [:finish-animate-response-resize])))) + (assoc-in db [:animations :response-height-current] value))) (register-handler :animate-response-resize - (after animate-response-resize!) (fn [db _] (assoc-in db [:animations :response-resize?] true))) @@ -80,30 +53,21 @@ (min response-height-normal (+ suggestions-height request-info-height))))) (defn update-response-height [db] - (assoc-in db [:animations :response-height-fixed] (get-response-height db))) + (assoc-in db [:animations :to-response-height] (get-response-height db))) -(register-handler :finish-show-response! +(register-handler :finish-show-response (fn [db _] (assoc-in db [:animations :commands-input-is-switching?] false))) -(defn animate-show-response! [{{scale-anim-value :message-input-buttons-scale - input-offset-anim-value :message-input-offset - messages-offset-anim-value :messages-offset-anim-value} :animations}] - (let [to-value 0.1] - (anim/start (anim/timing scale-anim-value {:toValue to-value - :duration response-input-hiding-duration}) - #(dispatch [:finish-show-response!])) - (anim/start (anim/timing input-offset-anim-value {:toValue -40 - :duration response-input-hiding-duration})) - (anim/start (anim/spring messages-offset-anim-value {:toValue request-info-height})))) - (register-handler :animate-show-response - (after animate-show-response!) (fn [db _] (dispatch [:animate-response-resize]) (-> db (assoc-in [:animations :commands-input-is-switching?] true) - (assoc-in [:animations :response-height] zero-height) + (assoc-in [:animations :response-height-current] zero-height) + (assoc-in [:animations :message-input-buttons-scale] 0.1) + (assoc-in [:animations :message-input-offset] -40) + (assoc-in [:animations :messages-offset] request-info-height) (update-response-height)))) (register-handler :set-response-max-height @@ -112,12 +76,12 @@ (register-handler :on-drag-response (fn [db [_ dy]] - (let [fixed (get-in db [:animations :response-height-fixed])] - (assoc-in db [:animations :response-height] (- fixed dy))))) + (let [fixed (get-in db [:animations :to-response-height])] + (assoc-in db [:animations :response-height-current] (- fixed dy))))) (register-handler :fix-response-height (fn [db _] - (let [current (get-in db [:animations :response-height]) + (let [current (get-in db [:animations :response-height-current]) normal-height response-height-normal max-height (get-in db [:animations :response-height-max]) delta (/ normal-height 2) @@ -126,7 +90,7 @@ (<= current (+ zero-height normal-height delta)) (get-response-height db) :else max-height)] (dispatch [:animate-response-resize]) - (assoc-in db [:animations :response-height-fixed] new-fixed)))) + (assoc-in db [:animations :to-response-height] new-fixed)))) (defn create-response-pan-responder [] (drag/create-pan-responder diff --git a/src/status_im/chat/screen.cljs b/src/status_im/chat/screen.cljs index 10e5bd3754..7b921803bc 100644 --- a/src/status_im/chat/screen.cljs +++ b/src/status_im/chat/screen.cljs @@ -22,7 +22,9 @@ [status-im.chat.views.suggestions :refer [suggestions-view]] [status-im.chat.views.response :refer [response-view]] [status-im.chat.views.new-message :refer [chat-message-new]] - [status-im.i18n :refer [label label-pluralize]])) + [status-im.i18n :refer [label label-pluralize]] + [status-im.components.animation :as anim] + [reagent.core :as r])) (defn contacts-by-identity [contacts] @@ -217,17 +219,41 @@ :custom-action [toolbar-action]}]))) (defview messages-view [group-chat] - [messages [:chat :messages] - contacts [:chat :contacts] - messages-offset [:get-in [:animations :messages-offset-anim-value]]] - (let [contacts' (contacts-by-identity contacts)] - [animated-view {:style (st/messages-container messages-offset)} - [list-view {:renderRow (message-row contacts' group-chat) - :renderScrollComponent #(invertible-scroll-view (js->clj %)) - :onEndReached #(dispatch [:load-more-messages]) - :enableEmptySections true - :keyboardShouldPersistTaps true - :dataSource (to-datasource messages)}]])) + [messages [:chat :messages] + contacts [:chat :contacts]] + (let [contacts' (contacts-by-identity contacts)] + [list-view {:renderRow (message-row contacts' group-chat) + :renderScrollComponent #(invertible-scroll-view (js->clj %)) + :onEndReached #(dispatch [:load-more-messages]) + :enableEmptySections true + :keyboardShouldPersistTaps true + :dataSource (to-datasource messages)}])) + +(defn messages-container-animation-logic [{:keys [to-value val]}] + (fn [_] + (let [to-value @to-value] + (anim/start (anim/spring val {:toValue to-value}) + (fn [arg] + (when (.-finished arg) + (dispatch [:set-in [:animations ::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]]) + messages-offset (anim/create-value (or @cur-messages-offset 0)) + context {:to-value to-messages-offset + :val messages-offset} + on-update (messages-container-animation-logic context)] + (r/create-class + {:component-did-mount + on-update + :component-did-update + on-update + :reagent-render + (fn [messages] + @to-messages-offset + [animated-view {:style (st/messages-container messages-offset)} + messages])}))) (defview chat [] [group-chat [:chat :group-chat] @@ -236,7 +262,8 @@ to-msg-id [:get-chat-command-to-msg-id]] [view st/chat-view [chat-toolbar] - [messages-view group-chat] + [messages-container + [messages-view group-chat]] (when group-chat [typing-all]) (cond (and command to-msg-id) [response-view] diff --git a/src/status_im/chat/views/plain_input.cljs b/src/status_im/chat/views/plain_input.cljs index 9c03cf12a9..b92c7f6521 100644 --- a/src/status_im/chat/views/plain_input.cljs +++ b/src/status_im/chat/views/plain_input.cljs @@ -1,17 +1,20 @@ (ns status-im.chat.views.plain-input (:require-macros [status-im.utils.views :refer [defview]]) (:require [re-frame.core :refer [subscribe dispatch]] + [reagent.core :as r] [status-im.components.react :refer [view animated-view icon touchable-highlight text-input dismiss-keyboard!]] + [status-im.components.animation :as anim] [status-im.chat.views.command :as command] [status-im.chat.views.response :as response] [status-im.chat.styles.plain-input :as st] [status-im.chat.styles.input :as st-command] - [status-im.chat.styles.response :as st-response])) + [status-im.chat.styles.response :as st-response] + [status-im.constants :refer [response-input-hiding-duration]])) (defn set-input-message [message] (dispatch [:set-chat-input-text message])) @@ -30,29 +33,95 @@ (when (message-valid? staged-commands message) (send dismiss-keyboard))) -(defview commands-button [animation?] - [typing-command? [:typing-command?] - buttons-scale [:get-in [:animations :message-input-buttons-scale]]] - [touchable-highlight {:disabled animation? - :on-press #(dispatch [:switch-command-suggestions]) - :style st/message-input-button-touchable} - [animated-view {:style (st/message-input-button buttons-scale)} - (if typing-command? - [icon :close-gray st/close-icon] - [icon :list st/list-icon])]]) +(defn commands-button-animation-logic [{:keys [to-value val]}] + (fn [_] + (let [to-scale @to-value + minimum 0.1 + scale (cond (< 1 to-scale) 1 + (< to-scale minimum) minimum + :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])))))))) -(defview smile-button [animation?] - [buttons-scale [:get-in [:animations :message-input-buttons-scale]]] - [touchable-highlight {:disabled animation? - :on-press #(dispatch [:switch-command-suggestions]) - :style st/message-input-button-touchable} - [animated-view {:style (st/message-input-button buttons-scale)} - [icon :smile st/smile-icon]]]) +(defn commands-button [animation?] + (let [typing-command? (subscribe [:typing-command?]) + to-scale (subscribe [:get-in [:animations :message-input-buttons-scale]]) + cur-scale (subscribe [:get-in [: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)] + (r/create-class + {:component-did-mount + on-update + :component-did-update + on-update + :reagent-render + (fn [animation?] + (let [typing-command? @typing-command?] + @to-scale + [touchable-highlight {:disabled animation? + :on-press #(dispatch [:switch-command-suggestions]) + :style st/message-input-button-touchable} + [animated-view {:style (st/message-input-button buttons-scale)} + (if typing-command? + [icon :close-gray st/close-icon] + [icon :list st/list-icon])]]))}))) -(defview message-input-container [input] - [message-input-offset [:get-in [:animations :message-input-offset]]] - [animated-view {:style (st/message-input-container message-input-offset)} - input]) +(defn smile-button [animation?] + (let [to-scale (subscribe [:get-in [:animations :message-input-buttons-scale]]) + cur-scale (subscribe [:get-in [: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)] + (r/create-class + {:component-did-mount + on-update + :component-did-update + on-update + :reagent-render + (fn [animation?] + @to-scale + [touchable-highlight {:disabled animation? + :on-press (fn [] + ;; TODO emoticons: not implemented + ) + :style st/message-input-button-touchable} + [animated-view {:style (st/message-input-button buttons-scale)} + [icon :smile st/smile-icon]]])}))) + +(defn message-input-container-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]))))))) + +(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]]) + 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)] + (r/create-class + {:component-did-mount + on-update + :component-did-update + on-update + :reagent-render + (fn [input] + @to-message-input-offset + [animated-view {:style (st/message-input-container message-input-offset)} + input])}))) (defview plain-message-input-view [{:keys [input-options validator]}] [input-message [:get-chat-input-text] diff --git a/src/status_im/chat/views/response.cljs b/src/status_im/chat/views/response.cljs index 489a08e07f..37bc12385a 100644 --- a/src/status_im/chat/views/response.cljs +++ b/src/status_im/chat/views/response.cljs @@ -1,6 +1,8 @@ (ns status-im.chat.views.response - (: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]] + [reagent.core :as r] [status-im.components.react :refer [view animated-view icon @@ -10,7 +12,9 @@ touchable-highlight]] [status-im.components.drag-drop :as drag] [status-im.chat.views.response-suggestions :refer [response-suggestions-view]] - [status-im.chat.styles.response :as st])) + [status-im.chat.styles.response :as st] + [status-im.chat.styles.plain-input :refer [input-height]] + [status-im.components.animation :as anim])) (defn drag-icon [] [view st/drag-container @@ -40,20 +44,54 @@ [view st/cancel-container [icon :close-white st/cancel-icon]]]]]) -(defview response-view [] - [pan-responder [:get-in [:animations :response-pan-responder]] - response-height [:get-in [:animations :response-height]] - anim-height [:get-in [:animations :response-height-anim-value]] - commands-input-is-switching? [:get-in [:animations :commands-input-is-switching?]] - response-resize? [:get-in [:animations :response-resize?]]] +(defn inner-container-animation-logic [{:keys [animation? to-value current-value val]}] + (fn [_] + (if @animation? + (let [to-value @to-value] + (anim/start (anim/spring val {:toValue to-value}) + (fn [arg] + (when (.-finished arg) + (dispatch [:set-in [:animations :response-height-current] to-value]) + (dispatch [:finish-animate-response-resize]) + (when (= to-value input-height) + (dispatch [:finish-animate-cancel-command]) + (dispatch [:cancel-command])))))) + (anim/set-value val @current-value)))) + +(defn inner-container [content] + (let [pan-responder (subscribe [:get-in [:animations :response-pan-responder]]) + 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]]) + 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 + :current-value cur-response-height + :val response-height} + on-update (inner-container-animation-logic context)] + (r/create-class + {:component-did-mount + on-update + :component-did-update + on-update + :reagent-render + (fn [content] + @to-response-height + [animated-view (merge (drag/pan-handlers @pan-responder) + {:style (st/response-view (if (or @commands-input-is-switching? @response-resize?) + response-height + (or @cur-response-height 0)))}) + content])}))) + +(defn response-view [] [view {:style st/container :onLayout (fn [event] (let [height (.. event -nativeEvent -layout -height)] (dispatch [:set-response-max-height height])))} - [animated-view (merge (drag/pan-handlers pan-responder) - {:style (st/response-view (if (or commands-input-is-switching? response-resize?) - anim-height - response-height))}) - [request-info] - [response-suggestions-view] - [view st/input-placeholder]]]) + [inner-container + [view + [request-info] + [response-suggestions-view] + [view st/input-placeholder]]]]) diff --git a/src/status_im/db.cljs b/src/status_im/db.cljs index 2f6a31549d..5fa2ad7f22 100644 --- a/src/status_im/db.cljs +++ b/src/status_im/db.cljs @@ -33,14 +33,13 @@ :current-tag nil :disable-group-creation false :animations {;; mutable data - :response-height nil - :response-height-fixed nil + :to-response-height nil + :response-height-current nil :response-pan-responder nil - :message-input-offset (anim/create-value 0) - :message-input-buttons-scale (anim/create-value 1) + :message-input-offset 0 + :message-input-buttons-scale 1 :commands-input-is-switching? false - :response-height-anim-value (anim/create-value 0) - :messages-offset-anim-value (anim/create-value 0) + :messages-offset 0 :response-resize? false}}) (def protocol-initialized-path [:protocol-initialized])