From ed5a1386e007d8799f59413188ce7e2c675c04f0 Mon Sep 17 00:00:00 2001 From: Diego Mello Date: Thu, 8 Feb 2018 12:08:50 -0200 Subject: [PATCH] Improvements on emoji picker / message box (#227) * Emoji keyboard * Keyboard emoji working * animation and flatlist * fix * Unread separator animation * easeInEaseOut animation --- android/app/build.gradle | 1 + .../com/rocketchatrn/MainApplication.java | 22 ++-- android/settings.gradle | 2 + app/actions/actionsTypes.js | 14 ++- app/actions/keyboard.js | 13 --- app/actions/room.js | 6 + app/animations/collapse.js | 3 +- app/animations/fade.js | 3 +- app/containers/Avatar.js | 25 ++-- app/containers/EmojiPicker/CustomEmoji.js | 5 +- app/containers/EmojiPicker/EmojiCategory.js | 38 +++--- app/containers/EmojiPicker/TabBar.js | 2 +- app/containers/EmojiPicker/index.js | 14 ++- app/containers/Header.js | 2 +- .../MessageBox/AnimatedContainer.js | 74 ------------ app/containers/MessageBox/EmojiKeyboard.js | 23 ++++ app/containers/MessageBox/index.js | 108 +++++++++--------- .../MessageBox/{style.js => styles.js} | 11 +- app/containers/MessageErrorActions.js | 2 +- app/containers/Sidebar.js | 5 - app/containers/message/Emoji.js | 6 +- app/containers/message/Image.js | 2 +- app/containers/message/Markdown.js | 3 +- app/containers/message/PhotoModal.js | 2 +- app/containers/message/ReactionsModal.js | 2 +- app/containers/message/index.js | 43 ++----- app/containers/message/styles.js | 7 ++ app/presentation/KeyboardView.js | 12 +- app/reducers/index.js | 4 +- app/reducers/keyboard.js | 22 ---- app/reducers/room.js | 8 +- app/views/Photo.js | 2 +- app/views/RoomView/Header/index.js | 2 +- app/views/RoomView/ListView.js | 16 +-- app/views/RoomView/ReactionPicker.js | 2 +- app/views/RoomView/UnreadSeparator.js | 23 ++-- app/views/RoomView/index.js | 56 ++++----- app/views/RoomView/styles.js | 4 +- app/views/RoomsListView/Header/index.js | 2 +- app/views/SelectUsersView.js | 2 +- index.android.js | 2 + ios/RocketChatRN.xcodeproj/project.pbxproj | 60 ++++++++++ package-lock.json | 24 ++-- package.json | 5 +- 44 files changed, 336 insertions(+), 348 deletions(-) delete mode 100644 app/actions/keyboard.js delete mode 100644 app/containers/MessageBox/AnimatedContainer.js create mode 100644 app/containers/MessageBox/EmojiKeyboard.js rename app/containers/MessageBox/{style.js => styles.js} (91%) delete mode 100644 app/reducers/keyboard.js diff --git a/android/app/build.gradle b/android/app/build.gradle index 1d5816d8..e6c65008 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -144,6 +144,7 @@ android { } dependencies { + compile project(":reactnativekeyboardinput") compile project(':react-native-splash-screen') compile project(':react-native-video') compile project(':react-native-push-notification') diff --git a/android/app/src/main/java/com/rocketchatrn/MainApplication.java b/android/app/src/main/java/com/rocketchatrn/MainApplication.java index 72ed5b81..beb8cc02 100644 --- a/android/app/src/main/java/com/rocketchatrn/MainApplication.java +++ b/android/app/src/main/java/com/rocketchatrn/MainApplication.java @@ -16,6 +16,7 @@ import com.facebook.soloader.SoLoader; import com.dieam.reactnativepushnotification.ReactNativePushNotificationPackage; import com.brentvatne.react.ReactVideoPackage; import com.remobile.toast.RCTToastPackage; +import com.wix.reactnativekeyboardinput.KeyboardInputPackage; import java.util.Arrays; import java.util.List; @@ -33,16 +34,17 @@ public class MainApplication extends Application implements ReactApplication { protected List getPackages() { return Arrays.asList( new MainReactPackage(), - new SvgPackage(), - new ImagePickerPackage(), - new VectorIconsPackage(), - new RNFetchBlobPackage(), - new ZeroconfReactPackage(), - new RealmReactPackage(), - new ReactNativePushNotificationPackage(), - new ReactVideoPackage(), - new SplashScreenReactPackage(), - new RCTToastPackage() + new SvgPackage(), + new ImagePickerPackage(), + new VectorIconsPackage(), + new RNFetchBlobPackage(), + new ZeroconfReactPackage(), + new RealmReactPackage(), + new ReactNativePushNotificationPackage(), + new ReactVideoPackage(), + new SplashScreenReactPackage(), + new RCTToastPackage(), + new KeyboardInputPackage(MainApplication.this) ); } }; diff --git a/android/settings.gradle b/android/settings.gradle index 5455aadb..236de741 100644 --- a/android/settings.gradle +++ b/android/settings.gradle @@ -1,4 +1,6 @@ rootProject.name = 'RocketChatRN' +include ':reactnativekeyboardinput' +project(':reactnativekeyboardinput').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-keyboard-input/lib/android') include ':react-native-splash-screen' project(':react-native-splash-screen').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-splash-screen/android') include ':react-native-video' diff --git a/app/actions/actionsTypes.js b/app/actions/actionsTypes.js index 4e67c747..2422a207 100644 --- a/app/actions/actionsTypes.js +++ b/app/actions/actionsTypes.js @@ -28,7 +28,17 @@ export const FORGOT_PASSWORD = createRequestTypes('FORGOT_PASSWORD', [ ]); export const USER = createRequestTypes('USER', ['SET']); export const ROOMS = createRequestTypes('ROOMS', [...defaultTypes, 'SET_SEARCH']); -export const ROOM = createRequestTypes('ROOM', ['ADD_USER_TYPING', 'REMOVE_USER_TYPING', 'SOMEONE_TYPING', 'OPEN', 'CLOSE', 'USER_TYPING', 'MESSAGE_RECEIVED', 'SET_LAST_OPEN']); +export const ROOM = createRequestTypes('ROOM', [ + 'ADD_USER_TYPING', + 'REMOVE_USER_TYPING', + 'SOMEONE_TYPING', + 'OPEN', + 'CLOSE', + 'USER_TYPING', + 'MESSAGE_RECEIVED', + 'SET_LAST_OPEN', + 'LAYOUT_ANIMATION' +]); export const APP = createRequestTypes('APP', ['READY', 'INIT']); export const MESSAGES = createRequestTypes('MESSAGES', [ ...defaultTypes, @@ -82,4 +92,4 @@ export const ACTIVE_USERS = createRequestTypes('ACTIVE_USERS', ['SET', 'REQUEST' export const INCREMENT = 'INCREMENT'; export const DECREMENT = 'DECREMENT'; -export const KEYBOARD = createRequestTypes('KEYBOARD', ['OPEN', 'CLOSE']); + diff --git a/app/actions/keyboard.js b/app/actions/keyboard.js deleted file mode 100644 index 6c598d02..00000000 --- a/app/actions/keyboard.js +++ /dev/null @@ -1,13 +0,0 @@ -import * as types from './actionsTypes'; - -export function setKeyboardOpen() { - return { - type: types.KEYBOARD.OPEN - }; -} - -export function setKeyboardClosed() { - return { - type: types.KEYBOARD.CLOSE - }; -} diff --git a/app/actions/room.js b/app/actions/room.js index ebae2bd6..1b3a67e1 100644 --- a/app/actions/room.js +++ b/app/actions/room.js @@ -55,3 +55,9 @@ export function setLastOpen(date = new Date()) { date }; } + +export function layoutAnimation() { + return { + type: types.ROOM.LAYOUT_ANIMATION + }; +} diff --git a/app/animations/collapse.js b/app/animations/collapse.js index 35bca751..05cfce52 100644 --- a/app/animations/collapse.js +++ b/app/animations/collapse.js @@ -42,7 +42,8 @@ export default class Panel extends React.Component { this.state.animation, { toValue: finalValue, - duration: 150 + duration: 150, + useNativeDriver: true } ).start(); } diff --git a/app/animations/fade.js b/app/animations/fade.js index b9f27690..36da8da3 100644 --- a/app/animations/fade.js +++ b/app/animations/fade.js @@ -29,7 +29,8 @@ export default class Fade extends React.Component { } Animated.timing(this._visibility, { toValue: nextProps.visible ? 1 : 0, - duration: 300 + duration: 300, + useNativeDriver: true }).start(() => { this.setState({ visible: nextProps.visible }); }); diff --git a/app/containers/Avatar.js b/app/containers/Avatar.js index 8ff05e6d..f547b778 100644 --- a/app/containers/Avatar.js +++ b/app/containers/Avatar.js @@ -1,7 +1,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import { connect } from 'react-redux'; -import { StyleSheet, Text, View } from 'react-native'; +import { StyleSheet, Text, View, ViewPropTypes } from 'react-native'; import { CachedImage } from 'react-native-img-cache'; import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons'; import avatarInitialsAndColor from '../utils/avatarInitialsAndColor'; @@ -23,8 +23,16 @@ const styles = StyleSheet.create({ @connect(state => ({ baseUrl: state.settings.Site_Url || state.server ? state.server.server : '' })) - -class Avatar extends React.PureComponent { +export default class Avatar extends React.PureComponent { + static propTypes = { + style: ViewPropTypes.style, + baseUrl: PropTypes.string, + text: PropTypes.string.isRequired, + avatar: PropTypes.string, + size: PropTypes.number, + borderRadius: PropTypes.number, + type: PropTypes.string + }; render() { const { text = '', size = 25, baseUrl, borderRadius = 4, style, avatar, type = 'd' @@ -76,14 +84,3 @@ class Avatar extends React.PureComponent { ); } } - -Avatar.propTypes = { - style: PropTypes.object, - baseUrl: PropTypes.string, - text: PropTypes.string.isRequired, - avatar: PropTypes.string, - size: PropTypes.number, - borderRadius: PropTypes.number, - type: PropTypes.string -}; -export default Avatar; diff --git a/app/containers/EmojiPicker/CustomEmoji.js b/app/containers/EmojiPicker/CustomEmoji.js index 5988296f..8215f0ea 100644 --- a/app/containers/EmojiPicker/CustomEmoji.js +++ b/app/containers/EmojiPicker/CustomEmoji.js @@ -1,4 +1,5 @@ import React from 'react'; +import { ViewPropTypes } from 'react-native'; import PropTypes from 'prop-types'; import { CachedImage } from 'react-native-img-cache'; import { connect } from 'react-redux'; @@ -6,11 +7,11 @@ import { connect } from 'react-redux'; @connect(state => ({ baseUrl: state.settings.Site_Url })) -export default class extends React.Component { +export default class CustomEmoji extends React.Component { static propTypes = { baseUrl: PropTypes.string.isRequired, emoji: PropTypes.object.isRequired, - style: PropTypes.object + style: ViewPropTypes.style } shouldComponentUpdate() { return false; diff --git a/app/containers/EmojiPicker/EmojiCategory.js b/app/containers/EmojiPicker/EmojiCategory.js index 0fc3c65d..09e15fde 100644 --- a/app/containers/EmojiPicker/EmojiCategory.js +++ b/app/containers/EmojiPicker/EmojiCategory.js @@ -1,11 +1,12 @@ import React from 'react'; import PropTypes from 'prop-types'; -import { Text, View, TouchableOpacity, Platform } from 'react-native'; +import { Text, TouchableOpacity, Platform } from 'react-native'; import { emojify } from 'react-emojione'; import { responsive } from 'react-native-responsive-ui'; +import { OptimizedFlatList } from 'react-native-optimized-flatlist'; import styles from './styles'; import CustomEmoji from './CustomEmoji'; - +import scrollPersistTaps from '../../utils/scrollPersistTaps'; const emojisPerRow = Platform.OS === 'ios' ? 8 : 9; @@ -21,8 +22,6 @@ const renderEmoji = (emoji, size) => { }; -const nextFrame = () => new Promise(resolve => requestAnimationFrame(resolve)); - @responsive export default class EmojiCategory extends React.Component { static propTypes = { @@ -40,23 +39,7 @@ export default class EmojiCategory extends React.Component { this.emojis = []; } componentWillMount() { - this.emojis = this.props.emojis.slice(0, emojisPerRow * 3).map(item => this.renderItem(item, this.size)); - } - async componentDidMount() { - const array = this.props.emojis; - const temparray = []; - let i; - let j; - const chunk = emojisPerRow * 3; - for (i = chunk, j = array.length; i < j; i += chunk) { - temparray.push(array.slice(i, i + chunk)); - } - temparray.forEach(async(items) => { - await nextFrame(); - this.emojis = this.emojis.concat(items.map(item => this.renderItem(item, this.size))); - this.forceUpdate(); - await nextFrame(); - }); + this.emojis = this.props.emojis; } shouldComponentUpdate() { @@ -75,6 +58,17 @@ export default class EmojiCategory extends React.Component { } render() { - return {this.emojis}; + return ( + (item.isCustom && item.content) || item} + data={this.props.emojis} + renderItem={({ item }) => this.renderItem(item, this.size)} + numColumns={emojisPerRow} + initialNumToRender={45} + getItemLayout={(data, index) => ({ length: this.size, offset: this.size * index, index })} + removeClippedSubviews + {...scrollPersistTaps} + /> + ); } } diff --git a/app/containers/EmojiPicker/TabBar.js b/app/containers/EmojiPicker/TabBar.js index fb23b30e..4886006a 100644 --- a/app/containers/EmojiPicker/TabBar.js +++ b/app/containers/EmojiPicker/TabBar.js @@ -3,7 +3,7 @@ import PropTypes from 'prop-types'; import { View, TouchableOpacity, Text } from 'react-native'; import styles from './styles'; -export default class extends React.PureComponent { +export default class TabBar extends React.PureComponent { static propTypes = { goToPage: PropTypes.func, activeTab: PropTypes.number, diff --git a/app/containers/EmojiPicker/index.js b/app/containers/EmojiPicker/index.js index 4209b5cc..c722da0e 100644 --- a/app/containers/EmojiPicker/index.js +++ b/app/containers/EmojiPicker/index.js @@ -8,15 +8,15 @@ import TabBar from './TabBar'; import EmojiCategory from './EmojiCategory'; import styles from './styles'; import categories from './categories'; -import scrollPersistTaps from '../../utils/scrollPersistTaps'; import database from '../../lib/realm'; import { emojisByCategory } from '../../emojis'; const scrollProps = { - keyboardShouldPersistTaps: 'always' + keyboardShouldPersistTaps: 'always', + keyboardDismissMode: 'none' }; -export default class extends Component { +export default class EmojiPicker extends Component { static propTypes = { onEmojiSelected: PropTypes.func, tabEmojiStyle: PropTypes.object, @@ -45,9 +45,10 @@ export default class extends Component { this.customEmojis.addListener(this.updateCustomEmojis); this.updateFrequentlyUsed(); this.updateCustomEmojis(); - setTimeout(() => this.setState({ show: true }), 100); } - + componentDidMount() { + requestAnimationFrame(() => this.setState({ show: true })); + } componentWillUnmount() { this.frequentlyUsed.removeAllListeners(); this.customEmojis.removeAllListeners(); @@ -122,13 +123,14 @@ export default class extends Component { } contentProps={scrollProps} + // prerenderingSiblingsNumber={1} > { categories.tabs.map((tab, i) => ( {this.renderCategory(tab.category, i)} diff --git a/app/containers/Header.js b/app/containers/Header.js index b285d412..7dfa46f1 100644 --- a/app/containers/Header.js +++ b/app/containers/Header.js @@ -33,7 +33,7 @@ const styles = StyleSheet.create({ } }); -export default class extends React.PureComponent { +export default class Header extends React.PureComponent { static propTypes = { subview: PropTypes.object.isRequired } diff --git a/app/containers/MessageBox/AnimatedContainer.js b/app/containers/MessageBox/AnimatedContainer.js deleted file mode 100644 index 688ccd59..00000000 --- a/app/containers/MessageBox/AnimatedContainer.js +++ /dev/null @@ -1,74 +0,0 @@ -import React from 'react'; -import PropTypes from 'prop-types'; -import { Animated } from 'react-native'; - -export default class MessageBox extends React.PureComponent { - static propTypes = { - subview: PropTypes.object.isRequired, - visible: PropTypes.bool.isRequired, - messageboxHeight: PropTypes.number.isRequired - } - - constructor(props) { - super(props); - this.animatedBottom = new Animated.Value(0); - this.state = { render: false }; - } - - componentWillReceiveProps(nextProps) { - if (this.props.visible === nextProps.visible) { - return; - } - if (nextProps.visible) { - return this.show(); - } - this.hide(); - } - - show() { - this.setState({ render: true }); - this.animatedBottom.setValue(0); - Animated.timing(this.animatedBottom, { - toValue: 1, - duration: 300, - useNativeDriver: true - }).start(); - } - - hide() { - Animated.timing(this.animatedBottom, { - toValue: 0, - duration: 300, - useNativeDriver: true - }).start(); - setTimeout(() => { - this.setState({ render: false }); - }, 300); - } - - render() { - const bottom = this.animatedBottom.interpolate({ - inputRange: [0, 1], - outputRange: [0, -this.props.messageboxHeight - 200] - }); - - if (!this.state.render) { - return null; - } - - return ( - - {this.props.subview} - - ); - } -} diff --git a/app/containers/MessageBox/EmojiKeyboard.js b/app/containers/MessageBox/EmojiKeyboard.js new file mode 100644 index 00000000..9a6cb8b9 --- /dev/null +++ b/app/containers/MessageBox/EmojiKeyboard.js @@ -0,0 +1,23 @@ +import React from 'react'; +import { View } from 'react-native'; +import { KeyboardRegistry } from 'react-native-keyboard-input'; +import { Provider } from 'react-redux'; +import store from '../../lib/createStore'; +import EmojiPicker from '../EmojiPicker'; +import styles from './styles'; + +export default class EmojiKeyboard extends React.PureComponent { + onEmojiSelected = (emoji) => { + KeyboardRegistry.onItemSelected('EmojiKeyboard', { emoji }); + } + render() { + return ( + + + this.onEmojiSelected(emoji)} /> + + + ); + } +} +KeyboardRegistry.registerKeyboard('EmojiKeyboard', () => EmojiKeyboard); diff --git a/app/containers/MessageBox/index.js b/app/containers/MessageBox/index.js index b5f8aa3f..db4a0a06 100644 --- a/app/containers/MessageBox/index.js +++ b/app/containers/MessageBox/index.js @@ -1,22 +1,21 @@ import React from 'react'; import PropTypes from 'prop-types'; -import { View, TextInput, SafeAreaView, FlatList, Text, TouchableOpacity, Keyboard, StyleSheet } from 'react-native'; +import { View, TextInput, SafeAreaView, FlatList, Text, TouchableOpacity } from 'react-native'; import Icon from 'react-native-vector-icons/MaterialIcons'; import ImagePicker from 'react-native-image-picker'; import { connect } from 'react-redux'; import { emojify } from 'react-emojione'; -import { userTyping } from '../../actions/room'; +import { KeyboardAccessoryView } from 'react-native-keyboard-input'; +import { userTyping, layoutAnimation } from '../../actions/room'; import RocketChat from '../../lib/rocketchat'; import { editRequest, editCancel, clearInput } from '../../actions/messages'; -import styles from './style'; +import styles from './styles'; import MyIcon from '../icons'; import database from '../../lib/realm'; import Avatar from '../Avatar'; import CustomEmoji from '../EmojiPicker/CustomEmoji'; -import AnimatedContainer from './AnimatedContainer'; -import EmojiPicker from '../EmojiPicker'; -import scrollPersistTaps from '../../utils/scrollPersistTaps'; import { emojis } from '../../emojis'; +import './EmojiKeyboard'; const MENTIONS_TRACKING_TYPE_USERS = '@'; const MENTIONS_TRACKING_TYPE_EMOJIS = ':'; @@ -29,13 +28,13 @@ const onlyUnique = function onlyUnique(value, index, self) { room: state.room, message: state.messages.message, editing: state.messages.editing, - baseUrl: state.settings.Site_Url || state.server ? state.server.server : '', - isKeyboardOpen: state.keyboard.isOpen + baseUrl: state.settings.Site_Url || state.server ? state.server.server : '' }), dispatch => ({ editCancel: () => dispatch(editCancel()), editRequest: message => dispatch(editRequest(message)), typing: status => dispatch(userTyping(status)), - clearInput: () => dispatch(clearInput()) + clearInput: () => dispatch(clearInput()), + layoutAnimation: () => dispatch(layoutAnimation()) })) export default class MessageBox extends React.PureComponent { static propTypes = { @@ -48,23 +47,23 @@ export default class MessageBox extends React.PureComponent { editing: PropTypes.bool, typing: PropTypes.func, clearInput: PropTypes.func, - isKeyboardOpen: PropTypes.bool + layoutAnimation: PropTypes.func } constructor(props) { super(props); this.state = { - messageboxHeight: 0, text: '', mentions: [], showMentionsContainer: false, - showEmojiContainer: false, + showEmojiKeyboard: false, trackingType: '' }; this.users = []; this.rooms = []; this.emojis = []; this.customEmojis = []; + this._onEmojiSelected = this._onEmojiSelected.bind(this); } componentWillReceiveProps(nextProps) { if (this.props.message !== nextProps.message && nextProps.message.msg) { @@ -72,8 +71,6 @@ export default class MessageBox extends React.PureComponent { this.component.focus(); } else if (!nextProps.message) { this.setState({ text: '' }); - } else if (this.props.isKeyboardOpen !== nextProps.isKeyboardOpen && nextProps.isKeyboardOpen) { - this.closeEmoji(); } } @@ -100,6 +97,10 @@ export default class MessageBox extends React.PureComponent { }); } + onKeyboardResigned() { + this.closeEmoji(); + } + get leftButtons() { const { editing } = this.props; if (editing) { @@ -111,7 +112,7 @@ export default class MessageBox extends React.PureComponent { onPress={() => this.editCancel()} />); } - return !this.state.showEmojiContainer ? ( this.openEmoji()} accessibilityLabel='Open emoji selector' @@ -184,13 +185,11 @@ export default class MessageBox extends React.PureComponent { } async openEmoji() { await this.setState({ - showEmojiContainer: true, - showMentionsContainer: false + showEmojiKeyboard: true }); - Keyboard.dismiss(); } closeEmoji() { - this.setState({ showEmojiContainer: false }); + this.setState({ showEmojiKeyboard: false }); } submit(message) { this.setState({ text: '' }); @@ -324,9 +323,12 @@ export default class MessageBox extends React.PureComponent { } identifyMentionKeyword(keyword, type) { + if (!this.state.showMentionsContainer) { + this.props.layoutAnimation(); + } this.setState({ showMentionsContainer: true, - showEmojiContainer: false, + showEmojiKeyboard: false, trackingType: type }); this.updateMentions(keyword, type); @@ -360,8 +362,9 @@ export default class MessageBox extends React.PureComponent { this.component.focus(); requestAnimationFrame(() => this.stopTrackingMention()); } - _onEmojiSelected(emoji) { + _onEmojiSelected(keyboardId, params) { const { text } = this.state; + const { emoji } = params; let newText = ''; // if messagebox has an active cursor @@ -390,7 +393,7 @@ export default class MessageBox extends React.PureComponent { return ( @@ -399,7 +402,7 @@ export default class MessageBox extends React.PureComponent { return ( {emojify(`:${ item }:`, { output: 'unicode' })} @@ -433,34 +436,24 @@ export default class MessageBox extends React.PureComponent { ); } - renderEmoji() { - const emojiContainer = ( - - this._onEmojiSelected(emoji)} /> - - ); - const { showEmojiContainer, messageboxHeight } = this.state; - return ; - } - renderMentions() { - const list = ( - this.renderMentionItem(item)} - keyExtractor={item => item._id || item} - {...scrollPersistTaps} - /> - ); - const { showMentionsContainer, messageboxHeight } = this.state; - return ; - } - render() { + renderMentions = () => ( + this.renderMentionItem(item)} + keyExtractor={item => item._id || item} + keyboardShouldPersistTaps='always' + /> + ); + + renderContent() { return ( - + [ + this.renderMentions(), this.setState({ messageboxHeight: event.nativeEvent.layout.height })} > {this.leftButtons} @@ -480,9 +473,22 @@ export default class MessageBox extends React.PureComponent { {this.rightButtons} - {this.renderMentions()} - {this.renderEmoji()} - + ] + ); + } + + render() { + return ( + this.renderContent()} + kbInputRef={this.component} + kbComponent={this.state.showEmojiKeyboard ? 'EmojiKeyboard' : null} + onKeyboardResigned={() => this.onKeyboardResigned()} + onItemSelected={this._onEmojiSelected} + trackInteractive + revealKeyboardInteractive + requiresSameParentToManageScrollView + /> ); } } diff --git a/app/containers/MessageBox/style.js b/app/containers/MessageBox/styles.js similarity index 91% rename from app/containers/MessageBox/style.js rename to app/containers/MessageBox/styles.js index 98395ce4..fed138d6 100644 --- a/app/containers/MessageBox/style.js +++ b/app/containers/MessageBox/styles.js @@ -21,13 +21,14 @@ export default StyleSheet.create({ flexGrow: 0 }, textBoxInput: { - paddingHorizontal: 5, textAlignVertical: 'center', maxHeight: 120, flexGrow: 1, width: 1, paddingTop: 15, - paddingBottom: 15 + paddingBottom: 15, + paddingLeft: 0, + paddingRight: 0 }, editing: { backgroundColor: '#fff5df' @@ -37,6 +38,7 @@ export default StyleSheet.create({ fontSize: 20, textAlign: 'center', padding: 15, + paddingHorizontal: 21, flex: 0 }, actionRow: { @@ -98,5 +100,10 @@ export default StyleSheet.create({ fontWeight: 'bold', textAlign: 'center', width: 46 + }, + emojiKeyboardContainer: { + flex: 1, + borderTopColor: '#ECECEC', + borderTopWidth: 1 } }); diff --git a/app/containers/MessageErrorActions.js b/app/containers/MessageErrorActions.js index b96ca79a..4f3439fa 100644 --- a/app/containers/MessageErrorActions.js +++ b/app/containers/MessageErrorActions.js @@ -16,7 +16,7 @@ import database from '../lib/realm'; errorActionsHide: () => dispatch(errorActionsHide()) }) ) -export default class MessageActions extends React.Component { +export default class MessageErrorActions extends React.Component { static propTypes = { errorActionsHide: PropTypes.func.isRequired, showErrorActions: PropTypes.bool.isRequired, diff --git a/app/containers/Sidebar.js b/app/containers/Sidebar.js index c93f5857..ae76794f 100644 --- a/app/containers/Sidebar.js +++ b/app/containers/Sidebar.js @@ -1,7 +1,6 @@ import React, { Component } from 'react'; import PropTypes from 'prop-types'; import { ScrollView, Text, View, StyleSheet, FlatList, TouchableHighlight } from 'react-native'; -import { DrawerItems } from 'react-navigation'; import { connect } from 'react-redux'; import database from '../lib/realm'; @@ -101,10 +100,6 @@ export default class Sidebar extends Component { return ( - { const emojiExtension = customEmojis[content]; if (emojiExtension) { const emoji = { extension: emojiExtension, content }; - const style = StyleSheet.flatten(styles.customEmoji); element.props.children = ( - + ); } return element; diff --git a/app/containers/message/PhotoModal.js b/app/containers/message/PhotoModal.js index 694015b0..7d378794 100644 --- a/app/containers/message/PhotoModal.js +++ b/app/containers/message/PhotoModal.js @@ -25,7 +25,7 @@ const styles = { } }; -export default class extends React.PureComponent { +export default class PhotoModal extends React.PureComponent { static propTypes = { title: PropTypes.string.isRequired, image: PropTypes.string.isRequired, diff --git a/app/containers/message/ReactionsModal.js b/app/containers/message/ReactionsModal.js index c62dab65..08491518 100644 --- a/app/containers/message/ReactionsModal.js +++ b/app/containers/message/ReactionsModal.js @@ -52,7 +52,7 @@ const styles = StyleSheet.create({ }); const standardEmojiStyle = { fontSize: 20 }; const customEmojiStyle = { width: 20, height: 20 }; -export default class extends React.PureComponent { +export default class ReactionsModal extends React.PureComponent { static propTypes = { isVisible: PropTypes.bool.isRequired, onClose: PropTypes.func.isRequired, diff --git a/app/containers/message/index.js b/app/containers/message/index.js index 840cfa0b..a361ef57 100644 --- a/app/containers/message/index.js +++ b/app/containers/message/index.js @@ -1,10 +1,11 @@ import React from 'react'; import PropTypes from 'prop-types'; -import { View, TouchableHighlight, Text, TouchableOpacity, Animated, Keyboard, StyleSheet, Vibration } from 'react-native'; +import { View, TouchableHighlight, Text, TouchableOpacity, Vibration } from 'react-native'; import { connect } from 'react-redux'; import Icon from 'react-native-vector-icons/MaterialIcons'; import moment from 'moment'; import equal from 'deep-equal'; +import { KeyboardUtils } from 'react-native-keyboard-input'; import { actionsShow, errorActionsShow, toggleReactionPicker } from '../../actions/messages'; import Image from './Image'; @@ -20,9 +21,6 @@ import Emoji from './Emoji'; import messageStatus from '../../constants/messagesStatus'; import styles from './styles'; -const avatar = { marginRight: 10 }; -const flex = { flexDirection: 'row', flex: 1 }; - @connect(state => ({ message: state.messages.message, editing: state.messages.editing, @@ -43,7 +41,6 @@ export default class Message extends React.Component { editing: PropTypes.bool, actionsShow: PropTypes.func, errorActionsShow: PropTypes.func, - animate: PropTypes.bool, customEmojis: PropTypes.object, toggleReactionPicker: PropTypes.func, onReactionPress: PropTypes.func @@ -54,18 +51,6 @@ export default class Message extends React.Component { this.state = { reactionsModal: false }; this.onClose = this.onClose.bind(this); } - - componentWillMount() { - this._visibility = new Animated.Value(this.props.animate ? 0 : 1); - } - componentDidMount() { - if (this.props.animate) { - Animated.timing(this._visibility, { - toValue: 1, - duration: 300 - }).start(); - } - } componentWillReceiveProps() { this.extraStyle = this.extraStyle || {}; if (this.props.item.status === messageStatus.TEMP || this.props.item.status === messageStatus.ERROR) { @@ -84,7 +69,7 @@ export default class Message extends React.Component { } onPress = () => { - Keyboard.dismiss(); + KeyboardUtils.dismiss(); } onLongPress() { @@ -207,8 +192,8 @@ export default class Message extends React.Component { { reaction.usernames.length } @@ -239,18 +224,8 @@ export default class Message extends React.Component { const { item, message, editing, baseUrl, customEmojis } = this.props; - - const marginLeft = this._visibility.interpolate({ - inputRange: [0, 1], - outputRange: [-30, 0] - }); - const opacity = this._visibility.interpolate({ - inputRange: [0, 1], - outputRange: [0, 1] - }); const username = item.alias || item.u.username; const isEditing = message._id === item._id && editing; - const accessibilityLabel = `Message from ${ username } at ${ moment(item.ts).format(this.props.Message_TimeFormat) }, ${ this.props.item.msg }`; return ( @@ -263,11 +238,11 @@ export default class Message extends React.Component { style={[styles.message, isEditing ? styles.editing : null]} accessibilityLabel={accessibilityLabel} > - + {this.renderError()} - + : null } - + ); } diff --git a/app/containers/message/styles.js b/app/containers/message/styles.js index 7752dea7..23911cc4 100644 --- a/app/containers/message/styles.js +++ b/app/containers/message/styles.js @@ -5,6 +5,10 @@ export default StyleSheet.create({ flexGrow: 1, flexShrink: 1 }, + flex: { + flexDirection: 'row', + flex: 1 + }, message: { padding: 12, paddingTop: 6, @@ -63,5 +67,8 @@ export default StyleSheet.create({ reactionCustomEmoji: { width: 15, height: 15 + }, + avatar: { + marginRight: 10 } }); diff --git a/app/presentation/KeyboardView.js b/app/presentation/KeyboardView.js index 6e405c66..82a5e110 100644 --- a/app/presentation/KeyboardView.js +++ b/app/presentation/KeyboardView.js @@ -2,14 +2,8 @@ import React from 'react'; import PropTypes from 'prop-types'; import { ViewPropTypes } from 'react-native'; import { KeyboardAwareScrollView } from 'react-native-keyboard-aware-scroll-view'; -import { connect } from 'react-redux'; import scrollPersistTaps from '../utils/scrollPersistTaps'; -import { setKeyboardOpen, setKeyboardClosed } from '../actions/keyboard'; -@connect(null, dispatch => ({ - setKeyboardOpen: () => dispatch(setKeyboardOpen()), - setKeyboardClosed: () => dispatch(setKeyboardClosed()) -})) export default class KeyboardView extends React.PureComponent { static propTypes = { style: ViewPropTypes.style, @@ -19,9 +13,7 @@ export default class KeyboardView extends React.PureComponent { children: PropTypes.oneOfType([ PropTypes.arrayOf(PropTypes.node), PropTypes.node - ]), - setKeyboardOpen: PropTypes.func, - setKeyboardClosed: PropTypes.func + ]) } render() { @@ -34,8 +26,6 @@ export default class KeyboardView extends React.PureComponent { alwaysBounceVertical={false} extraHeight={this.props.keyboardVerticalOffset} behavior='position' - onKeyboardWillShow={() => this.props.setKeyboardOpen()} - onKeyboardWillHide={() => this.props.setKeyboardClosed()} > {this.props.children} diff --git a/app/reducers/index.js b/app/reducers/index.js index 60c2be01..f7872a53 100644 --- a/app/reducers/index.js +++ b/app/reducers/index.js @@ -12,7 +12,6 @@ import app from './app'; import permissions from './permissions'; import customEmojis from './customEmojis'; import activeUsers from './activeUsers'; -import keyboard from './keyboard'; export default combineReducers({ settings, @@ -27,6 +26,5 @@ export default combineReducers({ rooms, permissions, customEmojis, - activeUsers, - keyboard + activeUsers }); diff --git a/app/reducers/keyboard.js b/app/reducers/keyboard.js deleted file mode 100644 index 0885be77..00000000 --- a/app/reducers/keyboard.js +++ /dev/null @@ -1,22 +0,0 @@ -import * as types from '../actions/actionsTypes'; - -const initialState = { - isOpen: false -}; - -export default function messages(state = initialState, action) { - switch (action.type) { - case types.KEYBOARD.OPEN: - return { - ...state, - isOpen: true - }; - case types.KEYBOARD.CLOSE: - return { - ...state, - isOpen: false - }; - default: - return state; - } -} diff --git a/app/reducers/room.js b/app/reducers/room.js index 03b1fa89..4ed7c921 100644 --- a/app/reducers/room.js +++ b/app/reducers/room.js @@ -1,7 +1,8 @@ import * as types from '../actions/actionsTypes'; const initialState = { - usersTyping: [] + usersTyping: [], + layoutAnimation: new Date() }; export default function room(state = initialState, action) { @@ -31,6 +32,11 @@ export default function room(state = initialState, action) { ...state, usersTyping: [...state.usersTyping.filter(user => user !== action.username)] }; + case types.ROOM.LAYOUT_ANIMATION: + return { + ...state, + layoutAnimation: new Date() + }; default: return state; } diff --git a/app/views/Photo.js b/app/views/Photo.js index e6859754..a13dd4ad 100644 --- a/app/views/Photo.js +++ b/app/views/Photo.js @@ -14,7 +14,7 @@ const styles = { } }; -export default class extends React.PureComponent { +export default class Photo extends React.PureComponent { static propTypes = { navigation: PropTypes.object.isRequired } diff --git a/app/views/RoomView/Header/index.js b/app/views/RoomView/Header/index.js index 5cf03f12..1402712f 100644 --- a/app/views/RoomView/Header/index.js +++ b/app/views/RoomView/Header/index.js @@ -18,7 +18,7 @@ import { closeRoom } from '../../../actions/room'; }), dispatch => ({ close: () => dispatch(closeRoom()) })) -export default class extends React.PureComponent { +export default class RoomHeaderView extends React.PureComponent { static propTypes = { close: PropTypes.func.isRequired, navigation: PropTypes.object.isRequired, diff --git a/app/views/RoomView/ListView.js b/app/views/RoomView/ListView.js index 9c89b46f..500e693b 100644 --- a/app/views/RoomView/ListView.js +++ b/app/views/RoomView/ListView.js @@ -1,7 +1,7 @@ import { ListView as OldList } from 'realm/react-native'; import React from 'react'; import cloneReferencedElement from 'react-clone-referenced-element'; -import { ScrollView, ListView as OldList2 } from 'react-native'; +import { ScrollView, ListView as OldList2, LayoutAnimation } from 'react-native'; import moment from 'moment'; import { connect } from 'react-redux'; import PropTypes from 'prop-types'; @@ -12,6 +12,7 @@ import styles from './styles'; import debounce from '../../utils/debounce'; import Typing from '../../containers/Typing'; import database from '../../lib/realm'; +import scrollPersistTaps from '../../utils/scrollPersistTaps'; const DEFAULT_SCROLL_CALLBACK_THROTTLE = 100; @@ -28,10 +29,6 @@ export class DataSource extends OldList.DataSource { const ds = new DataSource({ rowHasChanged: (r1, r2) => r1._id !== r2._id }); -@connect(state => ({ - lastOpen: state.room.lastOpen -})) - export class List extends React.Component { static propTypes = { onEndReached: PropTypes.func, @@ -54,6 +51,9 @@ export class List extends React.Component { shouldComponentUpdate(nextProps) { return this.props.end !== nextProps.end; } + componentWillUpdate() { + LayoutAnimation.easeInEaseOut(); + } updateState = debounce(() => { // this.setState({ this.dataSource = this.dataSource.cloneWithRows(this.data); @@ -72,12 +72,14 @@ export class List extends React.Component { dataSource={this.dataSource} renderRow={item => this.props.renderRow(item)} initialListSize={10} - keyboardShouldPersistTaps='always' - keyboardDismissMode='none' + {...scrollPersistTaps} />); } } +@connect(state => ({ + lastOpen: state.room.lastOpen +})) export class ListView extends OldList2 { constructor(props) { super(props); diff --git a/app/views/RoomView/ReactionPicker.js b/app/views/RoomView/ReactionPicker.js index 96ff5fd2..7c5592cb 100644 --- a/app/views/RoomView/ReactionPicker.js +++ b/app/views/RoomView/ReactionPicker.js @@ -17,7 +17,7 @@ const tabEmojiStyle = { fontSize: 15 }; toggleReactionPicker: message => dispatch(toggleReactionPicker(message)) })) @responsive -export default class extends React.Component { +export default class ReactionPicker extends React.Component { static propTypes = { window: PropTypes.any, showReactionPicker: PropTypes.bool, diff --git a/app/views/RoomView/UnreadSeparator.js b/app/views/RoomView/UnreadSeparator.js index c2856e67..f5384fc2 100644 --- a/app/views/RoomView/UnreadSeparator.js +++ b/app/views/RoomView/UnreadSeparator.js @@ -1,5 +1,5 @@ import React from 'react'; -import { View, StyleSheet, Text } from 'react-native'; +import { View, StyleSheet, Text, LayoutAnimation } from 'react-native'; const styles = StyleSheet.create({ firstUnread: { @@ -22,11 +22,16 @@ const styles = StyleSheet.create({ } }); -const UnreadSeparator = () => ( - - - unread messages - -); - -export default UnreadSeparator; +export default class UnreadSeparator extends React.PureComponent { + componentWillUnmount() { + LayoutAnimation.linear(); + } + render() { + return ( + + + unread messages + + ); + } +} diff --git a/app/views/RoomView/index.js b/app/views/RoomView/index.js index dd76d5d4..4d32ca07 100644 --- a/app/views/RoomView/index.js +++ b/app/views/RoomView/index.js @@ -1,23 +1,20 @@ import React from 'react'; import PropTypes from 'prop-types'; -import { Text, View, Button, SafeAreaView, Platform, Keyboard } from 'react-native'; +import { Text, View, Button, LayoutAnimation } from 'react-native'; import { connect } from 'react-redux'; import { bindActionCreators } from 'redux'; import equal from 'deep-equal'; -import KeyboardSpacer from 'react-native-keyboard-spacer'; import { List } from './ListView'; import * as actions from '../../actions'; import { openRoom, setLastOpen } from '../../actions/room'; import { editCancel, toggleReactionPicker } from '../../actions/messages'; -import { setKeyboardOpen, setKeyboardClosed } from '../../actions/keyboard'; import database from '../../lib/realm'; import RocketChat from '../../lib/rocketchat'; import Message from '../../containers/message'; import MessageActions from '../../containers/MessageActions'; import MessageErrorActions from '../../containers/MessageErrorActions'; import MessageBox from '../../containers/MessageBox'; - import Header from '../../containers/Header'; import RoomsHeader from './Header'; import ReactionPicker from './ReactionPicker'; @@ -30,16 +27,15 @@ import styles from './styles'; Message_TimeFormat: state.settings.Message_TimeFormat, loading: state.messages.isFetching, user: state.login.user, - actionMessage: state.messages.actionMessage + actionMessage: state.messages.actionMessage, + layoutAnimation: state.room.layoutAnimation }), dispatch => ({ actions: bindActionCreators(actions, dispatch), openRoom: room => dispatch(openRoom(room)), editCancel: () => dispatch(editCancel()), setLastOpen: date => dispatch(setLastOpen(date)), - toggleReactionPicker: message => dispatch(toggleReactionPicker(message)), - setKeyboardOpen: () => dispatch(setKeyboardOpen()), - setKeyboardClosed: () => dispatch(setKeyboardClosed()) + toggleReactionPicker: message => dispatch(toggleReactionPicker(message)) }) ) export default class RoomView extends React.Component { @@ -56,8 +52,7 @@ export default class RoomView extends React.Component { loading: PropTypes.bool, actionMessage: PropTypes.object, toggleReactionPicker: PropTypes.func.isRequired, - setKeyboardOpen: PropTypes.func, - setKeyboardClosed: PropTypes.func + layoutAnimation: PropTypes.instanceOf(Date) }; static navigationOptions = ({ navigation }) => ({ @@ -72,7 +67,6 @@ export default class RoomView extends React.Component { this.name = props.name || props.navigation.state.params.name || props.navigation.state.params.room.name; - this.opened = new Date(); this.rooms = database.objects('subscriptions').filtered('rid = $0', this.rid); this.state = { loaded: true, @@ -82,12 +76,12 @@ export default class RoomView extends React.Component { this.onReactionPress = this.onReactionPress.bind(this); } - componentWillMount() { + async componentWillMount() { this.props.navigation.setParams({ title: this.name }); this.updateRoom(); - this.props.openRoom({ rid: this.rid, name: this.name, ls: this.state.room.ls }); + await this.props.openRoom({ rid: this.rid, name: this.name, ls: this.state.room.ls }); if (this.state.room.alert || this.state.room.unread || this.state.room.userMentions) { this.props.setLastOpen(this.state.room.ls); } else { @@ -95,8 +89,11 @@ export default class RoomView extends React.Component { } this.rooms.addListener(this.updateRoom); - this.keyboardDidShowListener = Keyboard.addListener('keyboardDidShow', () => this.props.setKeyboardOpen()); - this.keyboardDidHideListener = Keyboard.addListener('keyboardDidHide', () => this.props.setKeyboardClosed()); + } + componentWillReceiveProps(nextProps) { + if (this.props.layoutAnimation !== nextProps.layoutAnimation) { + LayoutAnimation.spring(); + } } shouldComponentUpdate(nextProps, nextState) { return !(equal(this.props, nextProps) && equal(this.state, nextState)); @@ -104,8 +101,6 @@ export default class RoomView extends React.Component { componentWillUnmount() { clearTimeout(this.timer); this.rooms.removeAllListeners(); - this.keyboardDidShowListener.remove(); - this.keyboardDidHideListener.remove(); this.props.editCancel(); } @@ -141,9 +136,11 @@ export default class RoomView extends React.Component { this.setState({ room: this.rooms[0] }); } - sendMessage = message => RocketChat.sendMessage(this.rid, message).then(() => { - this.props.setLastOpen(null); - }); + sendMessage = (message) => { + RocketChat.sendMessage(this.rid, message).then(() => { + this.props.setLastOpen(null); + }); + }; joinRoom = async() => { await RocketChat.joinRoom(this.props.rid); @@ -157,7 +154,6 @@ export default class RoomView extends React.Component { key={item._id} item={item} reactions={JSON.parse(JSON.stringify(item.reactions))} - animate={this.opened.toISOString() < item.ts.toISOString()} baseUrl={this.props.Site_Url} Message_TimeFormat={this.props.Message_TimeFormat} user={this.props.user} @@ -196,20 +192,18 @@ export default class RoomView extends React.Component { return ( - - this.renderItem(item)} - /> - + this.renderItem(item)} + /> {this.renderFooter()} {this.state.room._id ? : null} - {Platform.OS === 'ios' ? : null} ); } diff --git a/app/views/RoomView/styles.js b/app/views/RoomView/styles.js index 6b68d233..089c56fc 100644 --- a/app/views/RoomView/styles.js +++ b/app/views/RoomView/styles.js @@ -1,4 +1,4 @@ -import { StyleSheet, Platform } from 'react-native'; +import { StyleSheet } from 'react-native'; export default StyleSheet.create({ typing: { fontWeight: 'bold', paddingHorizontal: 15, height: 25 }, @@ -37,7 +37,7 @@ export default StyleSheet.create({ reactionPickerContainer: { // width: width - 20, // height: width - 20, - paddingHorizontal: Platform.OS === 'android' ? 11 : 10, + // paddingHorizontal: Platform.OS === 'android' ? 11 : 10, backgroundColor: '#F7F7F7', borderRadius: 4, flexDirection: 'column' diff --git a/app/views/RoomsListView/Header/index.js b/app/views/RoomsListView/Header/index.js index 52b4d506..af91e2e8 100644 --- a/app/views/RoomsListView/Header/index.js +++ b/app/views/RoomsListView/Header/index.js @@ -20,7 +20,7 @@ import styles from './styles'; }), dispatch => ({ setSearch: searchText => dispatch(setSearch(searchText)) })) -export default class extends React.Component { +export default class RoomsListHeaderView extends React.Component { static propTypes = { navigation: PropTypes.object.isRequired, user: PropTypes.object.isRequired, diff --git a/app/views/SelectUsersView.js b/app/views/SelectUsersView.js index 667ecccf..17b90889 100644 --- a/app/views/SelectUsersView.js +++ b/app/views/SelectUsersView.js @@ -69,7 +69,7 @@ const ds = new ListView.DataSource({ rowHasChanged: (r1, r2) => r1 !== r2 }); resetCreateChannel: () => dispatch(createChannelActions.reset()) }) ) -export default class RoomsListView extends React.Component { +export default class SelectUsersView extends React.Component { static propTypes = { navigation: PropTypes.object.isRequired, Site_Url: PropTypes.string, diff --git a/index.android.js b/index.android.js index c7a8b8bc..4fade3c0 100644 --- a/index.android.js +++ b/index.android.js @@ -5,6 +5,8 @@ import { AppRegistry } from 'react-native'; import './app/push'; import RocketChat from './app/index'; +// UIManager.setLayoutAnimationEnabledExperimental(true); + // import './app/ReactotronConfig'; // import { AppRegistry } from 'react-native'; // import Routes from './app/routes'; diff --git a/ios/RocketChatRN.xcodeproj/project.pbxproj b/ios/RocketChatRN.xcodeproj/project.pbxproj index 167166fe..23a4c827 100644 --- a/ios/RocketChatRN.xcodeproj/project.pbxproj +++ b/ios/RocketChatRN.xcodeproj/project.pbxproj @@ -47,11 +47,13 @@ 647660C6B6A340C7BD4D1099 /* EvilIcons.ttf in Resources */ = {isa = PBXBuildFile; fileRef = A18EFC3B0CFE40E0918A8F0C /* EvilIcons.ttf */; }; 70A8D9B456894EFFAF027CAB /* FontAwesome.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 7A30DA4B2D474348824CD05B /* FontAwesome.ttf */; }; 77C35F50C01C43668188886C /* libRNVectorIcons.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5A0EEFAF8AB14F5B9E796CDD /* libRNVectorIcons.a */; }; + 7A430E4F20238C46008F55BC /* libRCTCustomInputController.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 7A430E1E20238C02008F55BC /* libRCTCustomInputController.a */; }; 832341BD1AAA6AB300B99B32 /* libRCTText.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 832341B51AAA6A8300B99B32 /* libRCTText.a */; }; 8A159EDB97C44E52AF62D69C /* libRNSVG.a in Frameworks */ = {isa = PBXBuildFile; fileRef = DA50CE47374C4C35BE6D9D58 /* libRNSVG.a */; }; 8ECBD927DDAC4987B98E102E /* libRCTVideo.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 20CE3E407E0D4D9E8C9885F2 /* libRCTVideo.a */; }; AE5D35882AE04CC29630FB3D /* Entypo.ttf in Resources */ = {isa = PBXBuildFile; fileRef = DC6EE17B5550465E98C70FF0 /* Entypo.ttf */; }; B88F586F1FBF57F600B352B8 /* libRCTPushNotification.a in Frameworks */ = {isa = PBXBuildFile; fileRef = B88F58461FBF55E200B352B8 /* libRCTPushNotification.a */; }; + B8971BB2202A093B0000D245 /* libKeyboardTrackingView.a in Frameworks */ = {isa = PBXBuildFile; fileRef = B8971BB1202A091D0000D245 /* libKeyboardTrackingView.a */; }; B8C682A81FD850F4003A12C8 /* icomoon.ttf in Resources */ = {isa = PBXBuildFile; fileRef = B8C682611FD84CEF003A12C8 /* icomoon.ttf */; }; B8C682AC1FD8511D003A12C8 /* Ionicons.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 1B0746E708284151B8AD1198 /* Ionicons.ttf */; }; B8C682AD1FD8511E003A12C8 /* Ionicons.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 1B0746E708284151B8AD1198 /* Ionicons.ttf */; }; @@ -296,6 +298,13 @@ remoteGlobalIDString = 134814201AA4EA6300B7C361; remoteInfo = RCTLinking; }; + 7A430E1D20238C02008F55BC /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 7A430E1620238C01008F55BC /* RCTCustomInputController.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 39DF4FE71E00394E00F5B4B2; + remoteInfo = RCTCustomInputController; + }; 7A7F5C981FCC982500024129 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = AD0379F2BCE84C968538CDAF /* RCTVideo.xcodeproj */; @@ -366,6 +375,13 @@ remoteGlobalIDString = 9936F32F1F5F2E5B0010BF04; remoteInfo = "privatedata-tvOS"; }; + B8971BB0202A091D0000D245 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = B8971BAC202A091D0000D245 /* KeyboardTrackingView.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = D834CED81CC64F2400FA5668; + remoteInfo = KeyboardTrackingView; + }; B8E79A8D1F3CCC6D005B464F /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 4CD38E4891ED4601B7481448 /* RNFetchBlob.xcodeproj */; @@ -436,6 +452,7 @@ 6533FB90166345D29F1B91C0 /* libRNFetchBlob.a */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = archive.ar; path = libRNFetchBlob.a; sourceTree = ""; }; 78C398B01ACF4ADC00677621 /* RCTLinking.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTLinking.xcodeproj; path = "../node_modules/react-native/Libraries/LinkingIOS/RCTLinking.xcodeproj"; sourceTree = ""; }; 7A30DA4B2D474348824CD05B /* FontAwesome.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = FontAwesome.ttf; path = "../node_modules/react-native-vector-icons/Fonts/FontAwesome.ttf"; sourceTree = ""; }; + 7A430E1620238C01008F55BC /* RCTCustomInputController.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTCustomInputController.xcodeproj; path = "../node_modules/react-native-keyboard-input/lib/ios/RCTCustomInputController.xcodeproj"; sourceTree = ""; }; 832341B01AAA6A8300B99B32 /* RCTText.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTText.xcodeproj; path = "../node_modules/react-native/Libraries/Text/RCTText.xcodeproj"; sourceTree = ""; }; 8A2DD67ADD954AD9873F45FC /* SimpleLineIcons.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = SimpleLineIcons.ttf; path = "../node_modules/react-native-vector-icons/Fonts/SimpleLineIcons.ttf"; sourceTree = ""; }; 9A1E1766CCB84C91A62BD5A6 /* Foundation.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = Foundation.ttf; path = "../node_modules/react-native-vector-icons/Fonts/Foundation.ttf"; sourceTree = ""; }; @@ -444,6 +461,7 @@ B2607FA180F14E6584301101 /* libSplashScreen.a */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = archive.ar; path = libSplashScreen.a; sourceTree = ""; }; B37C79D9BD0742CE936B6982 /* libc++.tbd */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = "libc++.tbd"; path = "usr/lib/libc++.tbd"; sourceTree = SDKROOT; }; B88F58361FBF55E200B352B8 /* RCTPushNotification.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTPushNotification.xcodeproj; path = "../node_modules/react-native/Libraries/PushNotificationIOS/RCTPushNotification.xcodeproj"; sourceTree = ""; }; + B8971BAC202A091D0000D245 /* KeyboardTrackingView.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = KeyboardTrackingView.xcodeproj; path = "../node_modules/react-native-keyboard-tracking-view/lib/KeyboardTrackingView.xcodeproj"; sourceTree = ""; }; B8C682611FD84CEF003A12C8 /* icomoon.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; name = icomoon.ttf; path = ../resources/fonts/icomoon.ttf; sourceTree = ""; }; BAAE4B947F5D44959F0A9D5A /* libRNZeroconf.a */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = archive.ar; path = libRNZeroconf.a; sourceTree = ""; }; C23AEF1D9EBE4A38A1A6B97B /* RNSVG.xcodeproj */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = "wrapper.pb-project"; name = RNSVG.xcodeproj; path = "../node_modules/react-native-svg/ios/RNSVG.xcodeproj"; sourceTree = ""; }; @@ -467,6 +485,8 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + B8971BB2202A093B0000D245 /* libKeyboardTrackingView.a in Frameworks */, + 7A430E4F20238C46008F55BC /* libRCTCustomInputController.a in Frameworks */, 146834051AC3E58100842450 /* libReact.a in Frameworks */, B88F586F1FBF57F600B352B8 /* libRCTPushNotification.a in Frameworks */, 5E9157361DD0AC6A00FF2AA8 /* libRCTAnimation.a in Frameworks */, @@ -674,6 +694,14 @@ name = Products; sourceTree = ""; }; + 7A430E1720238C01008F55BC /* Products */ = { + isa = PBXGroup; + children = ( + 7A430E1E20238C02008F55BC /* libRCTCustomInputController.a */, + ); + name = Products; + sourceTree = ""; + }; 7A7F5C831FCC982500024129 /* Products */ = { isa = PBXGroup; children = ( @@ -694,6 +722,8 @@ 832341AE1AAA6A7D00B99B32 /* Libraries */ = { isa = PBXGroup; children = ( + B8971BAC202A091D0000D245 /* KeyboardTrackingView.xcodeproj */, + 7A430E1620238C01008F55BC /* RCTCustomInputController.xcodeproj */, B88F58361FBF55E200B352B8 /* RCTPushNotification.xcodeproj */, 5E91572D1DD0AC6500FF2AA8 /* RCTAnimation.xcodeproj */, 146833FF1AC3E56700842450 /* React.xcodeproj */, @@ -780,6 +810,14 @@ name = Products; sourceTree = ""; }; + B8971BAD202A091D0000D245 /* Products */ = { + isa = PBXGroup; + children = ( + B8971BB1202A091D0000D245 /* libKeyboardTrackingView.a */, + ); + name = Products; + sourceTree = ""; + }; B8E79A681F3CCC69005B464F /* Recovered References */ = { isa = PBXGroup; children = ( @@ -952,6 +990,10 @@ productRefGroup = 83CBBA001A601CBA00E9B192 /* Products */; projectDirPath = ""; projectReferences = ( + { + ProductGroup = B8971BAD202A091D0000D245 /* Products */; + ProjectRef = B8971BAC202A091D0000D245 /* KeyboardTrackingView.xcodeproj */; + }, { ProductGroup = 00C302A81ABCB8CE00DB3ED1 /* Products */; ProjectRef = 00C302A71ABCB8CE00DB3ED1 /* RCTActionSheet.xcodeproj */; @@ -960,6 +1002,10 @@ ProductGroup = 5E91572E1DD0AC6500FF2AA8 /* Products */; ProjectRef = 5E91572D1DD0AC6500FF2AA8 /* RCTAnimation.xcodeproj */; }, + { + ProductGroup = 7A430E1720238C01008F55BC /* Products */; + ProjectRef = 7A430E1620238C01008F55BC /* RCTCustomInputController.xcodeproj */; + }, { ProductGroup = 00C302B61ABCB90400DB3ED1 /* Products */; ProjectRef = 00C302B51ABCB90400DB3ED1 /* RCTGeolocation.xcodeproj */; @@ -1261,6 +1307,13 @@ remoteRef = 78C398B81ACF4ADC00677621 /* PBXContainerItemProxy */; sourceTree = BUILT_PRODUCTS_DIR; }; + 7A430E1E20238C02008F55BC /* libRCTCustomInputController.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = libRCTCustomInputController.a; + remoteRef = 7A430E1D20238C02008F55BC /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; 7A7F5C991FCC982500024129 /* libRCTVideo.a */ = { isa = PBXReferenceProxy; fileType = archive.ar; @@ -1331,6 +1384,13 @@ remoteRef = B88F58661FBF55E200B352B8 /* PBXContainerItemProxy */; sourceTree = BUILT_PRODUCTS_DIR; }; + B8971BB1202A091D0000D245 /* libKeyboardTrackingView.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = libKeyboardTrackingView.a; + remoteRef = B8971BB0202A091D0000D245 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; B8E79A8E1F3CCC6D005B464F /* libRNFetchBlob.a */ = { isa = PBXReferenceProxy; fileType = archive.ar; diff --git a/package-lock.json b/package-lock.json index fb9a3f7a..57ab9160 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12568,10 +12568,20 @@ "react-native-iphone-x-helper": "1.0.1" } }, - "react-native-keyboard-spacer": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/react-native-keyboard-spacer/-/react-native-keyboard-spacer-0.4.1.tgz", - "integrity": "sha1-RvGKMgQyCYol6p+on1FD3SVNMy0=" + "react-native-keyboard-input": { + "version": "git+https://github.com/RocketChat/react-native-keyboard-input.git#38273b0513f69a5e6e0719f65a675f9f2b5ee883", + "requires": { + "lodash": "4.17.4", + "react-native-keyboard-tracking-view": "git+https://github.com/RocketChat/react-native-keyboard-tracking-view.git#3a4084f0a1063e23ae6435facdf1f79152558d15" + }, + "dependencies": { + "react-native-keyboard-tracking-view": { + "version": "git+https://github.com/RocketChat/react-native-keyboard-tracking-view.git#3a4084f0a1063e23ae6435facdf1f79152558d15" + } + } + }, + "react-native-keyboard-tracking-view": { + "version": "git+https://github.com/RocketChat/react-native-keyboard-tracking-view.git#3a4084f0a1063e23ae6435facdf1f79152558d15" }, "react-native-loading-spinner-overlay": { "version": "0.5.2", @@ -12624,9 +12634,9 @@ } }, "react-native-optimized-flatlist": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/react-native-optimized-flatlist/-/react-native-optimized-flatlist-1.0.3.tgz", - "integrity": "sha1-tFN58lpXu05vhZwZDZmEexgR4Ak=", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/react-native-optimized-flatlist/-/react-native-optimized-flatlist-1.0.4.tgz", + "integrity": "sha512-PMoZRJAHKzd/ahYKUzt43AJ+kVhHpOSTvBhJdQqooZXw312xADWpR7iDvBAbBiRGkmk0yM4GJacd9TMft6q/Gg==", "requires": { "prop-types": "15.6.0" } diff --git a/package.json b/package.json index e6bc7f8d..8cf7d9a1 100644 --- a/package.json +++ b/package.json @@ -45,11 +45,12 @@ "react-native-image-picker": "^0.26.7", "react-native-img-cache": "^1.5.2", "react-native-keyboard-aware-scroll-view": "^0.4.1", - "react-native-keyboard-spacer": "^0.4.1", + "react-native-keyboard-input": "git+https://github.com/RocketChat/react-native-keyboard-input.git", + "react-native-keyboard-tracking-view": "git+https://github.com/RocketChat/react-native-keyboard-tracking-view.git", "react-native-loading-spinner-overlay": "^0.5.2", "react-native-meteor": "^1.2.0", "react-native-modal": "^4.1.1", - "react-native-optimized-flatlist": "^1.0.3", + "react-native-optimized-flatlist": "^1.0.4", "react-native-push-notification": "^3.0.1", "react-native-responsive-ui": "^1.1.1", "react-native-scrollable-tab-view": "^0.8.0",