react-native-gifted-chat
Advanced tools
Comparing version 0.0.10 to 0.1.0
{ | ||
"name": "react-native-gifted-chat", | ||
"version": "0.0.10", | ||
"version": "0.1.0", | ||
"description": "The most complete chat UI for React Native", | ||
@@ -26,17 +26,24 @@ "main": "index.js", | ||
"homepage": "https://github.com/FaridSafi/react-native-gifted-chat#readme", | ||
"scripts": { | ||
"lint": "eslint . --ext .js,.jsx" | ||
}, | ||
"devDependencies": { | ||
"eslint": "^3.2.2", | ||
"eslint-plugin-react": "^6.0.0", | ||
"eslint-plugin-react-native": "^2.0.0" | ||
"eslint": "~3.12.2", | ||
"eslint-config-airbnb": "~13.0.0", | ||
"eslint-config-cooperka": "~0.2.3", | ||
"eslint-plugin-import": "~2.2.0", | ||
"eslint-plugin-jsx-a11y": "~2.2.3", | ||
"eslint-plugin-react": "~6.8.0", | ||
"eslint-plugin-react-native": "~2.2.1" | ||
}, | ||
"dependencies": { | ||
"@exponent/react-native-action-sheet": "git+https://github.com/FaridSafi/react-native-action-sheet.git", | ||
"md5": "^2.1.0", | ||
"moment": "^2.13.0", | ||
"react-native-communications": "^2.1.0", | ||
"react-native-dismiss-keyboard": "^1.0.0", | ||
"react-native-invertible-scroll-view": "^1.0.0", | ||
"react-native-parsed-text": "^0.0.15", | ||
"shallowequal": "^0.2.2" | ||
"@exponent/react-native-action-sheet": "0.3.0", | ||
"md5": "2.1.1", | ||
"moment": "2.17.1", | ||
"react-native-communications": "2.1.1", | ||
"react-native-invertible-scroll-view": "1.0.0", | ||
"react-native-lightbox": "oblador/react-native-lightbox#c84a8543d4511fe6a44c3d7820747c9c1bddd875", | ||
"react-native-parsed-text": "0.0.16", | ||
"shallowequal": "0.2.2" | ||
} | ||
} |
@@ -8,3 +8,4 @@ # Gifted Chat | ||
## Dependency | ||
React Native minimum version `0.29.0` | ||
Use `0.0.10` for RN < `0.40.0` | ||
Use `0.1.0` for RN >= `0.40.0` | ||
@@ -28,11 +29,6 @@ ## Installation | ||
## Changelog | ||
### 0.0.7 | ||
- New prop `isLoadingEarlier` | ||
- `title` prop of `Send` component has been renamed to `label` | ||
- PropTypes checking | ||
## Example | ||
```jsx | ||
import {GiftedChat} from 'react-native-gifted-chat'; | ||
import { GiftedChat } from 'react-native-gifted-chat'; | ||
@@ -118,2 +114,4 @@ class Example extends React.Component { | ||
- **`renderMessageImage`** _(Function)_ - render the message image | ||
- **`imageProps`** _(Object)_ - extra props to be passed to the [`<Image>`](https://facebook.github.io/react-native/docs/image.html) component created by the default `renderMessageImage` | ||
- **`lightboxProps`** _(Object)_ - extra props to be passed to the MessageImage's [Lightbox](https://github.com/oblador/react-native-lightbox) | ||
- **`renderCustomView`** _(Function)_ - render a custom view inside the bubble | ||
@@ -128,5 +126,8 @@ - **`renderDay`** _(Function)_ - render the day above a message | ||
- **`renderAccessory`** _(Function)_ - renders a second line of actions below the message composer | ||
- **`onPressActionButton`** _(Function)_ - callback to perform custom logic when the Action button is pressed (the default `actionSheet` will not be used) | ||
- **`bottomOffset`** _(Integer)_ - distance of the chat from the bottom of the screen, useful if you display a tab bar | ||
- **`listViewProps`** _(Object)_ - extra props to be passed to the [`<ListView>`](https://facebook.github.io/react-native/docs/listview.html), some props can not be override, see the code in `render` method of `MessageContainer` for detail | ||
- **`keyboardShouldPersistTaps`** _(Enum)_ - determines when the keyboard should stay visible after a tap [`<ScrollView>`](https://facebook.github.io/react-native/docs/scrollview.html) | ||
- **`onInputTextChanged`** _(Function)_ - function that will be called when input text changes | ||
## Features | ||
@@ -133,0 +134,0 @@ - Custom components |
@@ -21,2 +21,3 @@ import React from 'react'; | ||
cancelButtonIndex, | ||
tintColor: this.props.optionTintColor | ||
}, | ||
@@ -58,3 +59,3 @@ (buttonIndex) => { | ||
style={[styles.container, this.props.containerStyle]} | ||
onPress={this.onActionsPress} | ||
onPress={this.props.onPressActionButton || this.onActionsPress} | ||
> | ||
@@ -96,2 +97,3 @@ {this.renderIcon()} | ||
options: {}, | ||
optionTintColor: '#007AFF', | ||
icon: null, | ||
@@ -105,5 +107,7 @@ containerStyle: {}, | ||
options: React.PropTypes.object, | ||
optionTintColor: React.PropTypes.string, | ||
icon: React.PropTypes.func, | ||
onPressActionButton: React.PropTypes.func, | ||
containerStyle: View.propTypes.style, | ||
iconTextStyle: Text.propTypes.style, | ||
}; |
@@ -10,2 +10,4 @@ import React from 'react'; | ||
import { isSameUser, isSameDay, warnDeprecated } from './utils'; | ||
export default class Avatar extends React.Component { | ||
@@ -26,3 +28,3 @@ renderAvatar() { | ||
render() { | ||
if (this.props.isSameUser(this.props.currentMessage, this.props.nextMessage) && this.props.isSameDay(this.props.currentMessage, this.props.nextMessage)) { | ||
if (isSameUser(this.props.currentMessage, this.props.nextMessage) && isSameDay(this.props.currentMessage, this.props.nextMessage)) { | ||
return ( | ||
@@ -68,4 +70,2 @@ <View style={[styles[this.props.position].container, this.props.containerStyle[this.props.position]]}> | ||
Avatar.defaultProps = { | ||
isSameDay: () => {}, | ||
isSameUser: () => {}, | ||
position: 'left', | ||
@@ -78,12 +78,22 @@ currentMessage: { | ||
imageStyle: {}, | ||
//TODO: remove in next major release | ||
isSameDay: warnDeprecated(isSameDay), | ||
isSameUser: warnDeprecated(isSameUser) | ||
}; | ||
Avatar.propTypes = { | ||
isSameDay: React.PropTypes.func, | ||
isSameUser: React.PropTypes.func, | ||
position: React.PropTypes.oneOf(['left', 'right']), | ||
currentMessage: React.PropTypes.object, | ||
nextMessage: React.PropTypes.object, | ||
containerStyle: View.propTypes.style, | ||
imageStyle: React.PropTypes.oneOfType([View.propTypes.style, Image.propTypes.style]), | ||
containerStyle: React.PropTypes.shape({ | ||
left: View.propTypes.style, | ||
right: View.propTypes.style, | ||
}), | ||
imageStyle: React.PropTypes.shape({ | ||
left: View.propTypes.style, | ||
right: View.propTypes.style, | ||
}), | ||
//TODO: remove in next major release | ||
isSameDay: React.PropTypes.func, | ||
isSameUser: React.PropTypes.func | ||
}; |
import React from 'react'; | ||
import { | ||
Text, | ||
Clipboard, | ||
@@ -13,2 +14,4 @@ StyleSheet, | ||
import { isSameUser, isSameDay, warnDeprecated } from './utils'; | ||
export default class Bubble extends React.Component { | ||
@@ -21,3 +24,3 @@ constructor(props) { | ||
handleBubbleToNext() { | ||
if (this.props.isSameUser(this.props.currentMessage, this.props.nextMessage) && this.props.isSameDay(this.props.currentMessage, this.props.nextMessage)) { | ||
if (isSameUser(this.props.currentMessage, this.props.nextMessage) && isSameDay(this.props.currentMessage, this.props.nextMessage)) { | ||
return StyleSheet.flatten([styles[this.props.position].containerToNext, this.props.containerToNextStyle[this.props.position]]); | ||
@@ -29,3 +32,3 @@ } | ||
handleBubbleToPrevious() { | ||
if (this.props.isSameUser(this.props.currentMessage, this.props.previousMessage) && this.props.isSameDay(this.props.currentMessage, this.props.previousMessage)) { | ||
if (isSameUser(this.props.currentMessage, this.props.previousMessage) && isSameDay(this.props.currentMessage, this.props.previousMessage)) { | ||
return StyleSheet.flatten([styles[this.props.position].containerToPrevious, this.props.containerToPreviousStyle[this.props.position]]); | ||
@@ -58,2 +61,20 @@ } | ||
renderTicks() { | ||
const {currentMessage} = this.props; | ||
if (this.props.renderTicks) { | ||
return this.props.renderTicks(currentMessage); | ||
} | ||
if (currentMessage.user._id !== this.props.user._id) { | ||
return; | ||
} | ||
if (currentMessage.sent || currentMessage.received) { | ||
return ( | ||
<View style={styles.tickView}> | ||
{currentMessage.sent && <Text style={[styles.tick, this.props.tickStyle]}>✓</Text>} | ||
{currentMessage.received && <Text style={[styles.tick, this.props.tickStyle]}>✓</Text>} | ||
</View> | ||
) | ||
} | ||
} | ||
renderTime() { | ||
@@ -108,2 +129,3 @@ if (this.props.currentMessage.createdAt) { | ||
onLongPress={this.onLongPress} | ||
accessibilityTraits="text" | ||
{...this.props.touchableProps} | ||
@@ -115,3 +137,6 @@ > | ||
{this.renderMessageText()} | ||
{this.renderTime()} | ||
<View style={styles.bottom}> | ||
{this.renderTime()} | ||
{this.renderTicks()} | ||
</View> | ||
</View> | ||
@@ -164,2 +189,15 @@ </TouchableWithoutFeedback> | ||
}), | ||
bottom: { | ||
flexDirection: 'row', | ||
justifyContent: 'flex-end', | ||
}, | ||
tick: { | ||
fontSize: 10, | ||
backgroundColor: 'transparent', | ||
color: 'white', | ||
}, | ||
tickView: { | ||
flexDirection: 'row', | ||
marginRight: 10, | ||
} | ||
}; | ||
@@ -178,4 +216,2 @@ | ||
renderTime: null, | ||
isSameUser: () => {}, | ||
isSameDay: () => {}, | ||
position: 'left', | ||
@@ -191,4 +227,8 @@ currentMessage: { | ||
wrapperStyle: {}, | ||
tickStyle: {}, | ||
containerToNextStyle: {}, | ||
containerToPreviousStyle: {}, | ||
//TODO: remove in next major release | ||
isSameDay: warnDeprecated(isSameDay), | ||
isSameUser: warnDeprecated(isSameUser), | ||
}; | ||
@@ -203,4 +243,2 @@ | ||
renderTime: React.PropTypes.func, | ||
isSameUser: React.PropTypes.func, | ||
isSameDay: React.PropTypes.func, | ||
position: React.PropTypes.oneOf(['left', 'right']), | ||
@@ -218,2 +256,3 @@ currentMessage: React.PropTypes.object, | ||
}), | ||
tickStyle: Text.propTypes.style, | ||
containerToNextStyle: React.PropTypes.shape({ | ||
@@ -227,2 +266,5 @@ left: View.propTypes.style, | ||
}), | ||
//TODO: remove in next major release | ||
isSameDay: React.PropTypes.func, | ||
isSameUser: React.PropTypes.func, | ||
}; |
@@ -9,2 +9,17 @@ import React from 'react'; | ||
export default class Composer extends React.Component { | ||
onChange(e) { | ||
const contentSize = e.nativeEvent.contentSize; | ||
if (!this.contentSize) { | ||
this.contentSize = contentSize; | ||
this.props.onInputSizeChanged(this.contentSize); | ||
} else if (this.contentSize.width !== contentSize.width || this.contentSize.height !== contentSize.height) { | ||
this.contentSize = contentSize; | ||
this.props.onInputSizeChanged(this.contentSize); | ||
} | ||
} | ||
onChangeText(text) { | ||
this.props.onTextChanged(text); | ||
} | ||
render() { | ||
@@ -16,9 +31,10 @@ return ( | ||
multiline={this.props.multiline} | ||
onChange={(e) => { | ||
this.props.onChange(e); | ||
}} | ||
style={[styles.textInput, this.props.textInputStyle, { | ||
height: this.props.composerHeight, | ||
}]} | ||
onChange={(e) => this.onChange(e)} | ||
onChangeText={text => this.onChangeText(text)} | ||
style={[styles.textInput, this.props.textInputStyle, {height: this.props.composerHeight}]} | ||
value={this.props.text} | ||
accessibilityLabel={this.props.text || this.props.placeholder} | ||
enablesReturnKeyAutomatically={true} | ||
@@ -50,3 +66,4 @@ underlineColorAndroid="transparent" | ||
Composer.defaultProps = { | ||
onChange: () => {}, | ||
onChange: () => { | ||
}, | ||
composerHeight: Platform.select({ | ||
@@ -62,2 +79,6 @@ ios: 33, | ||
textInputStyle: {}, | ||
onTextChanged: () => { | ||
}, | ||
onInputSizeChanged: () => { | ||
}, | ||
}; | ||
@@ -72,4 +93,6 @@ | ||
textInputProps: React.PropTypes.object, | ||
onTextChanged: React.PropTypes.func, | ||
onInputSizeChanged: React.PropTypes.func, | ||
multiline: React.PropTypes.bool, | ||
textInputStyle: TextInput.propTypes.style, | ||
}; |
@@ -10,5 +10,7 @@ import React from 'react'; | ||
import { isSameDay, isSameUser, warnDeprecated } from './utils'; | ||
export default class Day extends React.Component { | ||
render() { | ||
if (!this.props.isSameDay(this.props.currentMessage, this.props.previousMessage)) { | ||
if (!isSameDay(this.props.currentMessage, this.props.previousMessage)) { | ||
return ( | ||
@@ -56,3 +58,2 @@ <View style={[styles.container, this.props.containerStyle]}> | ||
Day.defaultProps = { | ||
isSameDay: () => {}, | ||
currentMessage: { | ||
@@ -66,6 +67,8 @@ // TODO test if crash when createdAt === null | ||
textStyle: {}, | ||
//TODO: remove in next major release | ||
isSameDay: warnDeprecated(isSameDay), | ||
isSameUser: warnDeprecated(isSameUser), | ||
}; | ||
Day.propTypes = { | ||
isSameDay: React.PropTypes.func, | ||
currentMessage: React.PropTypes.object, | ||
@@ -76,2 +79,5 @@ previousMessage: React.PropTypes.object, | ||
textStyle: Text.propTypes.style, | ||
//TODO: remove in next major release | ||
isSameDay: React.PropTypes.func, | ||
isSameUser: React.PropTypes.func, | ||
}; |
@@ -74,7 +74,10 @@ /* | ||
return ( | ||
<View style={[ | ||
defaultStyles.avatarStyle, | ||
{backgroundColor: 'transparent'}, | ||
this.props.avatarStyle, | ||
]}/> | ||
<View | ||
style={[ | ||
defaultStyles.avatarStyle, | ||
{backgroundColor: 'transparent'}, | ||
this.props.avatarStyle, | ||
]} | ||
accessibilityTraits="image" | ||
/> | ||
) | ||
@@ -90,2 +93,3 @@ } | ||
}} | ||
accessibilityTraits="image" | ||
> | ||
@@ -113,2 +117,3 @@ {this.renderAvatar()} | ||
]} | ||
accessibilityTraits="image" | ||
> | ||
@@ -115,0 +120,0 @@ {this.renderInitials()} |
@@ -11,5 +11,5 @@ import React from 'react'; | ||
import ActionSheet from '@exponent/react-native-action-sheet'; | ||
import dismissKeyboard from 'react-native-dismiss-keyboard'; | ||
import moment from 'moment/min/moment-with-locales.min'; | ||
import * as utils from './utils'; | ||
import Actions from './Actions'; | ||
@@ -28,2 +28,3 @@ import Avatar from './Avatar'; | ||
import Time from './Time'; | ||
import GiftedAvatar from './GiftedAvatar'; | ||
@@ -49,5 +50,3 @@ // Min and max heights of ToolbarInput and Composer | ||
this._maxHeight = null; | ||
this._touchStarted = false; | ||
this._isFirstLayout = true; | ||
this._isTypingDisabled = false; | ||
this._locale = 'en'; | ||
@@ -58,7 +57,7 @@ this._messages = []; | ||
isInitialized: false, // initialization will calculate maxHeight before rendering the chat | ||
composerHeight: MIN_COMPOSER_HEIGHT, | ||
messagesContainerHeight: null, | ||
typingDisabled: false | ||
}; | ||
this.onTouchStart = this.onTouchStart.bind(this); | ||
this.onTouchMove = this.onTouchMove.bind(this); | ||
this.onTouchEnd = this.onTouchEnd.bind(this); | ||
this.onKeyboardWillShow = this.onKeyboardWillShow.bind(this); | ||
@@ -68,12 +67,13 @@ this.onKeyboardWillHide = this.onKeyboardWillHide.bind(this); | ||
this.onKeyboardDidHide = this.onKeyboardDidHide.bind(this); | ||
this.onType = this.onType.bind(this); | ||
this.onSend = this.onSend.bind(this); | ||
this.getLocale = this.getLocale.bind(this); | ||
this.onInputSizeChanged = this.onInputSizeChanged.bind(this); | ||
this.onInputTextChanged = this.onInputTextChanged.bind(this); | ||
this.onMainViewLayout = this.onMainViewLayout.bind(this); | ||
this.onInitialLayoutViewLayout = this.onInitialLayoutViewLayout.bind(this); | ||
this.invertibleScrollViewProps = { | ||
inverted: true, | ||
keyboardShouldPersistTaps: true, | ||
onTouchStart: this.onTouchStart, | ||
onTouchMove: this.onTouchMove, | ||
onTouchEnd: this.onTouchEnd, | ||
keyboardShouldPersistTaps: this.props.keyboardShouldPersistTaps, | ||
onKeyboardWillShow: this.onKeyboardWillShow, | ||
@@ -182,7 +182,9 @@ onKeyboardWillHide: this.onKeyboardWillHide, | ||
setIsTypingDisabled(value) { | ||
this._isTypingDisabled = value; | ||
this.setState({ | ||
typingDisabled: value | ||
}); | ||
} | ||
getIsTypingDisabled() { | ||
return this._isTypingDisabled; | ||
return this.state.typingDisabled; | ||
} | ||
@@ -225,6 +227,4 @@ | ||
} else { | ||
this.setState((previousState) => { | ||
return { | ||
messagesContainerHeight: newMessagesContainerHeight, | ||
}; | ||
this.setState({ | ||
messagesContainerHeight: newMessagesContainerHeight, | ||
}); | ||
@@ -245,6 +245,4 @@ } | ||
} else { | ||
this.setState((previousState) => { | ||
return { | ||
messagesContainerHeight: newMessagesContainerHeight, | ||
}; | ||
this.setState({ | ||
messagesContainerHeight: newMessagesContainerHeight, | ||
}); | ||
@@ -275,18 +273,2 @@ } | ||
onTouchStart() { | ||
this._touchStarted = true; | ||
} | ||
onTouchMove() { | ||
this._touchStarted = false; | ||
} | ||
// handle Tap event to dismiss keyboard | ||
onTouchEnd() { | ||
if (this._touchStarted === true) { | ||
dismissKeyboard(); | ||
} | ||
this._touchStarted = false; | ||
} | ||
renderMessages() { | ||
@@ -339,3 +321,3 @@ const AnimatedView = this.props.isAnimated === true ? Animated.View : View; | ||
} | ||
}, 200); | ||
}, 100); | ||
} | ||
@@ -345,8 +327,9 @@ } | ||
resetInputToolbar() { | ||
this.setState((previousState) => { | ||
return { | ||
text: '', | ||
composerHeight: MIN_COMPOSER_HEIGHT, | ||
messagesContainerHeight: this.prepareMessagesContainerHeight(this.getMaxHeight() - this.getMinInputToolbarHeight() - this.getKeyboardHeight() + this.getBottomOffset()), | ||
}; | ||
if (this.textInput) { | ||
this.textInput.clear(); | ||
} | ||
this.setState({ | ||
text: '', | ||
composerHeight: MIN_COMPOSER_HEIGHT, | ||
messagesContainerHeight: this.prepareMessagesContainerHeight(this.getMaxHeight() - this.getMinInputToolbarHeight() - this.getKeyboardHeight() + this.props.bottomOffset), | ||
}); | ||
@@ -359,25 +342,53 @@ } | ||
onType(e) { | ||
if (this.getIsTypingDisabled() === true) { | ||
onInputSizeChanged(size) { | ||
const newComposerHeight = Math.max(MIN_COMPOSER_HEIGHT, Math.min(MAX_COMPOSER_HEIGHT, size.height)); | ||
const newMessagesContainerHeight = this.getMaxHeight() - this.calculateInputToolbarHeight(newComposerHeight) - this.getKeyboardHeight() + this.getBottomOffset(); | ||
this.setState({ | ||
composerHeight: newComposerHeight, | ||
messagesContainerHeight: this.prepareMessagesContainerHeight(newMessagesContainerHeight), | ||
}); | ||
} | ||
onInputTextChanged(text) { | ||
if (this.getIsTypingDisabled()) { | ||
return; | ||
} | ||
if (this.props.onInputTextChanged) { | ||
this.props.onInputTextChanged(text); | ||
} | ||
this.setState({text}); | ||
} | ||
let newComposerHeight = null; | ||
if (e.nativeEvent && e.nativeEvent.contentSize) { | ||
newComposerHeight = Math.max(MIN_COMPOSER_HEIGHT, Math.min(MAX_COMPOSER_HEIGHT, e.nativeEvent.contentSize.height)); | ||
} else { | ||
newComposerHeight = MIN_COMPOSER_HEIGHT; | ||
onInitialLayoutViewLayout(e) { | ||
const layout = e.nativeEvent.layout; | ||
if (layout.height <= 0) { | ||
return; | ||
} | ||
const newMessagesContainerHeight = this.getMaxHeight() - this.calculateInputToolbarHeight(newComposerHeight) - this.getKeyboardHeight() + this.getBottomOffset(); | ||
const newText = e.nativeEvent.text; | ||
this.setState((previousState) => { | ||
return { | ||
text: newText, | ||
composerHeight: newComposerHeight, | ||
messagesContainerHeight: this.prepareMessagesContainerHeight(newMessagesContainerHeight), | ||
}; | ||
this.setMaxHeight(layout.height); | ||
InteractionManager.runAfterInteractions(() => { | ||
this.setState({ | ||
isInitialized: true, | ||
text: '', | ||
composerHeight: MIN_COMPOSER_HEIGHT, | ||
messagesContainerHeight: this.prepareMessagesContainerHeight(this.getMaxHeight() - this.getMinInputToolbarHeight()), | ||
}); | ||
}); | ||
} | ||
onMainViewLayout(e) { | ||
if (Platform.OS === 'android') { | ||
// fix an issue when keyboard is dismissing during the initialization | ||
const layout = e.nativeEvent.layout; | ||
if (this.getMaxHeight() !== layout.height && this.getIsFirstLayout() === true) { | ||
this.setMaxHeight(layout.height); | ||
this.setState({ | ||
messagesContainerHeight: this.prepareMessagesContainerHeight(this.getMaxHeight() - this.getMinInputToolbarHeight()), | ||
}); | ||
} | ||
} | ||
if (this.getIsFirstLayout() === true) { | ||
this.setIsFirstLayout(false); | ||
} | ||
} | ||
renderInputToolbar() { | ||
@@ -388,6 +399,14 @@ const inputToolbarProps = { | ||
composerHeight: Math.max(MIN_COMPOSER_HEIGHT, this.state.composerHeight), | ||
onChange: this.onType, | ||
onSend: this.onSend, | ||
onInputSizeChanged: this.onInputSizeChanged, | ||
onTextChanged: this.onInputTextChanged, | ||
textInputProps: { | ||
...this.props.textInputProps, | ||
ref: textInput => this.textInput = textInput, | ||
maxLength: this.getIsTypingDisabled() ? 0 : null | ||
} | ||
}; | ||
if (this.getIsTypingDisabled()) { | ||
inputToolbarProps.textInputProps.maxLength = 0; | ||
} | ||
if (this.props.renderInputToolbar) { | ||
@@ -424,20 +443,3 @@ return this.props.renderInputToolbar(inputToolbarProps); | ||
<ActionSheet ref={component => this._actionSheetRef = component}> | ||
<View | ||
style={styles.container} | ||
onLayout={(e) => { | ||
if (Platform.OS === 'android') { | ||
// fix an issue when keyboard is dismissing during the initialization | ||
const layout = e.nativeEvent.layout; | ||
if (this.getMaxHeight() !== layout.height && this.getIsFirstLayout() === true) { | ||
this.setMaxHeight(layout.height); | ||
this.setState({ | ||
messagesContainerHeight: this.prepareMessagesContainerHeight(this.getMaxHeight() - this.getMinInputToolbarHeight()), | ||
}); | ||
} | ||
} | ||
if (this.getIsFirstLayout() === true) { | ||
this.setIsFirstLayout(false); | ||
} | ||
}} | ||
> | ||
<View style={styles.container} onLayout={this.onMainViewLayout}> | ||
{this.renderMessages()} | ||
@@ -450,17 +452,3 @@ {this.renderInputToolbar()} | ||
return ( | ||
<View | ||
style={styles.container} | ||
onLayout={(e) => { | ||
const layout = e.nativeEvent.layout; | ||
this.setMaxHeight(layout.height); | ||
InteractionManager.runAfterInteractions(() => { | ||
this.setState({ | ||
isInitialized: true, | ||
text: '', | ||
composerHeight: MIN_COMPOSER_HEIGHT, | ||
messagesContainerHeight: this.prepareMessagesContainerHeight(this.getMaxHeight() - this.getMinInputToolbarHeight()), | ||
}); | ||
}); | ||
}} | ||
> | ||
<View style={styles.container} onLayout={this.onInitialLayoutViewLayout}> | ||
{this.renderLoading()} | ||
@@ -495,2 +483,6 @@ </View> | ||
}), | ||
keyboardShouldPersistTaps: Platform.select({ | ||
ios: 'never', | ||
android: 'always', | ||
}), | ||
renderAccessory: null, | ||
@@ -521,2 +513,3 @@ renderActions: null, | ||
onSend: React.PropTypes.func, | ||
onInputTextChanged: React.PropTypes.func, | ||
loadEarlier: React.PropTypes.bool, | ||
@@ -546,2 +539,3 @@ onLoadEarlier: React.PropTypes.func, | ||
isLoadingEarlier: React.PropTypes.bool, | ||
keyboardShouldPersistTaps: React.PropTypes.oneOf(['always', 'never', 'handled']), | ||
}; | ||
@@ -563,2 +557,4 @@ | ||
Time, | ||
GiftedAvatar, | ||
utils | ||
}; |
@@ -9,2 +9,3 @@ import React from 'react'; | ||
import Send from './Send'; | ||
import Actions from './Actions'; | ||
@@ -15,2 +16,4 @@ export default class InputToolbar extends React.Component { | ||
return this.props.renderActions(this.props); | ||
} else if (this.props.onPressActionButton) { | ||
return <Actions {...this.props} />; | ||
} | ||
@@ -94,2 +97,3 @@ return null; | ||
renderComposer: React.PropTypes.func, | ||
onPressActionButton: React.PropTypes.func, | ||
containerStyle: View.propTypes.style, | ||
@@ -96,0 +100,0 @@ primaryStyle: View.propTypes.style, |
@@ -45,2 +45,3 @@ import React from 'react'; | ||
disabled={this.props.isLoadingEarlier === true} | ||
accessibilityTraits="button" | ||
> | ||
@@ -47,0 +48,0 @@ <View style={[styles.wrapper, this.props.wrapperStyle]}> |
@@ -7,4 +7,2 @@ import React from 'react'; | ||
import moment from 'moment'; | ||
import Avatar from './Avatar'; | ||
@@ -14,34 +12,18 @@ import Bubble from './Bubble'; | ||
import {isSameUser, isSameDay} from './utils'; | ||
export default class Message extends React.Component { | ||
isSameDay(currentMessage = {}, diffMessage = {}) { | ||
let diff = 0; | ||
if (diffMessage.createdAt && currentMessage.createdAt) { | ||
diff = Math.abs(moment(diffMessage.createdAt).startOf('day').diff(moment(currentMessage.createdAt).startOf('day'), 'days')); | ||
} else { | ||
diff = 1; | ||
getInnerComponentProps() { | ||
const {containerStyle, ...props} = this.props; | ||
return { | ||
...props, | ||
isSameUser, | ||
isSameDay | ||
} | ||
if (diff === 0) { | ||
return true; | ||
} | ||
return false; | ||
} | ||
isSameUser(currentMessage = {}, diffMessage = {}) { | ||
if (diffMessage.user && currentMessage.user) { | ||
if (diffMessage.user._id === currentMessage.user._id) { | ||
return true; | ||
} | ||
} | ||
return false; | ||
} | ||
renderDay() { | ||
if (this.props.currentMessage.createdAt) { | ||
const {containerStyle, ...other} = this.props; | ||
const dayProps = { | ||
...other, | ||
isSameUser: this.isSameUser, | ||
isSameDay: this.isSameDay, | ||
}; | ||
const dayProps = this.getInnerComponentProps(); | ||
if (this.props.renderDay) { | ||
@@ -56,8 +38,3 @@ return this.props.renderDay(dayProps); | ||
renderBubble() { | ||
const {containerStyle, ...other} = this.props; | ||
const bubbleProps = { | ||
...other, | ||
isSameUser: this.isSameUser, | ||
isSameDay: this.isSameDay, | ||
}; | ||
const bubbleProps = this.getInnerComponentProps(); | ||
if (this.props.renderBubble) { | ||
@@ -71,9 +48,3 @@ return this.props.renderBubble(bubbleProps); | ||
if (this.props.user._id !== this.props.currentMessage.user._id) { | ||
const {containerStyle, ...other} = this.props; | ||
const avatarProps = { | ||
...other, | ||
isSameUser: this.isSameUser, | ||
isSameDay: this.isSameDay, | ||
}; | ||
const avatarProps = this.getInnerComponentProps(); | ||
return <Avatar {...avatarProps}/>; | ||
@@ -89,3 +60,3 @@ } | ||
<View style={[styles[this.props.position].container, { | ||
marginBottom: this.isSameUser(this.props.currentMessage, this.props.nextMessage) ? 2 : 10, | ||
marginBottom: isSameUser(this.props.currentMessage, this.props.nextMessage) ? 2 : 10, | ||
}, this.props.containerStyle[this.props.position]]}> | ||
@@ -92,0 +63,0 @@ {this.props.position === 'left' ? this.renderAvatar() : null} |
@@ -143,3 +143,2 @@ import React from 'react'; | ||
enableEmptySections={true} | ||
keyboardShouldPersistTaps={true} | ||
automaticallyAdjustContentInsets={false} | ||
@@ -149,2 +148,5 @@ initialListSize={20} | ||
{...this.props.listViewProps} | ||
enableEmptySections={true} | ||
dataSource={this.state.dataSource} | ||
@@ -167,2 +169,3 @@ | ||
renderMessage: null, | ||
listViewProps: {}, | ||
onLoadEarlier: () => { | ||
@@ -178,2 +181,3 @@ }, | ||
onLoadEarlier: React.PropTypes.func, | ||
listViewProps: React.PropTypes.object, | ||
}; |
@@ -6,12 +6,24 @@ import React from 'react'; | ||
View, | ||
Dimensions, | ||
} from 'react-native'; | ||
import Lightbox from 'react-native-lightbox'; | ||
export default class MessageImage extends React.Component { | ||
render() { | ||
const { width, height } = Dimensions.get('window'); | ||
return ( | ||
<View style={[styles.container, this.props.containerStyle]}> | ||
<Image | ||
style={[styles.image, this.props.imageStyle]} | ||
source={{uri: this.props.currentMessage.image}} | ||
/> | ||
<Lightbox | ||
activeProps={{ | ||
style: [styles.imageActive, { width, height }], | ||
}} | ||
{...this.props.lightboxProps} | ||
> | ||
<Image | ||
{...this.props.imageProps} | ||
style={[styles.image, this.props.imageStyle]} | ||
source={{uri: this.props.currentMessage.image}} | ||
/> | ||
</Lightbox> | ||
</View> | ||
@@ -32,2 +44,5 @@ ); | ||
}, | ||
imageActive: { | ||
resizeMode: 'contain', | ||
}, | ||
}); | ||
@@ -41,2 +56,4 @@ | ||
imageStyle: {}, | ||
imageProps: {}, | ||
lightboxProps: {}, | ||
}; | ||
@@ -48,2 +65,4 @@ | ||
imageStyle: Image.propTypes.style, | ||
imageProps: React.PropTypes.object, | ||
lightboxProps: React.PropTypes.object, | ||
}; |
@@ -24,2 +24,3 @@ import React from 'react'; | ||
}} | ||
accessibilityTraits="button" | ||
> | ||
@@ -26,0 +27,0 @@ <Text style={[styles.text, this.props.textStyle]}>{this.props.label}</Text> |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
GitHub dependency
Supply chain riskContains a dependency which resolves to a GitHub URL. Dependencies fetched from GitHub specifiers are not immutable can be used to inject untrusted code or reduce the likelihood of a reproducible install.
Found 1 instance in 1 package
Manifest confusion
Supply chain riskThis package has inconsistent metadata. This could be malicious or caused by an error when publishing the package.
Found 1 instance in 1 package
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Git dependency
Supply chain riskContains a dependency which resolves to a remote git URL. Dependencies fetched from git URLs are not immutable can be used to inject untrusted code or reduce the likelihood of a reproducible install.
Found 1 instance in 1 package
118839
35
1983
145
7
2
+ Addedreact-native-lightbox@oblador/react-native-lightbox#c84a8543d4511fe6a44c3d7820747c9c1bddd875
+ Added@exponent/react-native-action-sheet@0.3.0(transitive)
+ Addedmoment@2.17.1(transitive)
+ Addedreact-native-communications@2.1.1(transitive)
+ Addedreact-native-invertible-scroll-view@1.0.0(transitive)
+ Addedreact-native-parsed-text@0.0.16(transitive)
- Removedreact-native-dismiss-keyboard@^1.0.0
- Removedcharenc@0.0.2(transitive)
- Removedcreate-react-class@15.7.0(transitive)
- Removedcrypt@0.0.2(transitive)
- Removedis-buffer@1.1.6(transitive)
- Removedjs-tokens@4.0.0(transitive)
- Removedloose-envify@1.4.0(transitive)
- Removedmd5@2.3.0(transitive)
- Removedmoment@2.30.1(transitive)
- Removedobject-assign@4.1.1(transitive)
- Removedprop-types@15.8.1(transitive)
- Removedreact-is@16.13.1(transitive)
- Removedreact-native-communications@2.2.1(transitive)
- Removedreact-native-dismiss-keyboard@1.0.0(transitive)
- Removedreact-native-invertible-scroll-view@1.1.1(transitive)
- Removedreact-native-parsed-text@0.0.15(transitive)
Updatedmd5@2.1.1
Updatedmoment@2.17.1
Updatedshallowequal@0.2.2