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 {
compile project(":reactnativekeyboardinput")
compile project(':react-native-splash-screen')
compile project(':react-native-video')
compile project(':react-native-push-notification')

View File

@ -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<ReactPackage> getPackages() {
return Arrays.<ReactPackage>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)
);
}
};

View File

@ -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'

View File

@ -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']);

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
};
}
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,
{
toValue: finalValue,
duration: 150
duration: 150,
useNativeDriver: true
}
).start();
}

View File

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

View File

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

View File

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

View File

@ -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 <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 styles from './styles';
export default class extends React.PureComponent {
export default class TabBar extends React.PureComponent {
static propTypes = {
goToPage: PropTypes.func,
activeTab: PropTypes.number,

View File

@ -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 {
<ScrollableTabView
renderTabBar={() => <TabBar tabEmojiStyle={this.props.tabEmojiStyle} />}
contentProps={scrollProps}
// prerenderingSiblingsNumber={1}
>
{
categories.tabs.map((tab, i) => (
<ScrollView
key={tab.category}
tabLabel={tab.tabLabel}
{...scrollPersistTaps}
{...scrollProps}
>
{this.renderCategory(tab.category, i)}
</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 = {
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 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 ? (<Icon
return !this.state.showEmojiKeyboard ? (<Icon
style={styles.actionButtons}
onPress={() => 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 (
<CustomEmoji
key='mention-item-avatar'
style={[styles.mentionItemCustomEmoji]}
style={styles.mentionItemCustomEmoji}
emoji={item}
baseUrl={this.props.baseUrl}
/>
@ -399,7 +402,7 @@ export default class MessageBox extends React.PureComponent {
return (
<Text
key='mention-item-avatar'
style={[StyleSheet.flatten(styles.mentionItemEmoji)]}
style={styles.mentionItemEmoji}
>
{emojify(`:${ item }:`, { output: 'unicode' })}
</Text>
@ -433,34 +436,24 @@ export default class MessageBox extends React.PureComponent {
</TouchableOpacity>
);
}
renderEmoji() {
const emojiContainer = (
<View style={styles.emojiContainer}>
<EmojiPicker onEmojiSelected={emoji => this._onEmojiSelected(emoji)} />
</View>
);
const { showEmojiContainer, messageboxHeight } = this.state;
return <AnimatedContainer visible={showEmojiContainer} subview={emojiContainer} messageboxHeight={messageboxHeight} />;
}
renderMentions() {
const list = (
<FlatList
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() {
renderMentions = () => (
<FlatList
key='messagebox-container'
style={styles.mentionList}
data={this.state.mentions}
renderItem={({ item }) => this.renderMentionItem(item)}
keyExtractor={item => item._id || item}
keyboardShouldPersistTaps='always'
/>
);
renderContent() {
return (
<View>
[
this.renderMentions(),
<SafeAreaView
key='messagebox'
style={[styles.textBox, (this.props.editing ? styles.editing : null)]}
onLayout={event => this.setState({ messageboxHeight: event.nativeEvent.layout.height })}
>
<View style={styles.textArea}>
{this.leftButtons}
@ -480,9 +473,22 @@ export default class MessageBox extends React.PureComponent {
{this.rightButtons}
</View>
</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
},
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
}
});

View File

@ -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,

View File

@ -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 (
<ScrollView style={styles.scrollView}>
<View style={{ paddingBottom: 20 }}>
<DrawerItems
{...this.props}
onItemPress={this.onItemPress}
/>
<FlatList
data={this.state.servers}
renderItem={this.renderItem}

View File

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

View File

@ -111,9 +111,8 @@ const Markdown = ({ msg, customEmojis }) => {
const emojiExtension = customEmojis[content];
if (emojiExtension) {
const emoji = { extension: emojiExtension, content };
const style = StyleSheet.flatten(styles.customEmoji);
element.props.children = (
<CustomEmoji key={state.key} style={style} emoji={emoji} />
<CustomEmoji key={state.key} style={styles.customEmoji} emoji={emoji} />
);
}
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 = {
title: PropTypes.string.isRequired,
image: PropTypes.string.isRequired,

View File

@ -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,

View File

@ -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 {
<View style={[styles.reactionContainer, reactedContainerStyle]}>
<Emoji
content={reaction.emoji}
standardEmojiStyle={StyleSheet.flatten(styles.reactionEmoji)}
customEmojiStyle={StyleSheet.flatten(styles.reactionCustomEmoji)}
standardEmojiStyle={styles.reactionEmoji}
customEmojiStyle={styles.reactionCustomEmoji}
customEmojis={this.props.customEmojis}
/>
<Text style={[styles.reactionCount, reactedCount]}>{ reaction.usernames.length }</Text>
@ -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}
>
<Animated.View style={[flex, { opacity, marginLeft }]}>
<View style={styles.flex}>
{this.renderError()}
<View style={[this.extraStyle, flex]}>
<View style={[this.extraStyle, styles.flex]}>
<Avatar
style={avatar}
style={styles.avatar}
text={item.avatar ? '' : username}
size={40}
baseUrl={baseUrl}
@ -296,7 +271,7 @@ export default class Message extends React.Component {
/>
: null
}
</Animated.View>
</View>
</TouchableHighlight>
);
}

View File

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

View File

@ -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}
</KeyboardAwareScrollView>

View File

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

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

View File

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

View File

@ -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,

View File

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

View File

@ -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,

View File

@ -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 = () => (
<View style={styles.firstUnread}>
<View style={styles.firstUnreadLine} />
<Text style={styles.firstUnreadBadge}>unread messages</Text>
</View>
);
export default UnreadSeparator;
export default class UnreadSeparator extends React.PureComponent {
componentWillUnmount() {
LayoutAnimation.linear();
}
render() {
return (
<View style={styles.firstUnread}>
<View style={styles.firstUnreadLine} />
<Text style={styles.firstUnreadBadge}>unread messages</Text>
</View>
);
}
}

View File

@ -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 (
<View style={styles.container}>
<Banner />
<SafeAreaView style={styles.safeAreaView}>
<List
end={this.state.end}
room={this.rid}
renderFooter={this.renderHeader}
onEndReached={this.onEndReached}
renderRow={item => this.renderItem(item)}
/>
</SafeAreaView>
<List
key='room-view-messages'
end={this.state.end}
room={this.rid}
renderFooter={this.renderHeader}
onEndReached={this.onEndReached}
renderRow={item => this.renderItem(item)}
/>
{this.renderFooter()}
{this.state.room._id ? <MessageActions room={this.state.room} /> : null}
<MessageErrorActions />
<ReactionPicker onEmojiSelected={this.onReactionPress} />
{Platform.OS === 'ios' ? <KeyboardSpacer /> : null}
</View>
);
}

View File

@ -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'

View File

@ -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,

View File

@ -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,

View File

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

View File

@ -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 = "<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>"; };
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>"; };
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>"; };
@ -444,6 +461,7 @@
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; };
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>"; };
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>"; };
@ -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 = "<group>";
};
7A430E1720238C01008F55BC /* Products */ = {
isa = PBXGroup;
children = (
7A430E1E20238C02008F55BC /* libRCTCustomInputController.a */,
);
name = Products;
sourceTree = "<group>";
};
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 = "<group>";
};
B8971BAD202A091D0000D245 /* Products */ = {
isa = PBXGroup;
children = (
B8971BB1202A091D0000D245 /* libKeyboardTrackingView.a */,
);
name = Products;
sourceTree = "<group>";
};
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;

24
package-lock.json generated
View File

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

View File

@ -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",