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:
parent
d270240a6d
commit
ed5a1386e0
|
@ -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')
|
||||
|
|
|
@ -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;
|
||||
|
@ -42,7 +43,8 @@ public class MainApplication extends Application implements ReactApplication {
|
|||
new ReactNativePushNotificationPackage(),
|
||||
new ReactVideoPackage(),
|
||||
new SplashScreenReactPackage(),
|
||||
new RCTToastPackage()
|
||||
new RCTToastPackage(),
|
||||
new KeyboardInputPackage(MainApplication.this)
|
||||
);
|
||||
}
|
||||
};
|
||||
|
|
|
@ -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'
|
||||
|
|
|
@ -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']);
|
||||
|
||||
|
|
|
@ -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
|
||||
};
|
||||
}
|
|
@ -55,3 +55,9 @@ export function setLastOpen(date = new Date()) {
|
|||
date
|
||||
};
|
||||
}
|
||||
|
||||
export function layoutAnimation() {
|
||||
return {
|
||||
type: types.ROOM.LAYOUT_ANIMATION
|
||||
};
|
||||
}
|
||||
|
|
|
@ -42,7 +42,8 @@ export default class Panel extends React.Component {
|
|||
this.state.animation,
|
||||
{
|
||||
toValue: finalValue,
|
||||
duration: 150
|
||||
duration: 150,
|
||||
useNativeDriver: true
|
||||
}
|
||||
).start();
|
||||
}
|
||||
|
|
|
@ -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 });
|
||||
});
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
);
|
||||
}
|
||||
}
|
|
@ -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);
|
|
@ -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 = (
|
||||
renderMentions = () => (
|
||||
<FlatList
|
||||
key='messagebox-container'
|
||||
style={styles.mentionList}
|
||||
data={this.state.mentions}
|
||||
renderItem={({ item }) => this.renderMentionItem(item)}
|
||||
keyExtractor={item => item._id || item}
|
||||
{...scrollPersistTaps}
|
||||
keyboardShouldPersistTaps='always'
|
||||
/>
|
||||
);
|
||||
const { showMentionsContainer, messageboxHeight } = this.state;
|
||||
return <AnimatedContainer visible={showMentionsContainer} subview={list} messageboxHeight={messageboxHeight} />;
|
||||
}
|
||||
render() {
|
||||
|
||||
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
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
});
|
|
@ -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,
|
||||
|
|
|
@ -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}
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
});
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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
|
||||
});
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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 = () => (
|
||||
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>
|
||||
);
|
||||
|
||||
export default UnreadSeparator;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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(() => {
|
||||
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
|
||||
key='room-view-messages'
|
||||
end={this.state.end}
|
||||
room={this.rid}
|
||||
renderFooter={this.renderHeader}
|
||||
onEndReached={this.onEndReached}
|
||||
renderRow={item => this.renderItem(item)}
|
||||
/>
|
||||
</SafeAreaView>
|
||||
{this.renderFooter()}
|
||||
{this.state.room._id ? <MessageActions room={this.state.room} /> : null}
|
||||
<MessageErrorActions />
|
||||
<ReactionPicker onEmojiSelected={this.onReactionPress} />
|
||||
{Platform.OS === 'ios' ? <KeyboardSpacer /> : null}
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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'
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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';
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
|
|
|
@ -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",
|
||||
|
|
Loading…
Reference in New Issue