Improvements on emoji picker / message box (#227)

* Emoji keyboard

* Keyboard emoji working

* animation and flatlist

* fix

* Unread separator animation

* easeInEaseOut animation
This commit is contained in:
Diego Mello 2018-02-08 12:08:50 -02:00 committed by Guilherme Gazzo
parent d270240a6d
commit ed5a1386e0
44 changed files with 336 additions and 348 deletions

View File

@ -144,6 +144,7 @@ android {
} }
dependencies { dependencies {
compile project(":reactnativekeyboardinput")
compile project(':react-native-splash-screen') compile project(':react-native-splash-screen')
compile project(':react-native-video') compile project(':react-native-video')
compile project(':react-native-push-notification') compile project(':react-native-push-notification')

View File

@ -16,6 +16,7 @@ import com.facebook.soloader.SoLoader;
import com.dieam.reactnativepushnotification.ReactNativePushNotificationPackage; import com.dieam.reactnativepushnotification.ReactNativePushNotificationPackage;
import com.brentvatne.react.ReactVideoPackage; import com.brentvatne.react.ReactVideoPackage;
import com.remobile.toast.RCTToastPackage; import com.remobile.toast.RCTToastPackage;
import com.wix.reactnativekeyboardinput.KeyboardInputPackage;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
@ -33,16 +34,17 @@ public class MainApplication extends Application implements ReactApplication {
protected List<ReactPackage> getPackages() { protected List<ReactPackage> getPackages() {
return Arrays.<ReactPackage>asList( return Arrays.<ReactPackage>asList(
new MainReactPackage(), new MainReactPackage(),
new SvgPackage(), new SvgPackage(),
new ImagePickerPackage(), new ImagePickerPackage(),
new VectorIconsPackage(), new VectorIconsPackage(),
new RNFetchBlobPackage(), new RNFetchBlobPackage(),
new ZeroconfReactPackage(), new ZeroconfReactPackage(),
new RealmReactPackage(), new RealmReactPackage(),
new ReactNativePushNotificationPackage(), new ReactNativePushNotificationPackage(),
new ReactVideoPackage(), new ReactVideoPackage(),
new SplashScreenReactPackage(), new SplashScreenReactPackage(),
new RCTToastPackage() new RCTToastPackage(),
new KeyboardInputPackage(MainApplication.this)
); );
} }
}; };

View File

@ -1,4 +1,6 @@
rootProject.name = 'RocketChatRN' 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' include ':react-native-splash-screen'
project(':react-native-splash-screen').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-splash-screen/android') project(':react-native-splash-screen').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-splash-screen/android')
include ':react-native-video' include ':react-native-video'

View File

@ -28,7 +28,17 @@ export const FORGOT_PASSWORD = createRequestTypes('FORGOT_PASSWORD', [
]); ]);
export const USER = createRequestTypes('USER', ['SET']); export const USER = createRequestTypes('USER', ['SET']);
export const ROOMS = createRequestTypes('ROOMS', [...defaultTypes, 'SET_SEARCH']); 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 APP = createRequestTypes('APP', ['READY', 'INIT']);
export const MESSAGES = createRequestTypes('MESSAGES', [ export const MESSAGES = createRequestTypes('MESSAGES', [
...defaultTypes, ...defaultTypes,
@ -82,4 +92,4 @@ export const ACTIVE_USERS = createRequestTypes('ACTIVE_USERS', ['SET', 'REQUEST'
export const INCREMENT = 'INCREMENT'; export const INCREMENT = 'INCREMENT';
export const DECREMENT = 'DECREMENT'; export const DECREMENT = 'DECREMENT';
export const KEYBOARD = createRequestTypes('KEYBOARD', ['OPEN', 'CLOSE']);

View File

@ -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
};
}

View File

@ -55,3 +55,9 @@ export function setLastOpen(date = new Date()) {
date date
}; };
} }
export function layoutAnimation() {
return {
type: types.ROOM.LAYOUT_ANIMATION
};
}

View File

@ -42,7 +42,8 @@ export default class Panel extends React.Component {
this.state.animation, this.state.animation,
{ {
toValue: finalValue, toValue: finalValue,
duration: 150 duration: 150,
useNativeDriver: true
} }
).start(); ).start();
} }

View File

@ -29,7 +29,8 @@ export default class Fade extends React.Component {
} }
Animated.timing(this._visibility, { Animated.timing(this._visibility, {
toValue: nextProps.visible ? 1 : 0, toValue: nextProps.visible ? 1 : 0,
duration: 300 duration: 300,
useNativeDriver: true
}).start(() => { }).start(() => {
this.setState({ visible: nextProps.visible }); this.setState({ visible: nextProps.visible });
}); });

View File

@ -1,7 +1,7 @@
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { connect } from 'react-redux'; 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 { CachedImage } from 'react-native-img-cache';
import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons'; import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons';
import avatarInitialsAndColor from '../utils/avatarInitialsAndColor'; import avatarInitialsAndColor from '../utils/avatarInitialsAndColor';
@ -23,8 +23,16 @@ const styles = StyleSheet.create({
@connect(state => ({ @connect(state => ({
baseUrl: state.settings.Site_Url || state.server ? state.server.server : '' baseUrl: state.settings.Site_Url || state.server ? state.server.server : ''
})) }))
export default class Avatar extends React.PureComponent {
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() { render() {
const { const {
text = '', size = 25, baseUrl, borderRadius = 4, style, avatar, type = 'd' 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;

View File

@ -1,4 +1,5 @@
import React from 'react'; import React from 'react';
import { ViewPropTypes } from 'react-native';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { CachedImage } from 'react-native-img-cache'; import { CachedImage } from 'react-native-img-cache';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
@ -6,11 +7,11 @@ import { connect } from 'react-redux';
@connect(state => ({ @connect(state => ({
baseUrl: state.settings.Site_Url baseUrl: state.settings.Site_Url
})) }))
export default class extends React.Component { export default class CustomEmoji extends React.Component {
static propTypes = { static propTypes = {
baseUrl: PropTypes.string.isRequired, baseUrl: PropTypes.string.isRequired,
emoji: PropTypes.object.isRequired, emoji: PropTypes.object.isRequired,
style: PropTypes.object style: ViewPropTypes.style
} }
shouldComponentUpdate() { shouldComponentUpdate() {
return false; return false;

View File

@ -1,11 +1,12 @@
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types'; 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 { emojify } from 'react-emojione';
import { responsive } from 'react-native-responsive-ui'; import { responsive } from 'react-native-responsive-ui';
import { OptimizedFlatList } from 'react-native-optimized-flatlist';
import styles from './styles'; import styles from './styles';
import CustomEmoji from './CustomEmoji'; import CustomEmoji from './CustomEmoji';
import scrollPersistTaps from '../../utils/scrollPersistTaps';
const emojisPerRow = Platform.OS === 'ios' ? 8 : 9; const emojisPerRow = Platform.OS === 'ios' ? 8 : 9;
@ -21,8 +22,6 @@ const renderEmoji = (emoji, size) => {
}; };
const nextFrame = () => new Promise(resolve => requestAnimationFrame(resolve));
@responsive @responsive
export default class EmojiCategory extends React.Component { export default class EmojiCategory extends React.Component {
static propTypes = { static propTypes = {
@ -40,23 +39,7 @@ export default class EmojiCategory extends React.Component {
this.emojis = []; this.emojis = [];
} }
componentWillMount() { componentWillMount() {
this.emojis = this.props.emojis.slice(0, emojisPerRow * 3).map(item => this.renderItem(item, this.size)); this.emojis = this.props.emojis;
}
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();
});
} }
shouldComponentUpdate() { shouldComponentUpdate() {
@ -75,6 +58,17 @@ export default class EmojiCategory extends React.Component {
} }
render() { render() {
return <View style={styles.categoryInner}>{this.emojis}</View>; return (
<OptimizedFlatList
keyExtractor={item => (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}
/>
);
} }
} }

View File

@ -3,7 +3,7 @@ import PropTypes from 'prop-types';
import { View, TouchableOpacity, Text } from 'react-native'; import { View, TouchableOpacity, Text } from 'react-native';
import styles from './styles'; import styles from './styles';
export default class extends React.PureComponent { export default class TabBar extends React.PureComponent {
static propTypes = { static propTypes = {
goToPage: PropTypes.func, goToPage: PropTypes.func,
activeTab: PropTypes.number, activeTab: PropTypes.number,

View File

@ -8,15 +8,15 @@ import TabBar from './TabBar';
import EmojiCategory from './EmojiCategory'; import EmojiCategory from './EmojiCategory';
import styles from './styles'; import styles from './styles';
import categories from './categories'; import categories from './categories';
import scrollPersistTaps from '../../utils/scrollPersistTaps';
import database from '../../lib/realm'; import database from '../../lib/realm';
import { emojisByCategory } from '../../emojis'; import { emojisByCategory } from '../../emojis';
const scrollProps = { const scrollProps = {
keyboardShouldPersistTaps: 'always' keyboardShouldPersistTaps: 'always',
keyboardDismissMode: 'none'
}; };
export default class extends Component { export default class EmojiPicker extends Component {
static propTypes = { static propTypes = {
onEmojiSelected: PropTypes.func, onEmojiSelected: PropTypes.func,
tabEmojiStyle: PropTypes.object, tabEmojiStyle: PropTypes.object,
@ -45,9 +45,10 @@ export default class extends Component {
this.customEmojis.addListener(this.updateCustomEmojis); this.customEmojis.addListener(this.updateCustomEmojis);
this.updateFrequentlyUsed(); this.updateFrequentlyUsed();
this.updateCustomEmojis(); this.updateCustomEmojis();
setTimeout(() => this.setState({ show: true }), 100);
} }
componentDidMount() {
requestAnimationFrame(() => this.setState({ show: true }));
}
componentWillUnmount() { componentWillUnmount() {
this.frequentlyUsed.removeAllListeners(); this.frequentlyUsed.removeAllListeners();
this.customEmojis.removeAllListeners(); this.customEmojis.removeAllListeners();
@ -122,13 +123,14 @@ export default class extends Component {
<ScrollableTabView <ScrollableTabView
renderTabBar={() => <TabBar tabEmojiStyle={this.props.tabEmojiStyle} />} renderTabBar={() => <TabBar tabEmojiStyle={this.props.tabEmojiStyle} />}
contentProps={scrollProps} contentProps={scrollProps}
// prerenderingSiblingsNumber={1}
> >
{ {
categories.tabs.map((tab, i) => ( categories.tabs.map((tab, i) => (
<ScrollView <ScrollView
key={tab.category} key={tab.category}
tabLabel={tab.tabLabel} tabLabel={tab.tabLabel}
{...scrollPersistTaps} {...scrollProps}
> >
{this.renderCategory(tab.category, i)} {this.renderCategory(tab.category, i)}
</ScrollView> </ScrollView>

View File

@ -33,7 +33,7 @@ const styles = StyleSheet.create({
} }
}); });
export default class extends React.PureComponent { export default class Header extends React.PureComponent {
static propTypes = { static propTypes = {
subview: PropTypes.object.isRequired subview: PropTypes.object.isRequired
} }

View File

@ -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 (
<Animated.View
style={{
position: 'absolute',
left: 0,
right: 0,
bottom: -200,
zIndex: 1,
transform: [{ translateY: bottom }]
}}
>
{this.props.subview}
</Animated.View>
);
}
}

View File

@ -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 (
<Provider store={store}>
<View style={styles.emojiKeyboardContainer}>
<EmojiPicker onEmojiSelected={emoji => this.onEmojiSelected(emoji)} />
</View>
</Provider>
);
}
}
KeyboardRegistry.registerKeyboard('EmojiKeyboard', () => EmojiKeyboard);

View File

@ -1,22 +1,21 @@
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types'; 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 Icon from 'react-native-vector-icons/MaterialIcons';
import ImagePicker from 'react-native-image-picker'; import ImagePicker from 'react-native-image-picker';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { emojify } from 'react-emojione'; 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 RocketChat from '../../lib/rocketchat';
import { editRequest, editCancel, clearInput } from '../../actions/messages'; import { editRequest, editCancel, clearInput } from '../../actions/messages';
import styles from './style'; import styles from './styles';
import MyIcon from '../icons'; import MyIcon from '../icons';
import database from '../../lib/realm'; import database from '../../lib/realm';
import Avatar from '../Avatar'; import Avatar from '../Avatar';
import CustomEmoji from '../EmojiPicker/CustomEmoji'; import CustomEmoji from '../EmojiPicker/CustomEmoji';
import AnimatedContainer from './AnimatedContainer';
import EmojiPicker from '../EmojiPicker';
import scrollPersistTaps from '../../utils/scrollPersistTaps';
import { emojis } from '../../emojis'; import { emojis } from '../../emojis';
import './EmojiKeyboard';
const MENTIONS_TRACKING_TYPE_USERS = '@'; const MENTIONS_TRACKING_TYPE_USERS = '@';
const MENTIONS_TRACKING_TYPE_EMOJIS = ':'; const MENTIONS_TRACKING_TYPE_EMOJIS = ':';
@ -29,13 +28,13 @@ const onlyUnique = function onlyUnique(value, index, self) {
room: state.room, room: state.room,
message: state.messages.message, message: state.messages.message,
editing: state.messages.editing, editing: state.messages.editing,
baseUrl: state.settings.Site_Url || state.server ? state.server.server : '', baseUrl: state.settings.Site_Url || state.server ? state.server.server : ''
isKeyboardOpen: state.keyboard.isOpen
}), dispatch => ({ }), dispatch => ({
editCancel: () => dispatch(editCancel()), editCancel: () => dispatch(editCancel()),
editRequest: message => dispatch(editRequest(message)), editRequest: message => dispatch(editRequest(message)),
typing: status => dispatch(userTyping(status)), typing: status => dispatch(userTyping(status)),
clearInput: () => dispatch(clearInput()) clearInput: () => dispatch(clearInput()),
layoutAnimation: () => dispatch(layoutAnimation())
})) }))
export default class MessageBox extends React.PureComponent { export default class MessageBox extends React.PureComponent {
static propTypes = { static propTypes = {
@ -48,23 +47,23 @@ export default class MessageBox extends React.PureComponent {
editing: PropTypes.bool, editing: PropTypes.bool,
typing: PropTypes.func, typing: PropTypes.func,
clearInput: PropTypes.func, clearInput: PropTypes.func,
isKeyboardOpen: PropTypes.bool layoutAnimation: PropTypes.func
} }
constructor(props) { constructor(props) {
super(props); super(props);
this.state = { this.state = {
messageboxHeight: 0,
text: '', text: '',
mentions: [], mentions: [],
showMentionsContainer: false, showMentionsContainer: false,
showEmojiContainer: false, showEmojiKeyboard: false,
trackingType: '' trackingType: ''
}; };
this.users = []; this.users = [];
this.rooms = []; this.rooms = [];
this.emojis = []; this.emojis = [];
this.customEmojis = []; this.customEmojis = [];
this._onEmojiSelected = this._onEmojiSelected.bind(this);
} }
componentWillReceiveProps(nextProps) { componentWillReceiveProps(nextProps) {
if (this.props.message !== nextProps.message && nextProps.message.msg) { if (this.props.message !== nextProps.message && nextProps.message.msg) {
@ -72,8 +71,6 @@ export default class MessageBox extends React.PureComponent {
this.component.focus(); this.component.focus();
} else if (!nextProps.message) { } else if (!nextProps.message) {
this.setState({ text: '' }); 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() { get leftButtons() {
const { editing } = this.props; const { editing } = this.props;
if (editing) { if (editing) {
@ -111,7 +112,7 @@ export default class MessageBox extends React.PureComponent {
onPress={() => this.editCancel()} onPress={() => this.editCancel()}
/>); />);
} }
return !this.state.showEmojiContainer ? (<Icon return !this.state.showEmojiKeyboard ? (<Icon
style={styles.actionButtons} style={styles.actionButtons}
onPress={() => this.openEmoji()} onPress={() => this.openEmoji()}
accessibilityLabel='Open emoji selector' accessibilityLabel='Open emoji selector'
@ -184,13 +185,11 @@ export default class MessageBox extends React.PureComponent {
} }
async openEmoji() { async openEmoji() {
await this.setState({ await this.setState({
showEmojiContainer: true, showEmojiKeyboard: true
showMentionsContainer: false
}); });
Keyboard.dismiss();
} }
closeEmoji() { closeEmoji() {
this.setState({ showEmojiContainer: false }); this.setState({ showEmojiKeyboard: false });
} }
submit(message) { submit(message) {
this.setState({ text: '' }); this.setState({ text: '' });
@ -324,9 +323,12 @@ export default class MessageBox extends React.PureComponent {
} }
identifyMentionKeyword(keyword, type) { identifyMentionKeyword(keyword, type) {
if (!this.state.showMentionsContainer) {
this.props.layoutAnimation();
}
this.setState({ this.setState({
showMentionsContainer: true, showMentionsContainer: true,
showEmojiContainer: false, showEmojiKeyboard: false,
trackingType: type trackingType: type
}); });
this.updateMentions(keyword, type); this.updateMentions(keyword, type);
@ -360,8 +362,9 @@ export default class MessageBox extends React.PureComponent {
this.component.focus(); this.component.focus();
requestAnimationFrame(() => this.stopTrackingMention()); requestAnimationFrame(() => this.stopTrackingMention());
} }
_onEmojiSelected(emoji) { _onEmojiSelected(keyboardId, params) {
const { text } = this.state; const { text } = this.state;
const { emoji } = params;
let newText = ''; let newText = '';
// if messagebox has an active cursor // if messagebox has an active cursor
@ -390,7 +393,7 @@ export default class MessageBox extends React.PureComponent {
return ( return (
<CustomEmoji <CustomEmoji
key='mention-item-avatar' key='mention-item-avatar'
style={[styles.mentionItemCustomEmoji]} style={styles.mentionItemCustomEmoji}
emoji={item} emoji={item}
baseUrl={this.props.baseUrl} baseUrl={this.props.baseUrl}
/> />
@ -399,7 +402,7 @@ export default class MessageBox extends React.PureComponent {
return ( return (
<Text <Text
key='mention-item-avatar' key='mention-item-avatar'
style={[StyleSheet.flatten(styles.mentionItemEmoji)]} style={styles.mentionItemEmoji}
> >
{emojify(`:${ item }:`, { output: 'unicode' })} {emojify(`:${ item }:`, { output: 'unicode' })}
</Text> </Text>
@ -433,34 +436,24 @@ export default class MessageBox extends React.PureComponent {
</TouchableOpacity> </TouchableOpacity>
); );
} }
renderEmoji() { renderMentions = () => (
const emojiContainer = ( <FlatList
<View style={styles.emojiContainer}> key='messagebox-container'
<EmojiPicker onEmojiSelected={emoji => this._onEmojiSelected(emoji)} /> style={styles.mentionList}
</View> data={this.state.mentions}
); renderItem={({ item }) => this.renderMentionItem(item)}
const { showEmojiContainer, messageboxHeight } = this.state; keyExtractor={item => item._id || item}
return <AnimatedContainer visible={showEmojiContainer} subview={emojiContainer} messageboxHeight={messageboxHeight} />; keyboardShouldPersistTaps='always'
} />
renderMentions() { );
const list = (
<FlatList renderContent() {
style={styles.mentionList}
data={this.state.mentions}
renderItem={({ item }) => this.renderMentionItem(item)}
keyExtractor={item => item._id || item}
{...scrollPersistTaps}
/>
);
const { showMentionsContainer, messageboxHeight } = this.state;
return <AnimatedContainer visible={showMentionsContainer} subview={list} messageboxHeight={messageboxHeight} />;
}
render() {
return ( return (
<View> [
this.renderMentions(),
<SafeAreaView <SafeAreaView
key='messagebox'
style={[styles.textBox, (this.props.editing ? styles.editing : null)]} style={[styles.textBox, (this.props.editing ? styles.editing : null)]}
onLayout={event => this.setState({ messageboxHeight: event.nativeEvent.layout.height })}
> >
<View style={styles.textArea}> <View style={styles.textArea}>
{this.leftButtons} {this.leftButtons}
@ -480,9 +473,22 @@ export default class MessageBox extends React.PureComponent {
{this.rightButtons} {this.rightButtons}
</View> </View>
</SafeAreaView> </SafeAreaView>
{this.renderMentions()} ]
{this.renderEmoji()} );
</View> }
render() {
return (
<KeyboardAccessoryView
renderContent={() => this.renderContent()}
kbInputRef={this.component}
kbComponent={this.state.showEmojiKeyboard ? 'EmojiKeyboard' : null}
onKeyboardResigned={() => this.onKeyboardResigned()}
onItemSelected={this._onEmojiSelected}
trackInteractive
revealKeyboardInteractive
requiresSameParentToManageScrollView
/>
); );
} }
} }

View File

@ -21,13 +21,14 @@ export default StyleSheet.create({
flexGrow: 0 flexGrow: 0
}, },
textBoxInput: { textBoxInput: {
paddingHorizontal: 5,
textAlignVertical: 'center', textAlignVertical: 'center',
maxHeight: 120, maxHeight: 120,
flexGrow: 1, flexGrow: 1,
width: 1, width: 1,
paddingTop: 15, paddingTop: 15,
paddingBottom: 15 paddingBottom: 15,
paddingLeft: 0,
paddingRight: 0
}, },
editing: { editing: {
backgroundColor: '#fff5df' backgroundColor: '#fff5df'
@ -37,6 +38,7 @@ export default StyleSheet.create({
fontSize: 20, fontSize: 20,
textAlign: 'center', textAlign: 'center',
padding: 15, padding: 15,
paddingHorizontal: 21,
flex: 0 flex: 0
}, },
actionRow: { actionRow: {
@ -98,5 +100,10 @@ export default StyleSheet.create({
fontWeight: 'bold', fontWeight: 'bold',
textAlign: 'center', textAlign: 'center',
width: 46 width: 46
},
emojiKeyboardContainer: {
flex: 1,
borderTopColor: '#ECECEC',
borderTopWidth: 1
} }
}); });

View File

@ -16,7 +16,7 @@ import database from '../lib/realm';
errorActionsHide: () => dispatch(errorActionsHide()) errorActionsHide: () => dispatch(errorActionsHide())
}) })
) )
export default class MessageActions extends React.Component { export default class MessageErrorActions extends React.Component {
static propTypes = { static propTypes = {
errorActionsHide: PropTypes.func.isRequired, errorActionsHide: PropTypes.func.isRequired,
showErrorActions: PropTypes.bool.isRequired, showErrorActions: PropTypes.bool.isRequired,

View File

@ -1,7 +1,6 @@
import React, { Component } from 'react'; import React, { Component } from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { ScrollView, Text, View, StyleSheet, FlatList, TouchableHighlight } from 'react-native'; import { ScrollView, Text, View, StyleSheet, FlatList, TouchableHighlight } from 'react-native';
import { DrawerItems } from 'react-navigation';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import database from '../lib/realm'; import database from '../lib/realm';
@ -101,10 +100,6 @@ export default class Sidebar extends Component {
return ( return (
<ScrollView style={styles.scrollView}> <ScrollView style={styles.scrollView}>
<View style={{ paddingBottom: 20 }}> <View style={{ paddingBottom: 20 }}>
<DrawerItems
{...this.props}
onItemPress={this.onItemPress}
/>
<FlatList <FlatList
data={this.state.servers} data={this.state.servers}
renderItem={this.renderItem} renderItem={this.renderItem}

View File

@ -1,5 +1,5 @@
import React from 'react'; import React from 'react';
import { Text } from 'react-native'; import { Text, ViewPropTypes } from 'react-native';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { emojify } from 'react-emojione'; import { emojify } from 'react-emojione';
import CustomEmoji from '../EmojiPicker/CustomEmoji'; import CustomEmoji from '../EmojiPicker/CustomEmoji';
@ -7,8 +7,8 @@ import CustomEmoji from '../EmojiPicker/CustomEmoji';
export default class Emoji extends React.PureComponent { export default class Emoji extends React.PureComponent {
static propTypes = { static propTypes = {
content: PropTypes.string, content: PropTypes.string,
standardEmojiStyle: PropTypes.object, standardEmojiStyle: Text.propTypes.style,
customEmojiStyle: PropTypes.object, customEmojiStyle: ViewPropTypes.style,
customEmojis: PropTypes.object.isRequired customEmojis: PropTypes.object.isRequired
}; };
render() { render() {

View File

@ -39,7 +39,7 @@ const styles = StyleSheet.create({
} }
}); });
export default class extends React.PureComponent { export default class Image extends React.PureComponent {
static propTypes = { static propTypes = {
file: PropTypes.object.isRequired, file: PropTypes.object.isRequired,
baseUrl: PropTypes.string.isRequired, baseUrl: PropTypes.string.isRequired,

View File

@ -111,9 +111,8 @@ const Markdown = ({ msg, customEmojis }) => {
const emojiExtension = customEmojis[content]; const emojiExtension = customEmojis[content];
if (emojiExtension) { if (emojiExtension) {
const emoji = { extension: emojiExtension, content }; const emoji = { extension: emojiExtension, content };
const style = StyleSheet.flatten(styles.customEmoji);
element.props.children = ( element.props.children = (
<CustomEmoji key={state.key} style={style} emoji={emoji} /> <CustomEmoji key={state.key} style={styles.customEmoji} emoji={emoji} />
); );
} }
return element; return element;

View File

@ -25,7 +25,7 @@ const styles = {
} }
}; };
export default class extends React.PureComponent { export default class PhotoModal extends React.PureComponent {
static propTypes = { static propTypes = {
title: PropTypes.string.isRequired, title: PropTypes.string.isRequired,
image: PropTypes.string.isRequired, image: PropTypes.string.isRequired,

View File

@ -52,7 +52,7 @@ const styles = StyleSheet.create({
}); });
const standardEmojiStyle = { fontSize: 20 }; const standardEmojiStyle = { fontSize: 20 };
const customEmojiStyle = { width: 20, height: 20 }; const customEmojiStyle = { width: 20, height: 20 };
export default class extends React.PureComponent { export default class ReactionsModal extends React.PureComponent {
static propTypes = { static propTypes = {
isVisible: PropTypes.bool.isRequired, isVisible: PropTypes.bool.isRequired,
onClose: PropTypes.func.isRequired, onClose: PropTypes.func.isRequired,

View File

@ -1,10 +1,11 @@
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types'; 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 { connect } from 'react-redux';
import Icon from 'react-native-vector-icons/MaterialIcons'; import Icon from 'react-native-vector-icons/MaterialIcons';
import moment from 'moment'; import moment from 'moment';
import equal from 'deep-equal'; import equal from 'deep-equal';
import { KeyboardUtils } from 'react-native-keyboard-input';
import { actionsShow, errorActionsShow, toggleReactionPicker } from '../../actions/messages'; import { actionsShow, errorActionsShow, toggleReactionPicker } from '../../actions/messages';
import Image from './Image'; import Image from './Image';
@ -20,9 +21,6 @@ import Emoji from './Emoji';
import messageStatus from '../../constants/messagesStatus'; import messageStatus from '../../constants/messagesStatus';
import styles from './styles'; import styles from './styles';
const avatar = { marginRight: 10 };
const flex = { flexDirection: 'row', flex: 1 };
@connect(state => ({ @connect(state => ({
message: state.messages.message, message: state.messages.message,
editing: state.messages.editing, editing: state.messages.editing,
@ -43,7 +41,6 @@ export default class Message extends React.Component {
editing: PropTypes.bool, editing: PropTypes.bool,
actionsShow: PropTypes.func, actionsShow: PropTypes.func,
errorActionsShow: PropTypes.func, errorActionsShow: PropTypes.func,
animate: PropTypes.bool,
customEmojis: PropTypes.object, customEmojis: PropTypes.object,
toggleReactionPicker: PropTypes.func, toggleReactionPicker: PropTypes.func,
onReactionPress: PropTypes.func onReactionPress: PropTypes.func
@ -54,18 +51,6 @@ export default class Message extends React.Component {
this.state = { reactionsModal: false }; this.state = { reactionsModal: false };
this.onClose = this.onClose.bind(this); 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() { componentWillReceiveProps() {
this.extraStyle = this.extraStyle || {}; this.extraStyle = this.extraStyle || {};
if (this.props.item.status === messageStatus.TEMP || this.props.item.status === messageStatus.ERROR) { 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 = () => { onPress = () => {
Keyboard.dismiss(); KeyboardUtils.dismiss();
} }
onLongPress() { onLongPress() {
@ -207,8 +192,8 @@ export default class Message extends React.Component {
<View style={[styles.reactionContainer, reactedContainerStyle]}> <View style={[styles.reactionContainer, reactedContainerStyle]}>
<Emoji <Emoji
content={reaction.emoji} content={reaction.emoji}
standardEmojiStyle={StyleSheet.flatten(styles.reactionEmoji)} standardEmojiStyle={styles.reactionEmoji}
customEmojiStyle={StyleSheet.flatten(styles.reactionCustomEmoji)} customEmojiStyle={styles.reactionCustomEmoji}
customEmojis={this.props.customEmojis} customEmojis={this.props.customEmojis}
/> />
<Text style={[styles.reactionCount, reactedCount]}>{ reaction.usernames.length }</Text> <Text style={[styles.reactionCount, reactedCount]}>{ reaction.usernames.length }</Text>
@ -239,18 +224,8 @@ export default class Message extends React.Component {
const { const {
item, message, editing, baseUrl, customEmojis item, message, editing, baseUrl, customEmojis
} = this.props; } = 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 username = item.alias || item.u.username;
const isEditing = message._id === item._id && editing; 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 }`; const accessibilityLabel = `Message from ${ username } at ${ moment(item.ts).format(this.props.Message_TimeFormat) }, ${ this.props.item.msg }`;
return ( return (
@ -263,11 +238,11 @@ export default class Message extends React.Component {
style={[styles.message, isEditing ? styles.editing : null]} style={[styles.message, isEditing ? styles.editing : null]}
accessibilityLabel={accessibilityLabel} accessibilityLabel={accessibilityLabel}
> >
<Animated.View style={[flex, { opacity, marginLeft }]}> <View style={styles.flex}>
{this.renderError()} {this.renderError()}
<View style={[this.extraStyle, flex]}> <View style={[this.extraStyle, styles.flex]}>
<Avatar <Avatar
style={avatar} style={styles.avatar}
text={item.avatar ? '' : username} text={item.avatar ? '' : username}
size={40} size={40}
baseUrl={baseUrl} baseUrl={baseUrl}
@ -296,7 +271,7 @@ export default class Message extends React.Component {
/> />
: null : null
} }
</Animated.View> </View>
</TouchableHighlight> </TouchableHighlight>
); );
} }

View File

@ -5,6 +5,10 @@ export default StyleSheet.create({
flexGrow: 1, flexGrow: 1,
flexShrink: 1 flexShrink: 1
}, },
flex: {
flexDirection: 'row',
flex: 1
},
message: { message: {
padding: 12, padding: 12,
paddingTop: 6, paddingTop: 6,
@ -63,5 +67,8 @@ export default StyleSheet.create({
reactionCustomEmoji: { reactionCustomEmoji: {
width: 15, width: 15,
height: 15 height: 15
},
avatar: {
marginRight: 10
} }
}); });

View File

@ -2,14 +2,8 @@ import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { ViewPropTypes } from 'react-native'; import { ViewPropTypes } from 'react-native';
import { KeyboardAwareScrollView } from 'react-native-keyboard-aware-scroll-view'; import { KeyboardAwareScrollView } from 'react-native-keyboard-aware-scroll-view';
import { connect } from 'react-redux';
import scrollPersistTaps from '../utils/scrollPersistTaps'; 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 { export default class KeyboardView extends React.PureComponent {
static propTypes = { static propTypes = {
style: ViewPropTypes.style, style: ViewPropTypes.style,
@ -19,9 +13,7 @@ export default class KeyboardView extends React.PureComponent {
children: PropTypes.oneOfType([ children: PropTypes.oneOfType([
PropTypes.arrayOf(PropTypes.node), PropTypes.arrayOf(PropTypes.node),
PropTypes.node PropTypes.node
]), ])
setKeyboardOpen: PropTypes.func,
setKeyboardClosed: PropTypes.func
} }
render() { render() {
@ -34,8 +26,6 @@ export default class KeyboardView extends React.PureComponent {
alwaysBounceVertical={false} alwaysBounceVertical={false}
extraHeight={this.props.keyboardVerticalOffset} extraHeight={this.props.keyboardVerticalOffset}
behavior='position' behavior='position'
onKeyboardWillShow={() => this.props.setKeyboardOpen()}
onKeyboardWillHide={() => this.props.setKeyboardClosed()}
> >
{this.props.children} {this.props.children}
</KeyboardAwareScrollView> </KeyboardAwareScrollView>

View File

@ -12,7 +12,6 @@ import app from './app';
import permissions from './permissions'; import permissions from './permissions';
import customEmojis from './customEmojis'; import customEmojis from './customEmojis';
import activeUsers from './activeUsers'; import activeUsers from './activeUsers';
import keyboard from './keyboard';
export default combineReducers({ export default combineReducers({
settings, settings,
@ -27,6 +26,5 @@ export default combineReducers({
rooms, rooms,
permissions, permissions,
customEmojis, customEmojis,
activeUsers, activeUsers
keyboard
}); });

View File

@ -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;
}
}

View File

@ -1,7 +1,8 @@
import * as types from '../actions/actionsTypes'; import * as types from '../actions/actionsTypes';
const initialState = { const initialState = {
usersTyping: [] usersTyping: [],
layoutAnimation: new Date()
}; };
export default function room(state = initialState, action) { export default function room(state = initialState, action) {
@ -31,6 +32,11 @@ export default function room(state = initialState, action) {
...state, ...state,
usersTyping: [...state.usersTyping.filter(user => user !== action.username)] usersTyping: [...state.usersTyping.filter(user => user !== action.username)]
}; };
case types.ROOM.LAYOUT_ANIMATION:
return {
...state,
layoutAnimation: new Date()
};
default: default:
return state; return state;
} }

View File

@ -14,7 +14,7 @@ const styles = {
} }
}; };
export default class extends React.PureComponent { export default class Photo extends React.PureComponent {
static propTypes = { static propTypes = {
navigation: PropTypes.object.isRequired navigation: PropTypes.object.isRequired
} }

View File

@ -18,7 +18,7 @@ import { closeRoom } from '../../../actions/room';
}), dispatch => ({ }), dispatch => ({
close: () => dispatch(closeRoom()) close: () => dispatch(closeRoom())
})) }))
export default class extends React.PureComponent { export default class RoomHeaderView extends React.PureComponent {
static propTypes = { static propTypes = {
close: PropTypes.func.isRequired, close: PropTypes.func.isRequired,
navigation: PropTypes.object.isRequired, navigation: PropTypes.object.isRequired,

View File

@ -1,7 +1,7 @@
import { ListView as OldList } from 'realm/react-native'; import { ListView as OldList } from 'realm/react-native';
import React from 'react'; import React from 'react';
import cloneReferencedElement from 'react-clone-referenced-element'; 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 moment from 'moment';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
@ -12,6 +12,7 @@ import styles from './styles';
import debounce from '../../utils/debounce'; import debounce from '../../utils/debounce';
import Typing from '../../containers/Typing'; import Typing from '../../containers/Typing';
import database from '../../lib/realm'; import database from '../../lib/realm';
import scrollPersistTaps from '../../utils/scrollPersistTaps';
const DEFAULT_SCROLL_CALLBACK_THROTTLE = 100; 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 }); const ds = new DataSource({ rowHasChanged: (r1, r2) => r1._id !== r2._id });
@connect(state => ({
lastOpen: state.room.lastOpen
}))
export class List extends React.Component { export class List extends React.Component {
static propTypes = { static propTypes = {
onEndReached: PropTypes.func, onEndReached: PropTypes.func,
@ -54,6 +51,9 @@ export class List extends React.Component {
shouldComponentUpdate(nextProps) { shouldComponentUpdate(nextProps) {
return this.props.end !== nextProps.end; return this.props.end !== nextProps.end;
} }
componentWillUpdate() {
LayoutAnimation.easeInEaseOut();
}
updateState = debounce(() => { updateState = debounce(() => {
// this.setState({ // this.setState({
this.dataSource = this.dataSource.cloneWithRows(this.data); this.dataSource = this.dataSource.cloneWithRows(this.data);
@ -72,12 +72,14 @@ export class List extends React.Component {
dataSource={this.dataSource} dataSource={this.dataSource}
renderRow={item => this.props.renderRow(item)} renderRow={item => this.props.renderRow(item)}
initialListSize={10} initialListSize={10}
keyboardShouldPersistTaps='always' {...scrollPersistTaps}
keyboardDismissMode='none'
/>); />);
} }
} }
@connect(state => ({
lastOpen: state.room.lastOpen
}))
export class ListView extends OldList2 { export class ListView extends OldList2 {
constructor(props) { constructor(props) {
super(props); super(props);

View File

@ -17,7 +17,7 @@ const tabEmojiStyle = { fontSize: 15 };
toggleReactionPicker: message => dispatch(toggleReactionPicker(message)) toggleReactionPicker: message => dispatch(toggleReactionPicker(message))
})) }))
@responsive @responsive
export default class extends React.Component { export default class ReactionPicker extends React.Component {
static propTypes = { static propTypes = {
window: PropTypes.any, window: PropTypes.any,
showReactionPicker: PropTypes.bool, showReactionPicker: PropTypes.bool,

View File

@ -1,5 +1,5 @@
import React from 'react'; import React from 'react';
import { View, StyleSheet, Text } from 'react-native'; import { View, StyleSheet, Text, LayoutAnimation } from 'react-native';
const styles = StyleSheet.create({ const styles = StyleSheet.create({
firstUnread: { firstUnread: {
@ -22,11 +22,16 @@ const styles = StyleSheet.create({
} }
}); });
const UnreadSeparator = () => ( export default class UnreadSeparator extends React.PureComponent {
<View style={styles.firstUnread}> componentWillUnmount() {
<View style={styles.firstUnreadLine} /> LayoutAnimation.linear();
<Text style={styles.firstUnreadBadge}>unread messages</Text> }
</View> render() {
); return (
<View style={styles.firstUnread}>
export default UnreadSeparator; <View style={styles.firstUnreadLine} />
<Text style={styles.firstUnreadBadge}>unread messages</Text>
</View>
);
}
}

View File

@ -1,23 +1,20 @@
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types'; 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 { connect } from 'react-redux';
import { bindActionCreators } from 'redux'; import { bindActionCreators } from 'redux';
import equal from 'deep-equal'; import equal from 'deep-equal';
import KeyboardSpacer from 'react-native-keyboard-spacer';
import { List } from './ListView'; import { List } from './ListView';
import * as actions from '../../actions'; import * as actions from '../../actions';
import { openRoom, setLastOpen } from '../../actions/room'; import { openRoom, setLastOpen } from '../../actions/room';
import { editCancel, toggleReactionPicker } from '../../actions/messages'; import { editCancel, toggleReactionPicker } from '../../actions/messages';
import { setKeyboardOpen, setKeyboardClosed } from '../../actions/keyboard';
import database from '../../lib/realm'; import database from '../../lib/realm';
import RocketChat from '../../lib/rocketchat'; import RocketChat from '../../lib/rocketchat';
import Message from '../../containers/message'; import Message from '../../containers/message';
import MessageActions from '../../containers/MessageActions'; import MessageActions from '../../containers/MessageActions';
import MessageErrorActions from '../../containers/MessageErrorActions'; import MessageErrorActions from '../../containers/MessageErrorActions';
import MessageBox from '../../containers/MessageBox'; import MessageBox from '../../containers/MessageBox';
import Header from '../../containers/Header'; import Header from '../../containers/Header';
import RoomsHeader from './Header'; import RoomsHeader from './Header';
import ReactionPicker from './ReactionPicker'; import ReactionPicker from './ReactionPicker';
@ -30,16 +27,15 @@ import styles from './styles';
Message_TimeFormat: state.settings.Message_TimeFormat, Message_TimeFormat: state.settings.Message_TimeFormat,
loading: state.messages.isFetching, loading: state.messages.isFetching,
user: state.login.user, user: state.login.user,
actionMessage: state.messages.actionMessage actionMessage: state.messages.actionMessage,
layoutAnimation: state.room.layoutAnimation
}), }),
dispatch => ({ dispatch => ({
actions: bindActionCreators(actions, dispatch), actions: bindActionCreators(actions, dispatch),
openRoom: room => dispatch(openRoom(room)), openRoom: room => dispatch(openRoom(room)),
editCancel: () => dispatch(editCancel()), editCancel: () => dispatch(editCancel()),
setLastOpen: date => dispatch(setLastOpen(date)), setLastOpen: date => dispatch(setLastOpen(date)),
toggleReactionPicker: message => dispatch(toggleReactionPicker(message)), toggleReactionPicker: message => dispatch(toggleReactionPicker(message))
setKeyboardOpen: () => dispatch(setKeyboardOpen()),
setKeyboardClosed: () => dispatch(setKeyboardClosed())
}) })
) )
export default class RoomView extends React.Component { export default class RoomView extends React.Component {
@ -56,8 +52,7 @@ export default class RoomView extends React.Component {
loading: PropTypes.bool, loading: PropTypes.bool,
actionMessage: PropTypes.object, actionMessage: PropTypes.object,
toggleReactionPicker: PropTypes.func.isRequired, toggleReactionPicker: PropTypes.func.isRequired,
setKeyboardOpen: PropTypes.func, layoutAnimation: PropTypes.instanceOf(Date)
setKeyboardClosed: PropTypes.func
}; };
static navigationOptions = ({ navigation }) => ({ static navigationOptions = ({ navigation }) => ({
@ -72,7 +67,6 @@ export default class RoomView extends React.Component {
this.name = props.name || this.name = props.name ||
props.navigation.state.params.name || props.navigation.state.params.name ||
props.navigation.state.params.room.name; props.navigation.state.params.room.name;
this.opened = new Date();
this.rooms = database.objects('subscriptions').filtered('rid = $0', this.rid); this.rooms = database.objects('subscriptions').filtered('rid = $0', this.rid);
this.state = { this.state = {
loaded: true, loaded: true,
@ -82,12 +76,12 @@ export default class RoomView extends React.Component {
this.onReactionPress = this.onReactionPress.bind(this); this.onReactionPress = this.onReactionPress.bind(this);
} }
componentWillMount() { async componentWillMount() {
this.props.navigation.setParams({ this.props.navigation.setParams({
title: this.name title: this.name
}); });
this.updateRoom(); 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) { if (this.state.room.alert || this.state.room.unread || this.state.room.userMentions) {
this.props.setLastOpen(this.state.room.ls); this.props.setLastOpen(this.state.room.ls);
} else { } else {
@ -95,8 +89,11 @@ export default class RoomView extends React.Component {
} }
this.rooms.addListener(this.updateRoom); 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) { shouldComponentUpdate(nextProps, nextState) {
return !(equal(this.props, nextProps) && equal(this.state, nextState)); return !(equal(this.props, nextProps) && equal(this.state, nextState));
@ -104,8 +101,6 @@ export default class RoomView extends React.Component {
componentWillUnmount() { componentWillUnmount() {
clearTimeout(this.timer); clearTimeout(this.timer);
this.rooms.removeAllListeners(); this.rooms.removeAllListeners();
this.keyboardDidShowListener.remove();
this.keyboardDidHideListener.remove();
this.props.editCancel(); this.props.editCancel();
} }
@ -141,9 +136,11 @@ export default class RoomView extends React.Component {
this.setState({ room: this.rooms[0] }); this.setState({ room: this.rooms[0] });
} }
sendMessage = message => RocketChat.sendMessage(this.rid, message).then(() => { sendMessage = (message) => {
this.props.setLastOpen(null); RocketChat.sendMessage(this.rid, message).then(() => {
}); this.props.setLastOpen(null);
});
};
joinRoom = async() => { joinRoom = async() => {
await RocketChat.joinRoom(this.props.rid); await RocketChat.joinRoom(this.props.rid);
@ -157,7 +154,6 @@ export default class RoomView extends React.Component {
key={item._id} key={item._id}
item={item} item={item}
reactions={JSON.parse(JSON.stringify(item.reactions))} reactions={JSON.parse(JSON.stringify(item.reactions))}
animate={this.opened.toISOString() < item.ts.toISOString()}
baseUrl={this.props.Site_Url} baseUrl={this.props.Site_Url}
Message_TimeFormat={this.props.Message_TimeFormat} Message_TimeFormat={this.props.Message_TimeFormat}
user={this.props.user} user={this.props.user}
@ -196,20 +192,18 @@ export default class RoomView extends React.Component {
return ( return (
<View style={styles.container}> <View style={styles.container}>
<Banner /> <Banner />
<SafeAreaView style={styles.safeAreaView}> <List
<List key='room-view-messages'
end={this.state.end} end={this.state.end}
room={this.rid} room={this.rid}
renderFooter={this.renderHeader} renderFooter={this.renderHeader}
onEndReached={this.onEndReached} onEndReached={this.onEndReached}
renderRow={item => this.renderItem(item)} renderRow={item => this.renderItem(item)}
/> />
</SafeAreaView>
{this.renderFooter()} {this.renderFooter()}
{this.state.room._id ? <MessageActions room={this.state.room} /> : null} {this.state.room._id ? <MessageActions room={this.state.room} /> : null}
<MessageErrorActions /> <MessageErrorActions />
<ReactionPicker onEmojiSelected={this.onReactionPress} /> <ReactionPicker onEmojiSelected={this.onReactionPress} />
{Platform.OS === 'ios' ? <KeyboardSpacer /> : null}
</View> </View>
); );
} }

View File

@ -1,4 +1,4 @@
import { StyleSheet, Platform } from 'react-native'; import { StyleSheet } from 'react-native';
export default StyleSheet.create({ export default StyleSheet.create({
typing: { fontWeight: 'bold', paddingHorizontal: 15, height: 25 }, typing: { fontWeight: 'bold', paddingHorizontal: 15, height: 25 },
@ -37,7 +37,7 @@ export default StyleSheet.create({
reactionPickerContainer: { reactionPickerContainer: {
// width: width - 20, // width: width - 20,
// height: width - 20, // height: width - 20,
paddingHorizontal: Platform.OS === 'android' ? 11 : 10, // paddingHorizontal: Platform.OS === 'android' ? 11 : 10,
backgroundColor: '#F7F7F7', backgroundColor: '#F7F7F7',
borderRadius: 4, borderRadius: 4,
flexDirection: 'column' flexDirection: 'column'

View File

@ -20,7 +20,7 @@ import styles from './styles';
}), dispatch => ({ }), dispatch => ({
setSearch: searchText => dispatch(setSearch(searchText)) setSearch: searchText => dispatch(setSearch(searchText))
})) }))
export default class extends React.Component { export default class RoomsListHeaderView extends React.Component {
static propTypes = { static propTypes = {
navigation: PropTypes.object.isRequired, navigation: PropTypes.object.isRequired,
user: PropTypes.object.isRequired, user: PropTypes.object.isRequired,

View File

@ -69,7 +69,7 @@ const ds = new ListView.DataSource({ rowHasChanged: (r1, r2) => r1 !== r2 });
resetCreateChannel: () => dispatch(createChannelActions.reset()) resetCreateChannel: () => dispatch(createChannelActions.reset())
}) })
) )
export default class RoomsListView extends React.Component { export default class SelectUsersView extends React.Component {
static propTypes = { static propTypes = {
navigation: PropTypes.object.isRequired, navigation: PropTypes.object.isRequired,
Site_Url: PropTypes.string, Site_Url: PropTypes.string,

View File

@ -5,6 +5,8 @@ import { AppRegistry } from 'react-native';
import './app/push'; import './app/push';
import RocketChat from './app/index'; import RocketChat from './app/index';
// UIManager.setLayoutAnimationEnabledExperimental(true);
// import './app/ReactotronConfig'; // import './app/ReactotronConfig';
// import { AppRegistry } from 'react-native'; // import { AppRegistry } from 'react-native';
// import Routes from './app/routes'; // import Routes from './app/routes';

View File

@ -47,11 +47,13 @@
647660C6B6A340C7BD4D1099 /* EvilIcons.ttf in Resources */ = {isa = PBXBuildFile; fileRef = A18EFC3B0CFE40E0918A8F0C /* EvilIcons.ttf */; }; 647660C6B6A340C7BD4D1099 /* EvilIcons.ttf in Resources */ = {isa = PBXBuildFile; fileRef = A18EFC3B0CFE40E0918A8F0C /* EvilIcons.ttf */; };
70A8D9B456894EFFAF027CAB /* FontAwesome.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 7A30DA4B2D474348824CD05B /* FontAwesome.ttf */; }; 70A8D9B456894EFFAF027CAB /* FontAwesome.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 7A30DA4B2D474348824CD05B /* FontAwesome.ttf */; };
77C35F50C01C43668188886C /* libRNVectorIcons.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5A0EEFAF8AB14F5B9E796CDD /* libRNVectorIcons.a */; }; 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 */; }; 832341BD1AAA6AB300B99B32 /* libRCTText.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 832341B51AAA6A8300B99B32 /* libRCTText.a */; };
8A159EDB97C44E52AF62D69C /* libRNSVG.a in Frameworks */ = {isa = PBXBuildFile; fileRef = DA50CE47374C4C35BE6D9D58 /* libRNSVG.a */; }; 8A159EDB97C44E52AF62D69C /* libRNSVG.a in Frameworks */ = {isa = PBXBuildFile; fileRef = DA50CE47374C4C35BE6D9D58 /* libRNSVG.a */; };
8ECBD927DDAC4987B98E102E /* libRCTVideo.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 20CE3E407E0D4D9E8C9885F2 /* libRCTVideo.a */; }; 8ECBD927DDAC4987B98E102E /* libRCTVideo.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 20CE3E407E0D4D9E8C9885F2 /* libRCTVideo.a */; };
AE5D35882AE04CC29630FB3D /* Entypo.ttf in Resources */ = {isa = PBXBuildFile; fileRef = DC6EE17B5550465E98C70FF0 /* Entypo.ttf */; }; AE5D35882AE04CC29630FB3D /* Entypo.ttf in Resources */ = {isa = PBXBuildFile; fileRef = DC6EE17B5550465E98C70FF0 /* Entypo.ttf */; };
B88F586F1FBF57F600B352B8 /* libRCTPushNotification.a in Frameworks */ = {isa = PBXBuildFile; fileRef = B88F58461FBF55E200B352B8 /* libRCTPushNotification.a */; }; 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 */; }; B8C682A81FD850F4003A12C8 /* icomoon.ttf in Resources */ = {isa = PBXBuildFile; fileRef = B8C682611FD84CEF003A12C8 /* icomoon.ttf */; };
B8C682AC1FD8511D003A12C8 /* Ionicons.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 1B0746E708284151B8AD1198 /* Ionicons.ttf */; }; B8C682AC1FD8511D003A12C8 /* Ionicons.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 1B0746E708284151B8AD1198 /* Ionicons.ttf */; };
B8C682AD1FD8511E003A12C8 /* 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; remoteGlobalIDString = 134814201AA4EA6300B7C361;
remoteInfo = RCTLinking; remoteInfo = RCTLinking;
}; };
7A430E1D20238C02008F55BC /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 7A430E1620238C01008F55BC /* RCTCustomInputController.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = 39DF4FE71E00394E00F5B4B2;
remoteInfo = RCTCustomInputController;
};
7A7F5C981FCC982500024129 /* PBXContainerItemProxy */ = { 7A7F5C981FCC982500024129 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy; isa = PBXContainerItemProxy;
containerPortal = AD0379F2BCE84C968538CDAF /* RCTVideo.xcodeproj */; containerPortal = AD0379F2BCE84C968538CDAF /* RCTVideo.xcodeproj */;
@ -366,6 +375,13 @@
remoteGlobalIDString = 9936F32F1F5F2E5B0010BF04; remoteGlobalIDString = 9936F32F1F5F2E5B0010BF04;
remoteInfo = "privatedata-tvOS"; remoteInfo = "privatedata-tvOS";
}; };
B8971BB0202A091D0000D245 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = B8971BAC202A091D0000D245 /* KeyboardTrackingView.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = D834CED81CC64F2400FA5668;
remoteInfo = KeyboardTrackingView;
};
B8E79A8D1F3CCC6D005B464F /* PBXContainerItemProxy */ = { B8E79A8D1F3CCC6D005B464F /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy; isa = PBXContainerItemProxy;
containerPortal = 4CD38E4891ED4601B7481448 /* RNFetchBlob.xcodeproj */; 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 = "<group>"; }; 6533FB90166345D29F1B91C0 /* libRNFetchBlob.a */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = archive.ar; path = libRNFetchBlob.a; sourceTree = "<group>"; };
78C398B01ACF4ADC00677621 /* RCTLinking.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTLinking.xcodeproj; path = "../node_modules/react-native/Libraries/LinkingIOS/RCTLinking.xcodeproj"; sourceTree = "<group>"; }; 78C398B01ACF4ADC00677621 /* RCTLinking.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTLinking.xcodeproj; path = "../node_modules/react-native/Libraries/LinkingIOS/RCTLinking.xcodeproj"; sourceTree = "<group>"; };
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 = "<group>"; }; 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 = "<group>"; };
7A430E1620238C01008F55BC /* RCTCustomInputController.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTCustomInputController.xcodeproj; path = "../node_modules/react-native-keyboard-input/lib/ios/RCTCustomInputController.xcodeproj"; sourceTree = "<group>"; };
832341B01AAA6A8300B99B32 /* RCTText.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTText.xcodeproj; path = "../node_modules/react-native/Libraries/Text/RCTText.xcodeproj"; sourceTree = "<group>"; }; 832341B01AAA6A8300B99B32 /* RCTText.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTText.xcodeproj; path = "../node_modules/react-native/Libraries/Text/RCTText.xcodeproj"; sourceTree = "<group>"; };
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 = "<group>"; }; 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 = "<group>"; };
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 = "<group>"; }; 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 = "<group>"; };
@ -444,6 +461,7 @@
B2607FA180F14E6584301101 /* libSplashScreen.a */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = archive.ar; path = libSplashScreen.a; sourceTree = "<group>"; }; B2607FA180F14E6584301101 /* libSplashScreen.a */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = archive.ar; path = libSplashScreen.a; sourceTree = "<group>"; };
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; }; 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 = "<group>"; }; B88F58361FBF55E200B352B8 /* RCTPushNotification.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTPushNotification.xcodeproj; path = "../node_modules/react-native/Libraries/PushNotificationIOS/RCTPushNotification.xcodeproj"; sourceTree = "<group>"; };
B8971BAC202A091D0000D245 /* KeyboardTrackingView.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = KeyboardTrackingView.xcodeproj; path = "../node_modules/react-native-keyboard-tracking-view/lib/KeyboardTrackingView.xcodeproj"; sourceTree = "<group>"; };
B8C682611FD84CEF003A12C8 /* icomoon.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; name = icomoon.ttf; path = ../resources/fonts/icomoon.ttf; sourceTree = "<group>"; }; B8C682611FD84CEF003A12C8 /* icomoon.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; name = icomoon.ttf; path = ../resources/fonts/icomoon.ttf; sourceTree = "<group>"; };
BAAE4B947F5D44959F0A9D5A /* libRNZeroconf.a */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = archive.ar; path = libRNZeroconf.a; sourceTree = "<group>"; }; BAAE4B947F5D44959F0A9D5A /* libRNZeroconf.a */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = archive.ar; path = libRNZeroconf.a; sourceTree = "<group>"; };
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 = "<group>"; }; 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 = "<group>"; };
@ -467,6 +485,8 @@
isa = PBXFrameworksBuildPhase; isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
B8971BB2202A093B0000D245 /* libKeyboardTrackingView.a in Frameworks */,
7A430E4F20238C46008F55BC /* libRCTCustomInputController.a in Frameworks */,
146834051AC3E58100842450 /* libReact.a in Frameworks */, 146834051AC3E58100842450 /* libReact.a in Frameworks */,
B88F586F1FBF57F600B352B8 /* libRCTPushNotification.a in Frameworks */, B88F586F1FBF57F600B352B8 /* libRCTPushNotification.a in Frameworks */,
5E9157361DD0AC6A00FF2AA8 /* libRCTAnimation.a in Frameworks */, 5E9157361DD0AC6A00FF2AA8 /* libRCTAnimation.a in Frameworks */,
@ -674,6 +694,14 @@
name = Products; name = Products;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
7A430E1720238C01008F55BC /* Products */ = {
isa = PBXGroup;
children = (
7A430E1E20238C02008F55BC /* libRCTCustomInputController.a */,
);
name = Products;
sourceTree = "<group>";
};
7A7F5C831FCC982500024129 /* Products */ = { 7A7F5C831FCC982500024129 /* Products */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
@ -694,6 +722,8 @@
832341AE1AAA6A7D00B99B32 /* Libraries */ = { 832341AE1AAA6A7D00B99B32 /* Libraries */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
B8971BAC202A091D0000D245 /* KeyboardTrackingView.xcodeproj */,
7A430E1620238C01008F55BC /* RCTCustomInputController.xcodeproj */,
B88F58361FBF55E200B352B8 /* RCTPushNotification.xcodeproj */, B88F58361FBF55E200B352B8 /* RCTPushNotification.xcodeproj */,
5E91572D1DD0AC6500FF2AA8 /* RCTAnimation.xcodeproj */, 5E91572D1DD0AC6500FF2AA8 /* RCTAnimation.xcodeproj */,
146833FF1AC3E56700842450 /* React.xcodeproj */, 146833FF1AC3E56700842450 /* React.xcodeproj */,
@ -780,6 +810,14 @@
name = Products; name = Products;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
B8971BAD202A091D0000D245 /* Products */ = {
isa = PBXGroup;
children = (
B8971BB1202A091D0000D245 /* libKeyboardTrackingView.a */,
);
name = Products;
sourceTree = "<group>";
};
B8E79A681F3CCC69005B464F /* Recovered References */ = { B8E79A681F3CCC69005B464F /* Recovered References */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
@ -952,6 +990,10 @@
productRefGroup = 83CBBA001A601CBA00E9B192 /* Products */; productRefGroup = 83CBBA001A601CBA00E9B192 /* Products */;
projectDirPath = ""; projectDirPath = "";
projectReferences = ( projectReferences = (
{
ProductGroup = B8971BAD202A091D0000D245 /* Products */;
ProjectRef = B8971BAC202A091D0000D245 /* KeyboardTrackingView.xcodeproj */;
},
{ {
ProductGroup = 00C302A81ABCB8CE00DB3ED1 /* Products */; ProductGroup = 00C302A81ABCB8CE00DB3ED1 /* Products */;
ProjectRef = 00C302A71ABCB8CE00DB3ED1 /* RCTActionSheet.xcodeproj */; ProjectRef = 00C302A71ABCB8CE00DB3ED1 /* RCTActionSheet.xcodeproj */;
@ -960,6 +1002,10 @@
ProductGroup = 5E91572E1DD0AC6500FF2AA8 /* Products */; ProductGroup = 5E91572E1DD0AC6500FF2AA8 /* Products */;
ProjectRef = 5E91572D1DD0AC6500FF2AA8 /* RCTAnimation.xcodeproj */; ProjectRef = 5E91572D1DD0AC6500FF2AA8 /* RCTAnimation.xcodeproj */;
}, },
{
ProductGroup = 7A430E1720238C01008F55BC /* Products */;
ProjectRef = 7A430E1620238C01008F55BC /* RCTCustomInputController.xcodeproj */;
},
{ {
ProductGroup = 00C302B61ABCB90400DB3ED1 /* Products */; ProductGroup = 00C302B61ABCB90400DB3ED1 /* Products */;
ProjectRef = 00C302B51ABCB90400DB3ED1 /* RCTGeolocation.xcodeproj */; ProjectRef = 00C302B51ABCB90400DB3ED1 /* RCTGeolocation.xcodeproj */;
@ -1261,6 +1307,13 @@
remoteRef = 78C398B81ACF4ADC00677621 /* PBXContainerItemProxy */; remoteRef = 78C398B81ACF4ADC00677621 /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR; 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 */ = { 7A7F5C991FCC982500024129 /* libRCTVideo.a */ = {
isa = PBXReferenceProxy; isa = PBXReferenceProxy;
fileType = archive.ar; fileType = archive.ar;
@ -1331,6 +1384,13 @@
remoteRef = B88F58661FBF55E200B352B8 /* PBXContainerItemProxy */; remoteRef = B88F58661FBF55E200B352B8 /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR; 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 */ = { B8E79A8E1F3CCC6D005B464F /* libRNFetchBlob.a */ = {
isa = PBXReferenceProxy; isa = PBXReferenceProxy;
fileType = archive.ar; fileType = archive.ar;

24
package-lock.json generated
View File

@ -12568,10 +12568,20 @@
"react-native-iphone-x-helper": "1.0.1" "react-native-iphone-x-helper": "1.0.1"
} }
}, },
"react-native-keyboard-spacer": { "react-native-keyboard-input": {
"version": "0.4.1", "version": "git+https://github.com/RocketChat/react-native-keyboard-input.git#38273b0513f69a5e6e0719f65a675f9f2b5ee883",
"resolved": "https://registry.npmjs.org/react-native-keyboard-spacer/-/react-native-keyboard-spacer-0.4.1.tgz", "requires": {
"integrity": "sha1-RvGKMgQyCYol6p+on1FD3SVNMy0=" "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": { "react-native-loading-spinner-overlay": {
"version": "0.5.2", "version": "0.5.2",
@ -12624,9 +12634,9 @@
} }
}, },
"react-native-optimized-flatlist": { "react-native-optimized-flatlist": {
"version": "1.0.3", "version": "1.0.4",
"resolved": "https://registry.npmjs.org/react-native-optimized-flatlist/-/react-native-optimized-flatlist-1.0.3.tgz", "resolved": "https://registry.npmjs.org/react-native-optimized-flatlist/-/react-native-optimized-flatlist-1.0.4.tgz",
"integrity": "sha1-tFN58lpXu05vhZwZDZmEexgR4Ak=", "integrity": "sha512-PMoZRJAHKzd/ahYKUzt43AJ+kVhHpOSTvBhJdQqooZXw312xADWpR7iDvBAbBiRGkmk0yM4GJacd9TMft6q/Gg==",
"requires": { "requires": {
"prop-types": "15.6.0" "prop-types": "15.6.0"
} }

View File

@ -45,11 +45,12 @@
"react-native-image-picker": "^0.26.7", "react-native-image-picker": "^0.26.7",
"react-native-img-cache": "^1.5.2", "react-native-img-cache": "^1.5.2",
"react-native-keyboard-aware-scroll-view": "^0.4.1", "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-loading-spinner-overlay": "^0.5.2",
"react-native-meteor": "^1.2.0", "react-native-meteor": "^1.2.0",
"react-native-modal": "^4.1.1", "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-push-notification": "^3.0.1",
"react-native-responsive-ui": "^1.1.1", "react-native-responsive-ui": "^1.1.1",
"react-native-scrollable-tab-view": "^0.8.0", "react-native-scrollable-tab-view": "^0.8.0",