Merge pull request #140 from status-im/chat-animation

Chat animation
This commit is contained in:
Jarrad 2016-06-22 20:43:55 +02:00 committed by GitHub
commit 31d7161766
22 changed files with 305 additions and 325 deletions

View File

@ -1,7 +1,7 @@
{ {
"name": "StatusIm", "name": "StatusIm",
"interface": "reagent", "interface": "reagent",
"androidHost": "10.0.3.2", "androidHost": "localhost",
"modules": [ "modules": [
"react-native-contacts", "react-native-contacts",
"react-native-invertible-scroll-view", "react-native-invertible-scroll-view",
@ -19,6 +19,7 @@
"react-native-android-sms-listener", "react-native-android-sms-listener",
"react-native-camera", "react-native-camera",
"react-native-qrcode", "react-native-qrcode",
"react-native-orientation",
"identicon.js" "identicon.js"
], ],
"imageDirs": [ "imageDirs": [
@ -29,4 +30,4 @@
"dev": "env/dev", "dev": "env/dev",
"prod": "env/prod" "prod": "env/prod"
} }
} }

View File

@ -131,6 +131,7 @@ dependencies {
compile project(':react-native-linear-gradient') compile project(':react-native-linear-gradient')
compile project(':ReactNativeAndroidSmsListener') compile project(':ReactNativeAndroidSmsListener')
compile project(':react-native-camera') compile project(':react-native-camera')
compile project(':react-native-orientation')
// compile(name:'geth', ext:'aar') // compile(name:'geth', ext:'aar')
compile(group: 'status-im', name: 'android-geth', version: '1.4.0-201604110816-a97a114', ext: 'aar') compile(group: 'status-im', name: 'android-geth', version: '1.4.0-201604110816-a97a114', ext: 'aar')

View File

@ -23,6 +23,7 @@ import android.content.Context;
import com.bitgo.randombytes.RandomBytesPackage; import com.bitgo.randombytes.RandomBytesPackage;
import com.BV.LinearGradient.LinearGradientPackage; import com.BV.LinearGradient.LinearGradientPackage;
import com.centaurwarchief.smslistener.SmsListener; import com.centaurwarchief.smslistener.SmsListener;
import com.github.yamill.orientation.OrientationPackage;
import android.util.Log; import android.util.Log;
@ -35,7 +36,8 @@ import java.io.File;
import com.lwansbrough.RCTCamera.*; import com.lwansbrough.RCTCamera.*;
import com.i18n.reactnativei18n.ReactNativeI18n; import com.i18n.reactnativei18n.ReactNativeI18n;
import io.realm.react.RealmReactPackage; import io.realm.react.RealmReactPackage;
import android.content.Intent;
import android.content.res.Configuration;
public class MainActivity extends ReactActivity { public class MainActivity extends ReactActivity {
@ -181,7 +183,16 @@ public class MainActivity extends ReactActivity {
new RandomBytesPackage(), new RandomBytesPackage(),
new LinearGradientPackage(), new LinearGradientPackage(),
new RCTCameraPackage(), new RCTCameraPackage(),
new SmsListener(this) new SmsListener(this),
new OrientationPackage(this)
); );
} }
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
Intent intent = new Intent("onConfigurationChanged");
intent.putExtra("newConfig", newConfig);
this.sendBroadcast(intent);
}
} }

View File

@ -20,3 +20,5 @@ include ':ReactNativeAndroidSmsListener'
project(':ReactNativeAndroidSmsListener').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-android-sms-listener/android') project(':ReactNativeAndroidSmsListener').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-android-sms-listener/android')
include ':react-native-camera' include ':react-native-camera'
project(':react-native-camera').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-camera/android') project(':react-native-camera').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-camera/android')
include ':react-native-orientation', ':app'
project(':react-native-orientation').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-orientation/android')

View File

@ -22,6 +22,7 @@
"react-native-qrcode": "^0.2.2", "react-native-qrcode": "^0.2.2",
"react-native-randombytes": "^2.1.0", "react-native-randombytes": "^2.1.0",
"react-native-vector-icons": "^1.3.4", "react-native-vector-icons": "^1.3.4",
"react-native-orientation": "^1.17.0",
"realm": "^0.11.1" "realm": "^0.11.1"
} }
} }

View File

@ -5,7 +5,8 @@
[re-frame.core :refer [subscribe dispatch dispatch-sync]] [re-frame.core :refer [subscribe dispatch dispatch-sync]]
[status-im.handlers] [status-im.handlers]
[status-im.subs] [status-im.subs]
[status-im.components.react :refer [navigator app-registry]] [status-im.components.react :refer [navigator app-registry device-event-emitter
orientation]]
[status-im.components.main-tabs :refer [main-tabs]] [status-im.components.main-tabs :refer [main-tabs]]
[status-im.contacts.views.contact-list :refer [contact-list] ] [status-im.contacts.views.contact-list :refer [contact-list] ]
[status-im.contacts.views.new-contact :refer [new-contact]] [status-im.contacts.views.new-contact :refer [new-contact]]
@ -33,25 +34,45 @@
true)))] true)))]
(add-event-listener "hardwareBackPress" new-listener))) (add-event-listener "hardwareBackPress" new-listener)))
(defn orientation->keyword [o]
(keyword (.toLowerCase o)))
(defn app-root [] (defn app-root []
(let [signed-up (subscribe [:get :signed-up]) (let [signed-up (subscribe [:get :signed-up])
view-id (subscribe [:get :view-id])] view-id (subscribe [:get :view-id])]
(fn [] (r/create-class
(case (if @signed-up @view-id :chat) {:component-will-mount
:discovery [main-tabs] (fn []
:discovery-tag [discovery-tag] (let [o (orientation->keyword (.getInitialOrientation orientation))]
:add-participants [new-participants] (dispatch [:set :orientation o]))
:remove-participants [remove-participants] (.addOrientationListener
:chat-list [main-tabs] orientation
:new-group [new-group] #(dispatch [:set :orientation (orientation->keyword %)]))
:group-settings [group-settings] (.addListener device-event-emitter
:contact-list [main-tabs] "keyboardDidShow"
:group-contacts [contact-list] (fn [e]
:new-contact [new-contact] (let [h (.. e -endCoordinates -height)]
:qr-scanner [qr-scanner] (dispatch [:set :keyboard-height h]))))
:chat [chat] (.addListener device-event-emitter
:profile [profile] "keyboardDidHide"
:my-profile [my-profile])))) #(dispatch [:set :keyboard-height 0])))
:render
(fn []
(case (if @signed-up @view-id :chat)
:discovery [main-tabs]
:discovery-tag [discovery-tag]
:add-participants [new-participants]
:remove-participants [remove-participants]
:chat-list [main-tabs]
:new-group [new-group]
:group-settings [group-settings]
:contact-list [main-tabs]
:group-contacts [contact-list]
:new-contact [new-contact]
:qr-scanner [qr-scanner]
:chat [chat]
:profile [profile]
:my-profile [my-profile]))})))
(defn init [] (defn init []
(dispatch-sync [:initialize-db]) (dispatch-sync [:initialize-db])

View File

@ -48,16 +48,9 @@
(register-handler :start-cancel-command (register-handler :start-cancel-command
(u/side-effect! (u/side-effect!
(fn [db _] (fn [db _]
(if (commands/get-chat-command-to-msg-id db) (dispatch [:animate-cancel-command]))))
(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 (register-handler :set-chat-command-content
(after animate-set-chat-command-content)
(fn [{:keys [current-chat-id] :as db} [_ content]] (fn [{:keys [current-chat-id] :as db} [_ content]]
(as-> db db (as-> db db
(commands/set-chat-command-content db content) (commands/set-chat-command-content db content)
@ -78,7 +71,9 @@
command-info {:command command command-info {:command command
:content content :content content
:handler (:handler command)}] :handler (:handler command)}]
(commands/stage-command db command-info)))) (-> db
(assoc-in [:chats current-chat-id :command-input :command] nil)
(commands/stage-command command-info)))))
(register-handler :set-message-input [] (register-handler :set-message-input []
(fn [db [_ input]] (fn [db [_ input]]
@ -91,7 +86,8 @@
(.blur message-input))))) (.blur message-input)))))
(register-handler :set-response-chat-command (register-handler :set-response-chat-command
(after #(dispatch [:animate-show-response])) [(after #(dispatch [:command-edit-mode]))
(after #(dispatch [:animate-show-response]))]
(fn [db [_ to-msg-id command-key]] (fn [db [_ to-msg-id command-key]]
(commands/set-response-chat-command db to-msg-id command-key))) (commands/set-response-chat-command db to-msg-id command-key)))
@ -253,8 +249,9 @@
(commands/unstage-command db staged-command))) (commands/unstage-command db staged-command)))
(register-handler :set-chat-command (register-handler :set-chat-command
[(after #(dispatch [:command-edit-mode]))
(after #(dispatch [:animate-show-response]))]
(fn [db [_ command-key]] (fn [db [_ command-key]]
;; todo what is going on there?!
(commands/set-chat-command db command-key))) (commands/set-chat-command db command-key)))
(register-handler :init-console-chat (register-handler :init-console-chat
@ -431,3 +428,13 @@
;((after leaving-message!)) ;((after leaving-message!))
((after delete-messages!)) ((after delete-messages!))
((after delete-chat!)))) ((after delete-chat!))))
(defn edit-mode-handler [mode]
(fn [{:keys [current-chat-id] :as db} _]
(assoc-in db [:edit-mode current-chat-id] mode)))
(register-handler :command-edit-mode
(edit-mode-handler :command))
(register-handler :text-edit-mode
(edit-mode-handler :text))

View File

@ -15,34 +15,12 @@
([name middleware handler] ([name middleware handler]
(register-handler name [(path :animations) middleware] handler))) (register-handler name [(path :animations) middleware] handler)))
(animation-handler :finish-animate-cancel-command
(fn [db _]
(assoc db :commands-input-is-switching? false)))
(animation-handler :animate-cancel-command (animation-handler :animate-cancel-command
(after #(dispatch [:text-edit-mode]))
(fn [db _] (fn [db _]
(if-not (:commands-input-is-switching? db) (assoc db
(assoc db :to-response-height zero-height
:commands-input-is-switching? true :messages-offset 0)))
:message-input-buttons-scale 1
:message-input-offset 0
:to-response-height zero-height
:messages-offset 0)
db)))
(animation-handler :finish-animate-response-resize
(fn [db _]
(let [fixed (:to-response-height db)]
(assoc db :response-height-current fixed
:response-resize? false))))
(animation-handler :set-response-height
(fn [db [_ value]]
(assoc db :response-height-current value)))
(animation-handler :animate-response-resize
(fn [db _]
(assoc db :response-resize? true)))
(defn get-response-height [db] (defn get-response-height [db]
(let [command (commands/get-chat-command db) (let [command (commands/get-chat-command db)
@ -58,18 +36,10 @@
(defn update-response-height [db] (defn update-response-height [db]
(assoc-in db [:animations :to-response-height] (get-response-height db))) (assoc-in db [:animations :to-response-height] (get-response-height db)))
(animation-handler :finish-show-response
(fn [db _]
(assoc db :commands-input-is-switching? false)))
(register-handler :animate-show-response (register-handler :animate-show-response
(after #(dispatch [:animate-response-resize])) (after #(dispatch [:command-edit-mode]))
(fn [db _] (fn [db _]
(-> db (-> db
(assoc-in [:animations :commands-input-is-switching?] true)
(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) (assoc-in [:animations :messages-offset] request-info-height)
(update-response-height)))) (update-response-height))))
@ -79,33 +49,31 @@
(if (not= height prev-height) (if (not= height prev-height)
(let [db (assoc db :response-height-max height)] (let [db (assoc db :response-height-max height)]
(if (= prev-height (:to-response-height db)) (if (= prev-height (:to-response-height db))
(assoc db :to-response-height height (assoc db :to-response-height height)
:response-height-current height)
db)) db))
db)))) db))))
(animation-handler :on-drag-response
(fn [db [_ dy]]
(let [fixed (:to-response-height db)]
(assoc db :response-height-current (- fixed dy)
:response-resize? false))))
(register-handler :fix-response-height (register-handler :fix-response-height
(fn [db _] (fn [db [_ vy current]]
(if (and (commands/get-chat-command-to-msg-id db) (let [max-height (get-in db [:animations :response-height-max])
(not (get-in db [:animations :commands-input-is-switching?]))) ;; todo magic value
(let [current (get-in db [:animations :response-height-current]) middle 270
normal-height response-height-normal moving-down? (pos? vy)
command (commands/get-chat-command db) moving-up? (not moving-down?)
text (commands/get-chat-command-content db) under-middle-position? (<= current middle)
suggestions (get-content-suggestions command text) over-middle-position? (not under-middle-position?)
max-height (get-in db [:animations :response-height-max]) min-height (+ zero-height request-info-height)
delta (/ normal-height 2) new-fixed (cond (and under-middle-position? moving-down?)
new-fixed (cond min-height
(or (<= current (+ zero-height delta))
(empty? suggestions)) (+ zero-height request-info-height) (and under-middle-position? moving-up?)
(<= current (+ zero-height normal-height delta)) (get-response-height db) middle
:else max-height)]
(dispatch [:animate-response-resize]) (and over-middle-position? moving-down?)
(assoc-in db [:animations :to-response-height] new-fixed)) middle
db)))
(and over-middle-position? moving-up?)
max-height)]
(-> db
(assoc-in [:animations :to-response-height] new-fixed)
(update-in [:animations :response-height-changed] inc)))))

View File

@ -259,6 +259,7 @@
[group-chat [:chat :group-chat] [group-chat [:chat :group-chat]
show-actions-atom [:show-actions] show-actions-atom [:show-actions]
command [:get-chat-command] command [:get-chat-command]
command? [:command?]
to-msg-id [:get-chat-command-to-msg-id]] to-msg-id [:get-chat-command-to-msg-id]]
[view {:style st/chat-view [view {:style st/chat-view
:onLayout (fn [event] :onLayout (fn [event]
@ -268,9 +269,7 @@
[messages-container [messages-container
[messages-view group-chat]] [messages-view group-chat]]
(when group-chat [typing-all]) (when group-chat [typing-all])
(cond [response-view]
(and command to-msg-id) [response-view] (when-not command? [suggestions-view])
command [content-suggestions-view]
:else [suggestions-view])
[chat-message-new] [chat-message-new]
(when show-actions-atom [actions-view])]) (when show-actions-atom [actions-view])])

View File

@ -4,10 +4,9 @@
(def input-height 56) (def input-height 56)
(defn message-input-container [offset] (def message-input-container
{:flex 1 {:flex 1
:transform [{:translateX offset}] :marginRight 0})
:marginRight offset})
(def input-container (def input-container
{:flexDirection :column}) {:flexDirection :column})

View File

@ -2,8 +2,8 @@
(:require [status-im.components.styles :refer [font (:require [status-im.components.styles :refer [font
text2-color]])) text2-color]]))
(def message-input-button-touchable (defn message-input-button-touchable [w]
{:width 56 {:width w
:height 56 :height 56
:alignItems :center :alignItems :center
:justifyContent :center}) :justifyContent :center})

View File

@ -87,7 +87,6 @@
(def command-input (def command-input
{:flex 1 {:flex 1
:marginLeft 56
:marginRight 16 :marginRight 16
:marginTop -2 :marginTop -2
:padding 0 :padding 0

View File

@ -1,13 +1,15 @@
(ns status-im.chat.subs (ns status-im.chat.subs
(:require-macros [reagent.ratom :refer [reaction]]) (:require-macros [reagent.ratom :refer [reaction]])
(:require [re-frame.core :refer [register-sub dispatch]] (:require [re-frame.core :refer [register-sub dispatch subscribe]]
[status-im.db :as db] [status-im.db :as db]
;todo handlers in subs?... ;todo handlers in subs?...
[status-im.chat.suggestions :refer [status-im.chat.suggestions :refer
[get-suggestions typing-command?]] [get-suggestions typing-command?]]
[status-im.models.commands :as commands] [status-im.models.commands :as commands]
[status-im.constants :refer [response-suggesstion-resize-duration]] [status-im.constants :refer [response-suggesstion-resize-duration]]
[status-im.handlers.content-suggestions :refer [get-content-suggestions]])) [status-im.handlers.content-suggestions :refer [get-content-suggestions]]
[status-im.chat.views.plain-message :as plain-message]
[status-im.chat.views.command :as command]))
(register-sub :chat-properties (register-sub :chat-properties
(fn [db [_ properties]] (fn [db [_ properties]]
@ -63,6 +65,18 @@
(get-in @db) (get-in @db)
(reaction)))) (reaction))))
(register-sub :valid-plain-message?
(fn [_ _]
(let [input-message (subscribe [:get-chat-input-text])
staged-commands (subscribe [:get-chat-staged-commands])]
(reaction
(plain-message/message-valid? @staged-commands @input-message)))))
(register-sub :valid-command?
(fn [_ [_ validator]]
(let [input (subscribe [:get-chat-command-content])]
(reaction (command/valid? @input validator)))))
(register-sub :get-chat-command (register-sub :get-chat-command
(fn [db _] (fn [db _]
(reaction (commands/get-chat-command @db)))) (reaction (commands/get-chat-command @db))))
@ -97,3 +111,9 @@
(let [command (reaction (commands/get-chat-command @db)) (let [command (reaction (commands/get-chat-command @db))
text (reaction (commands/get-chat-command-content @db))] text (reaction (commands/get-chat-command-content @db))]
(reaction (get-content-suggestions @command @text))))) (reaction (get-content-suggestions @command @text)))))
(register-sub :command?
(fn [db ]
(->> (get-in @db [:edit-mode (:current-chat-id @db)])
(= :command)
(reaction))))

View File

@ -31,9 +31,7 @@
[view (st/command-text-container command) [view (st/command-text-container command)
[text {:style st/command-text} (:text command)]]) [text {:style st/command-text} (:text command)]])
(defview cancel-button [] (defn cancel-button []
[commands-input-is-switching? [:animations :commands-input-is-switching?]] [touchable-highlight {:on-press cancel-command-input}
[touchable-highlight {:disabled commands-input-is-switching?
:on-press cancel-command-input}
[view st/cancel-container [view st/cancel-container
[icon :close-gray st/cancel-icon]]]) [icon :close-gray st/cancel-icon]]])

View File

@ -1,7 +1,6 @@
(ns status-im.chat.views.message-input (ns status-im.chat.views.message-input
(:require-macros [status-im.utils.views :refer [defview]]) (:require-macros [status-im.utils.views :refer [defview]])
(:require [re-frame.core :refer [subscribe dispatch]] (:require [re-frame.core :refer [subscribe]]
[reagent.core :as r]
[status-im.components.react :refer [view [status-im.components.react :refer [view
animated-view animated-view
icon icon
@ -10,113 +9,58 @@
[status-im.components.animation :as anim] [status-im.components.animation :as anim]
[status-im.chat.views.plain-message :as plain-message] [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.chat.views.response :as response]
[status-im.chat.styles.message-input :as st] [status-im.chat.styles.message-input :as st]
[status-im.chat.styles.plain-message :as st-message] [status-im.chat.styles.plain-message :as st-message]
[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]]))
(defview send-button [{:keys [on-press accessibility-label]}] (defn send-button [{:keys [on-press accessibility-label]}]
[commands-input-is-switching? [:animations :commands-input-is-switching?]] [touchable-highlight {:on-press on-press
[touchable-highlight {:disabled commands-input-is-switching?
:on-press on-press
:accessibility-label accessibility-label} :accessibility-label accessibility-label}
[view st/send-container [view st/send-container
[icon :send st/send-icon]]]) [icon :send st/send-icon]]])
(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-animation ::message-input-offset-current to-value])))))))
(defn message-input-container [input] (defn message-input-container [input]
(let [to-message-input-offset (subscribe [:animations :message-input-offset]) [view st/message-input-container input])
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 (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 message-input [input-options validator] (def plain-input-options
[input-message [:get-chat-input-text] {:style st-message/message-input
command [:get-chat-command] :onChangeText plain-message/set-input-message
to-msg-id [:get-chat-command-to-msg-id] :onSubmitEditing plain-message/send})
input-command [:get-chat-command-content]
staged-commands [:get-chat-staged-commands] (def command-input-options
typing-command? [:typing-command?] {:style st-response/command-input
commands-input-is-switching? [:animations :commands-input-is-switching?]] :onChangeText command/set-input-message
(let [response? (and command to-msg-id) :onSubmitEditing command/send-command})
message-input? (or (not command) commands-input-is-switching?)
animation? commands-input-is-switching?] (defview message-input [input-options]
[text-input (merge {:style (cond [command? [:command?]
message-input? st-message/message-input input-message [:get-chat-input-text]
response? st-response/command-input input-command [:get-chat-command-content]]
command st-command/command-input) [text-input (merge
:ref (fn [input] (if command?
(dispatch [:set-message-input input])) command-input-options
:autoFocus false plain-input-options)
:blurOnSubmit false {:autoFocus false
:onChangeText (fn [text] :blurOnSubmit false
(when-not animation? :accessibility-label :input}
((if message-input? input-options)
plain-message/set-input-message (if command? input-command input-message)])
command/set-input-message)
text)))
:onSubmitEditing #(when-not animation?
(if message-input?
(plain-message/try-send staged-commands
input-message)
(command/try-send input-command validator)))}
(when command
{:accessibility-label :command-input})
input-options)
(if message-input?
input-message
input-command)]))
(defview plain-message-input-view [{:keys [input-options validator]}] (defview plain-message-input-view [{:keys [input-options validator]}]
[input-message [:get-chat-input-text] [command? [:command?]
command [:get-chat-command]
to-msg-id [:get-chat-command-to-msg-id]
input-command [:get-chat-command-content] input-command [:get-chat-command-content]
staged-commands [:get-chat-staged-commands] valid-plain-message? [:valid-plain-message?]
typing-command? [:typing-command?] valid-command? [:valid-command? validator]]
commands-input-is-switching? [:animations :commands-input-is-switching?]] [view st/input-container
(let [response? (and command to-msg-id) [view st/input-view
message-input? (or (not command) commands-input-is-switching?)] [plain-message/commands-button]
[view st/input-container [message-input-container
[view st/input-view [message-input input-options validator]]
(if message-input? ;; TODO emoticons: not implemented
[plain-message/commands-button] [plain-message/smile-button]
(when (and command (not response?)) (when (if command? valid-command? valid-plain-message?)
[command/command-icon command response?])) (let [on-press (if command?
[message-input-container command/send-command
[message-input input-options validator]] plain-message/send)]
;; TODO emoticons: not implemented [send-button {:on-press on-press
(when message-input? :accessibility-label :send-message}]))]])
[plain-message/smile-button])
(if message-input?
(when (plain-message/message-valid? staged-commands input-message)
[send-button {:on-press #(plain-message/try-send staged-commands
input-message)
:accessibility-label :send-message}])
(if (command/valid? input-command validator)
[send-button {:on-press command/send-command
:accessibility-label :stage-command}]
(when-not response?
[command/cancel-button])))]]))

View File

@ -16,9 +16,11 @@
(for [command staged-commands] (for [command staged-commands]
^{:key command} [staged-command-view command])]) ^{:key command} [staged-command-view command])])
(defn show-input [command] (defview show-input []
[command [:get-chat-command]
command? [:command?]]
[plain-message-input-view [plain-message-input-view
(when command (when command?
(case (:command command) (case (:command command)
:phone {:input-options {:keyboardType :phone-pad} :phone {:input-options {:keyboardType :phone-pad}
:validator valid-mobile-number?} :validator valid-mobile-number?}
@ -29,9 +31,8 @@
(throw (js/Error. "Uknown command type"))))]) (throw (js/Error. "Uknown command type"))))])
(defview chat-message-new [] (defview chat-message-new []
[command [:get-chat-command] [staged-commands [:get-chat-staged-commands]]
staged-commands [:get-chat-staged-commands]]
[view st/new-message-container [view st/new-message-container
(when (and staged-commands (pos? (count staged-commands))) (when (and staged-commands (pos? (count staged-commands)))
[staged-commands-view staged-commands]) [staged-commands-view staged-commands])
[show-input command]]) [show-input]])

View File

@ -13,53 +13,37 @@
(defn set-input-message [message] (defn set-input-message [message]
(dispatch [:set-chat-input-text message])) (dispatch [:set-chat-input-text message]))
(defn send []
(dispatch [:send-chat-msg]))
(defn message-valid? [staged-commands message] (defn message-valid? [staged-commands message]
(or (and (pos? (count message)) (or (and (pos? (count message))
(not= "!" message)) (not= "!" message))
(pos? (count staged-commands)))) (pos? (count staged-commands))))
(defn try-send [staged-commands message] (defn button-animation-logic [{:keys [command? val]}]
(when (message-valid? staged-commands message)
(dispatch [:send-chat-msg])))
(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 [_] (fn [_]
(let [to-scale @to-value (let [to-scale (if @command? 0 1)]
minimum 0.1 (anim/start (anim/spring val {:toValue to-scale})))))
scale (cond (< 1 to-scale) 1
(< to-scale minimum) minimum (defn list-container [min]
:else to-scale)] (fn [{:keys [command? width]}]
(anim/start (anim/timing val {:toValue scale (let [n-width (if @command? min 56)
:duration response-input-hiding-duration}) delay (if @command? 100 0)]
(when callback (anim/start (anim/timing width {:toValue n-width
(fn [arg] :duration response-input-hiding-duration
(callback arg to-scale))))))) :delay delay})))))
(defn commands-button [] (defn commands-button []
(let [typing-command? (subscribe [:typing-command?]) (let [command? (subscribe [:command?])
message-input (subscribe [:get :message-input]) buttons-scale (anim/create-value (if @command? 1 0))
animation? (subscribe [:animations :commands-input-is-switching?]) container-width (anim/create-value (if @command? 20 56))
to-scale (subscribe [:animations :message-input-buttons-scale]) context {:command? command?
cur-scale (subscribe [:animations ::message-input-buttons-scale-current]) :val buttons-scale
buttons-scale (anim/create-value (or @cur-scale 1)) :width container-width}
anim-callback (commands-button-animation-callback message-input) on-update (fn [_]
context {:to-value to-scale ((button-animation-logic context))
:val buttons-scale ((list-container 20) context))]
:callback anim-callback}
on-update (button-animation-logic context)]
(r/create-class (r/create-class
{:component-did-mount {:component-did-mount
on-update on-update
@ -67,24 +51,30 @@
on-update on-update
:reagent-render :reagent-render
(fn [] (fn []
(let [typing-command? @typing-command?] [touchable-highlight {:on-press #(dispatch [:switch-command-suggestions])
@to-scale :disabled @command?}
[touchable-highlight {:disabled @animation? [animated-view {:style (st/message-input-button-touchable
:on-press #(dispatch [:switch-command-suggestions]) container-width)}
:style st/message-input-button-touchable} [animated-view {:style (st/message-input-button buttons-scale)}
[animated-view {:style (st/message-input-button buttons-scale)} [icon :list st/list-icon]]]])})))
(if typing-command?
[icon :close-gray st/close-icon] (defn smile-animation-logic [{:keys [command? val width]}]
[icon :list st/list-icon])]]))}))) (fn [_]
(let [to-scale (if @command? 0 1)]
(when-not @command? (anim/set-value width 56))
(anim/start (anim/spring val {:toValue to-scale})
(fn [e]
(when (and @command? (.-finished e))
(anim/set-value width 0.1)))))))
(defn smile-button [] (defn smile-button []
(let [animation? (subscribe [:animations :commands-input-is-switching?]) (let [command? (subscribe [:command?])
to-scale (subscribe [:animations :message-input-buttons-scale]) buttons-scale (anim/create-value (if @command? 1 0))
cur-scale (subscribe [:animations ::message-input-buttons-scale-current]) container-width (anim/create-value (if @command? 0.1 56))
buttons-scale (anim/create-value (or @cur-scale 1)) context {:command? command?
context {:to-value to-scale :val buttons-scale
:val buttons-scale} :width container-width}
on-update (button-animation-logic context)] on-update (smile-animation-logic context)]
(r/create-class (r/create-class
{:component-did-mount {:component-did-mount
on-update on-update
@ -92,11 +82,11 @@
on-update on-update
:reagent-render :reagent-render
(fn [] (fn []
@to-scale [touchable-highlight {:on-press (fn []
[touchable-highlight {:disabled @animation?
:on-press (fn []
;; TODO emoticons: not implemented ;; TODO emoticons: not implemented
) )
:style st/message-input-button-touchable} :disabled @command?}
[animated-view {:style (st/message-input-button buttons-scale)} [animated-view {:style (st/message-input-button-touchable
[icon :smile st/smile-icon]]])}))) container-width)}
[animated-view {:style (st/message-input-button buttons-scale)}
[icon :smile st/smile-icon]]]])})))

View File

@ -14,7 +14,8 @@
[status-im.chat.views.response-suggestions :refer [response-suggestions-view]] [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.message-input :refer [input-height]] [status-im.chat.styles.message-input :refer [input-height]]
[status-im.components.animation :as anim])) [status-im.components.animation :as anim]
[status-im.components.react :as react]))
(defn drag-icon [] (defn drag-icon []
[view st/drag-container [view st/drag-container
@ -33,17 +34,47 @@
;; TODO stub data: request message info ;; TODO stub data: request message info
"By ???, MMM 1st at HH:mm"]]) "By ???, MMM 1st at HH:mm"]])
(defn create-response-pan-responder [] ;; todo bad name. Ideas?
(drag/create-pan-responder (defn enough-dy [gesture]
{:on-move (fn [e gesture] (> (Math/abs (.-dy gesture)) 10))
(dispatch [:on-drag-response (.-dy gesture)]))
:on-release (fn [e gesture]
(dispatch [:fix-response-height]))}))
(defn request-info [] (defn on-move [response-height kb-height orientation]
(let [pan-responder (create-response-pan-responder) (fn [_ gesture]
command (subscribe [:get-chat-command])] (when (enough-dy gesture)
(fn [] (let [w (react/get-dimensions "window")
;; depending on orientation use height or width of screen
prop (if (= :portrait @orientation)
:height
:width)
;; subtract keyboard height to get "real height" of screen
;; then subtract gesture position to get suggestions height
;; todo maybe it is better to use margin-top instead height
;; it is not obvious
to-value (- (prop w) @kb-height (.-moveY gesture))]
(println to-value )
(anim/start
(anim/spring response-height {:toValue to-value}))))))
(defn on-release [response-height]
(fn [_ gesture]
(when (enough-dy gesture)
(dispatch [:fix-response-height
(.-vy gesture)
;; todo access to "private" property
;; better to find another way...
(.-_value response-height)]))))
(defn pan-responder [response-height kb-height orientation]
(drag/create-pan-responder
{:on-move (on-move response-height kb-height orientation)
:on-release (on-release response-height)}))
(defn request-info [response-height]
(let [orientation (subscribe [:get :orientation])
kb-height (subscribe [:get :keyboard-height])
pan-responder (pan-responder response-height kb-height orientation)
command (subscribe [:get-chat-command])]
(fn [response-height]
[view (merge (drag/pan-handlers pan-responder) [view (merge (drag/pan-handlers pan-responder)
{:style (st/request-info (:color @command))}) {:style (st/request-info (:color @command))})
[drag-icon] [drag-icon]
@ -54,46 +85,33 @@
[view st/cancel-container [view st/cancel-container
[icon :close-white st/cancel-icon]]]]]))) [icon :close-white st/cancel-icon]]]]])))
(defn container-animation-logic [{:keys [animation? to-value current-value val]}] (defn container-animation-logic [{:keys [to-value val]}]
(fn [_] (fn [_]
(if @animation? (let [to-value @to-value]
(let [to-value @to-value] (anim/start (anim/spring val {:toValue to-value})))))
(anim/start (anim/spring val {:toValue to-value})
(fn [arg]
(when (.-finished arg)
(dispatch [:set-animation :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 container [& children] (defn container [response-height & children]
(let [commands-input-is-switching? (subscribe [:animations :commands-input-is-switching?]) (let [;; todo to-response-height, cur-response-height must be specific
response-resize? (subscribe [:animations :response-resize?]) ;; for each chat
to-response-height (subscribe [:animations :to-response-height]) to-response-height (subscribe [:animations :to-response-height])
cur-response-height (subscribe [:animations :response-height-current]) changed (subscribe [:animations :response-height-changed])
response-height (anim/create-value (or @cur-response-height 0)) context {:to-value to-response-height
context {:animation? (reaction (or @commands-input-is-switching? @response-resize?)) :val response-height}
:to-value to-response-height on-update (container-animation-logic context)]
:current-value cur-response-height
:val response-height}
on-update (container-animation-logic context)]
(r/create-class (r/create-class
{:component-did-mount {:component-did-mount
on-update on-update
:component-did-update :component-did-update
on-update on-update
:reagent-render :reagent-render
(fn [& children] (fn [response-height & children]
@to-response-height @to-response-height @changed
(into [animated-view {:style (st/response-view (if (or @commands-input-is-switching? @response-resize?) (into [animated-view {:style (st/response-view response-height)}]
response-height
(or @cur-response-height 0)))}]
children))}))) children))})))
(defn response-view [] (defn response-view []
[container (let [response-height (anim/create-value 0)]
[request-info] [container response-height
[response-suggestions-view] [request-info response-height]
[view st/input-placeholder]]) [response-suggestions-view]
[view st/input-placeholder]]))

View File

@ -11,6 +11,9 @@
(defn spring [anim-value config] (defn spring [anim-value config]
(.spring animated anim-value (clj->js config))) (.spring animated anim-value (clj->js config)))
(defn decay [anim-value config]
(.decay animated anim-value (clj->js config)))
(defn anim-sequence [animations] (defn anim-sequence [animations]
(.sequence animated (clj->js animations))) (.sequence animated (clj->js animations)))

View File

@ -76,3 +76,5 @@
(r/as-element component)) (r/as-element component))
(def dismiss-keyboard! (u/require "dismissKeyboard")) (def dismiss-keyboard! (u/require "dismissKeyboard"))
(def device-event-emitter (.-DeviceEventEmitter react))
(def orientation (u/require "react-native-orientation"))

View File

@ -13,5 +13,5 @@
(def max-chat-name-length 20) (def max-chat-name-length 20)
(def response-input-hiding-duration 300) (def response-input-hiding-duration 100)
(def response-suggesstion-resize-duration 100) (def response-suggesstion-resize-duration 100)

View File

@ -37,14 +37,9 @@
:whisper-identity "" :whisper-identity ""
:phone-number ""} :phone-number ""}
:disable-group-creation false :disable-group-creation false
:animations {;; mutable data :animations {:to-response-height 0.1
:to-response-height nil
:response-height-current nil
:message-input-offset 0
:message-input-buttons-scale 1
:messages-offset 0 :messages-offset 0
:commands-input-is-switching? false ;; todo clear this
:response-resize? false
:tabs-bar-value (anim/create-value 0)}}) :tabs-bar-value (anim/create-value 0)}})
(def protocol-initialized-path [:protocol-initialized]) (def protocol-initialized-path [:protocol-initialized])