diff --git a/.babelrc b/.babelrc new file mode 100644 index 000000000..ae09dd2a3 --- /dev/null +++ b/.babelrc @@ -0,0 +1,3 @@ +{ + "presets": ["react-native"] +} diff --git a/.eslintignore b/.eslintignore index 355bd39fc..da5ffb9ef 100644 --- a/.eslintignore +++ b/.eslintignore @@ -1,3 +1,5 @@ node_modules android ios +example +example-slack-message diff --git a/.eslintrc b/.eslintrc index 3b80aa885..af288cedb 100644 --- a/.eslintrc +++ b/.eslintrc @@ -1,12 +1,23 @@ { - "extends": "cooperka/react-native", - - "env": { - "browser": true, - "jest": true - }, - - // Any rules here will override those from - // https://github.com/cooperka/eslint-config-cooperka. - "rules": {} -} + "extends": "cooperka/react-native", + "env": { + "browser": true, + "jest": true + }, + "rules": { + "no-underscore-dangle": 0, + "import/no-unresolved": [ + 2, + { + "ignore": [ + "react", + "react-native" + ] + } + ], + "import/no-extraneous-dependencies": 0, + "import/extensions": 0, + "react/no-unused-prop-types": 0, + "react/no-typos": 0 + } +} \ No newline at end of file diff --git a/.gitignore b/.gitignore index 86c017f5e..cf53876e6 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,4 @@ npm-debug.log TODO.md .idea +.vscode \ No newline at end of file diff --git a/.npmignore b/.npmignore index 5e4f1893a..f07af2a7d 100644 --- a/.npmignore +++ b/.npmignore @@ -1,3 +1,5 @@ example/ TODO.md screenshots/ +.babelrc +tests/ \ No newline at end of file diff --git a/.watchmanconfig b/.watchmanconfig index 51ca5d1c0..e1abed318 100644 --- a/.watchmanconfig +++ b/.watchmanconfig @@ -1,7 +1,3 @@ { - "ignore_dirs": [ - ".git", - "node_modules", - "example" - ] + "ignore_dirs": [".git", "node_modules", "example"] } diff --git a/ISSUE_TEMPLATE.md b/ISSUE_TEMPLATE.md index 353a2b792..ae5292ea1 100644 --- a/ISSUE_TEMPLATE.md +++ b/ISSUE_TEMPLATE.md @@ -12,6 +12,8 @@ #### Additional Information +* Nodejs version: [FILL THIS OUT] +* React version: [FILL THIS OUT] * React Native version: [FILL THIS OUT] * react-native-gifted-chat version: [FILL THIS OUT] * Platform(s) (iOS, Android, or both?): [FILL THIS OUT] diff --git a/README.md b/README.md index 497ebd1d8..907223954 100644 --- a/README.md +++ b/README.md @@ -1,49 +1,65 @@ -# Gifted Chat - -[![npm downloads](https://img.shields.io/npm/dm/react-native-gifted-chat.svg)](https://www.npmjs.com/package/react-native-gifted-chat) -[![npm version](https://img.shields.io/npm/v/react-native-gifted-chat.svg)](https://www.npmjs.com/package/react-native-gifted-chat) -[![Latest GitHub tag](https://img.shields.io/github/tag/FaridSafi/react-native-gifted-chat.svg)](https://github.com/FaridSafi/react-native-gifted-chat) - -The most complete chat UI for React Native (formerly known as Gifted Messenger). - -![screenshot 1](https://raw.githubusercontent.com/FaridSafi/react-native-gifted-chat/master/screenshots/gifted-chat-1.png) -![screenshot 2](https://raw.githubusercontent.com/FaridSafi/react-native-gifted-chat/master/screenshots/gifted-chat-2.png) +

+    react-native-gifted-chat-1 +

+ +

+ 💬 Gifted Chat +

+

+ The most complete chat UI for React Native
+ formerly known as Gifted Messenger +

+

+ + npm dowloads + npm version + build +

+

+  build + +

+ +

+ +
+  Demo on snack (Expo) +

## Features -- Fully customizable components -- Composer actions (to attach photos, etc.) -- Load earlier messages -- Copy messages to clipboard -- Touchable links using [react-native-parsed-text](https://github.com/taskrabbit/react-native-parsed-text) -- Avatar as user's initials -- Localized dates -- Multiline TextInput -- InputToolbar avoiding keyboard -- Redux support -- System message +* Fully customizable components +* Composer actions (to attach photos, etc.) +* Load earlier messages +* Copy messages to clipboard +* Touchable links using [react-native-parsed-text](https://github.com/taskrabbit/react-native-parsed-text) +* Avatar as user's initials +* Localized dates +* Multiline TextInput +* InputToolbar avoiding keyboard +* Redux support +* System message ## Dependency -- Use version `0.2.x` for RN `>= 0.44.0` -- Use version `0.1.x` for RN `>= 0.40.0` -- Use version `0.0.10` for RN `< 0.40.0` +* Use version `0.2.x` for RN `>= 0.44.0` +* Use version `0.1.x` for RN `>= 0.40.0` +* Use version `0.0.10` for RN `< 0.40.0` ## Installation -- Using [npm](https://www.npmjs.com/#getting-started): `npm install react-native-gifted-chat --save` -- Using [Yarn](https://yarnpkg.com/): `yarn add react-native-gifted-chat` +* Using [npm](https://www.npmjs.com/#getting-started): `npm install react-native-gifted-chat --save` +* Using [Yarn](https://yarnpkg.com/): `yarn add react-native-gifted-chat` ## Example ```jsx -import { GiftedChat } from 'react-native-gifted-chat'; +import { GiftedChat } from 'react-native-gifted-chat' class Example extends React.Component { - state = { messages: [], - }; + } componentWillMount() { this.setState({ @@ -59,33 +75,36 @@ class Example extends React.Component { }, }, ], - }); + }) } onSend(messages = []) { - this.setState((previousState) => ({ + this.setState(previousState => ({ messages: GiftedChat.append(previousState.messages, messages), - })); + })) } render() { return ( this.onSend(messages)} + onSend={messages => this.onSend(messages)} user={{ _id: 1, }} /> - ); + ) } - } ``` ## Advanced example -See [example/App.js](example/App.js) for a working demo! +See [`example/App.js`](example/App.js) for a working demo! + +## "Slack" example + +See the files in [`example-slack-message`](example-slack-message) for an example of how to override the default UI to make something that looks more like Slack -- with usernames displayed and all messages on the left. ## Message object @@ -120,65 +139,66 @@ e.g. System Message ## Props -- **`messages`** _(Array)_ - Messages to display -- **`text`** _(String)_ - Input text; default is `undefined`, but if specified, it will override GiftedChat's internal state (e.g. for redux; [see notes below](#notes-for-redux)) -- **`placeholder`** _(String)_ - Placeholder when `text` is empty; default is `'Type a message...'` -- **`messageIdGenerator`** _(Function)_ - Generate an id for new messages. Defaults to UUID v4, generated by [uuid](https://github.com/kelektiv/node-uuid) -- **`user`** _(Object)_ - User sending the messages: `{ _id, name, avatar }` -- **`onSend`** _(Function)_ - Callback when sending a message -- **`locale`** _(String)_ - Locale to localize the dates -- **`timeFormat`** _(String)_ - Format to use for rendering times; default is `'LT'` -- **`dateFormat`** _(String)_ - Format to use for rendering dates; default is `'ll'` -- **`isAnimated`** _(Bool)_ - Animates the view when the keyboard appears -- **`loadEarlier`** _(Bool)_ - Enables the "Load earlier messages" button -- **`onLoadEarlier`** _(Function)_ - Callback when loading earlier messages -- **`isLoadingEarlier`** _(Bool)_ - Display an `ActivityIndicator` when loading earlier messages -- **`renderLoading`** _(Function)_ - Render a loading view when initializing -- **`renderLoadEarlier`** _(Function)_ - Custom "Load earlier messages" button -- **`renderAvatar`** _(Function)_ - Custom message avatar; set to `null` to not render any avatar for the message -- **`showUserAvatar`** _(Bool)_ - Whether to render an avatar for the current user; default is `false`, only show avatars for other users -- **`onPressAvatar`** _(Function(`user`))_ - Callback when a message avatar is tapped -- **`renderAvatarOnTop`** _(Bool)_ - Render the message avatar at the top of consecutive messages, rather than the bottom; default is `false` -- **`renderBubble`** _(Function)_ - Custom message bubble -- **`renderSystemMessage`** _(Function)_ - Custom system message -- **`onLongPress`** _(Function(`context`, `message`))_ - Callback when a message bubble is long-pressed; default is to show an ActionSheet with "Copy Text" (see [example using `showActionSheetWithOptions()`](https://github.com/FaridSafi/react-native-gifted-chat/blob/master@%7B2017-09-25%7D/src/Bubble.js#L96-L119)) -- **`renderMessage`** _(Function)_ - Custom message container -- **`renderMessageText`** _(Function)_ - Custom message text -- **`renderMessageImage`** _(Function)_ - Custom message image -- **`imageProps`** _(Object)_ - Extra props to be passed to the [``](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)_ - Custom view inside the bubble -- **`renderDay`** _(Function)_ - Custom day above a message -- **`renderTime`** _(Function)_ - Custom time inside a message -- **`renderFooter`** _(Function)_ - Custom footer component on the ListView, e.g. `'User is typing...'`; see [example/App.js](example/App.js) for an example -- **`renderChatFooter`** _(Function)_ - Custom component to render below the MessageContainer (separate from the ListView) -- **`renderInputToolbar`** _(Function)_ - Custom message composer container -- **`renderComposer`** _(Function)_ - Custom text input message composer -- **`renderActions`** _(Function)_ - Custom action button on the left of the message composer -- **`renderSend`** _(Function)_ - Custom send button; you can pass children to the original `Send` component quite easily, for example to use a custom icon ([example](https://github.com/FaridSafi/react-native-gifted-chat/pull/487)) -- **`renderAccessory`** _(Function)_ - Custom second line of actions below the message composer -- **`onPressActionButton`** _(Function)_ - Callback when the Action button is pressed (if set, the default `actionSheet` will not be used) -- **`bottomOffset`** _(Integer)_ - Distance of the chat from the bottom of the screen (e.g. useful if you display a tab bar) -- **`minInputToolbarHeight`** _(Integer)_ - Minimum height of the input toolbar; default is `44` -- **`listViewProps`** _(Object)_ - Extra props to be passed to the messages [``](https://facebook.github.io/react-native/docs/listview.html); some props can't be overridden, see the code in `MessageContainer.render()` for details -- **`textInputProps`** _(Object)_ - Extra props to be passed to the [``](https://facebook.github.io/react-native/docs/textinput.html) -- **`keyboardShouldPersistTaps`** _(Enum)_ - Determines whether the keyboard should stay visible after a tap; see [``](https://facebook.github.io/react-native/docs/scrollview.html) docs -- **`onInputTextChanged`** _(Function)_ - Callback when the input text changes -- **`maxInputLength`** _(Integer)_ - Max message composer TextInput length -- **`parsePatterns`** _(Function)_ - Custom parse patterns for [react-native-parsed-text](https://github.com/taskrabbit/react-native-parsed-text) used to linkify message content (like URLs and phone numbers), e.g.: - - ```js - [ - { type: 'phone', style: linkStyle, onPress: this.onPressPhoneNumber }, - { pattern: /#(\w+)/, style: { ...linkStyle, styles.hashtag }, onPress: this.onPressHashtag }, - ]} - /> - ``` +* **`messages`** _(Array)_ - Messages to display +* **`text`** _(String)_ - Input text; default is `undefined`, but if specified, it will override GiftedChat's internal state (e.g. for redux; [see notes below](#notes-for-redux)) +* **`placeholder`** _(String)_ - Placeholder when `text` is empty; default is `'Type a message...'` +* **`messageIdGenerator`** _(Function)_ - Generate an id for new messages. Defaults to UUID v4, generated by [uuid](https://github.com/kelektiv/node-uuid) +* **`user`** _(Object)_ - User sending the messages: `{ _id, name, avatar }` +* **`onSend`** _(Function)_ - Callback when sending a message +* **`locale`** _(String)_ - Locale to localize the dates +* **`timeFormat`** _(String)_ - Format to use for rendering times; default is `'LT'` +* **`dateFormat`** _(String)_ - Format to use for rendering dates; default is `'ll'` +* **`isAnimated`** _(Bool)_ - Animates the view when the keyboard appears +* **`loadEarlier`** _(Bool)_ - Enables the "Load earlier messages" button +* **`onLoadEarlier`** _(Function)_ - Callback when loading earlier messages +* **`isLoadingEarlier`** _(Bool)_ - Display an `ActivityIndicator` when loading earlier messages +* **`renderLoading`** _(Function)_ - Render a loading view when initializing +* **`renderLoadEarlier`** _(Function)_ - Custom "Load earlier messages" button +* **`renderAvatar`** _(Function)_ - Custom message avatar; set to `null` to not render any avatar for the message +* **`showUserAvatar`** _(Bool)_ - Whether to render an avatar for the current user; default is `false`, only show avatars for other users +* **`onPressAvatar`** _(Function(`user`))_ - Callback when a message avatar is tapped +* **`renderAvatarOnTop`** _(Bool)_ - Render the message avatar at the top of consecutive messages, rather than the bottom; default is `false` +* **`renderBubble`** _(Function)_ - Custom message bubble +* **`renderSystemMessage`** _(Function)_ - Custom system message +* **`onLongPress`** _(Function(`context`, `message`))_ - Callback when a message bubble is long-pressed; default is to show an ActionSheet with "Copy Text" (see [example using `showActionSheetWithOptions()`](https://github.com/FaridSafi/react-native-gifted-chat/blob/master@%7B2017-09-25%7D/src/Bubble.js#L96-L119)) +* **`inverted`** _(Bool)_ - Reverses display order of `messages`; default is `true` +* **`renderMessage`** _(Function)_ - Custom message container +* **`renderMessageText`** _(Function)_ - Custom message text +* **`renderMessageImage`** _(Function)_ - Custom message image +* **`imageProps`** _(Object)_ - Extra props to be passed to the [``](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)_ - Custom view inside the bubble +* **`renderDay`** _(Function)_ - Custom day above a message +* **`renderTime`** _(Function)_ - Custom time inside a message +* **`renderFooter`** _(Function)_ - Custom footer component on the ListView, e.g. `'User is typing...'`; see [example/App.js](example/App.js) for an example +* **`renderChatFooter`** _(Function)_ - Custom component to render below the MessageContainer (separate from the ListView) +* **`renderInputToolbar`** _(Function)_ - Custom message composer container +* **`renderComposer`** _(Function)_ - Custom text input message composer +* **`renderActions`** _(Function)_ - Custom action button on the left of the message composer +* **`renderSend`** _(Function)_ - Custom send button; you can pass children to the original `Send` component quite easily, for example to use a custom icon ([example](https://github.com/FaridSafi/react-native-gifted-chat/pull/487)) +* **`renderAccessory`** _(Function)_ - Custom second line of actions below the message composer +* **`onPressActionButton`** _(Function)_ - Callback when the Action button is pressed (if set, the default `actionSheet` will not be used) +* **`bottomOffset`** _(Integer)_ - Distance of the chat from the bottom of the screen (e.g. useful if you display a tab bar) +* **`minInputToolbarHeight`** _(Integer)_ - Minimum height of the input toolbar; default is `44` +* **`listViewProps`** _(Object)_ - Extra props to be passed to the messages [``](https://facebook.github.io/react-native/docs/listview.html); some props can't be overridden, see the code in `MessageContainer.render()` for details +* **`textInputProps`** _(Object)_ - Extra props to be passed to the [``](https://facebook.github.io/react-native/docs/textinput.html) +* **`keyboardShouldPersistTaps`** _(Enum)_ - Determines whether the keyboard should stay visible after a tap; see [``](https://facebook.github.io/react-native/docs/scrollview.html) docs +* **`onInputTextChanged`** _(Function)_ - Callback when the input text changes +* **`maxInputLength`** _(Integer)_ - Max message composer TextInput length +* **`parsePatterns`** _(Function)_ - Custom parse patterns for [react-native-parsed-text](https://github.com/taskrabbit/react-native-parsed-text) used to linkify message content (like URLs and phone numbers), e.g.: + + ```js + [ + { type: 'phone', style: linkStyle, onPress: this.onPressPhoneNumber }, + { pattern: /#(\w+)/, style: { ...linkStyle, styles.hashtag }, onPress: this.onPressHashtag }, + ]} + /> + ``` ## Imperative methods -- `focusTextInput()` - Open the keyboard and focus the text input box +* `focusTextInput()` - Open the keyboard and focus the text input box ## Notes for [Redux](https://github.com/reactjs/redux) @@ -191,25 +211,26 @@ simply implement `onInputTextChanged` to receive typing events and reset events ```js this.setCustomText(text)} - /* ... */ /> + onInputTextChanged={text => this.setCustomText(text)} + /* ... */ +/> ``` ## Notes for Android If you are using Create React Native App / Expo, no Android specific installation steps are required -- you can skip this section. Otherwise we recommend modifying your project configuration as follows. -- Make sure you have `android:windowSoftInputMode="adjustResize"` in your `AndroidManifest.xml`: +* Make sure you have `android:windowSoftInputMode="adjustResize"` in your `AndroidManifest.xml`: - ```xml - - ``` + ```xml + + ``` -- If you plan to use `GiftedChat` inside a `Modal`, see [#200](https://github.com/FaridSafi/react-native-gifted-chat/issues/200). +* If you plan to use `GiftedChat` inside a `Modal`, see [#200](https://github.com/FaridSafi/react-native-gifted-chat/issues/200). ## Notes for local development @@ -225,10 +246,28 @@ with any changes you make to the library during development. Steps: Note that it's important for `wml start` to come **after** `npm start`, or you'll get `Can't find entry file index.js` errors. If you have any issues, you can clear your watches using `watchman watch-del-all` and try again. +## Questions + +* [How can I set Bubble color for each user?](https://github.com/FaridSafi/react-native-gifted-chat/issues/672) +* [How can I pass style props to InputToolbar design and customize it's color and other styles properties?](https://github.com/FaridSafi/react-native-gifted-chat/issues/662) +* [How can I change the color of the message box?](https://github.com/FaridSafi/react-native-gifted-chat/issues/640) +* [Is there a way to manually dismiss the keyboard?](https://github.com/FaridSafi/react-native-gifted-chat/issues/647) +* [I want to implement a popover that pops right after clicking on a specific avatar, + what is the best implementation in this case and how?](https://github.com/FaridSafi/react-native-gifted-chat/issues/660) +* [Why Textinput is hidden on Android?](https://github.com/FaridSafi/react-native-gifted-chat/issues/578) + ## License -- [MIT](LICENSE) +* [MIT](LICENSE) ## Author Feel free to ask me questions on Twitter [@FaridSafi](https://www.twitter.com/FaridSafi)! + +## Contributors + +* Kevin Cooper [cooperka](https://github.com/cooperka) +* Kfir Golan [kfiroo](https://github.com/kfiroo) +* Bruno Cascio [brunocascio](https://github.com/brunocascio) +* Xavier Carpentier [xcarpentier](https://github.com/xcarpentier) +* [more](https://github.com/FaridSafi/react-native-gifted-chat/graphs/contributors) diff --git a/circle.yml b/circle.yml new file mode 100644 index 000000000..d67018b94 --- /dev/null +++ b/circle.yml @@ -0,0 +1,28 @@ +--- +machine: + environment: + PATH: "${PATH}:${HOME}/${CIRCLE_PROJECT_REPONAME}/node_modules/.bin" + node: + version: 8.2.0 + +dependencies: + override: + - yarn global add codecov + - yarn install --no-progress --no-emoji --ignore-engines + cache_directories: + - "node_modules" + - ~/.yarn + - ~/.cache/yarn + +test: + override: + - yarn run lint + - yarn run test:coverage + - codecov + +# TODO: deployment: +# release: +# tag: /v[0-9]+(\.[0-9]+)*/ +# commands: +# - echo "//registry.npmjs.org/:_authToken=${NPM_TOKEN}" >> ~/.npmrc +# - yarn publish --new-version `./scripts/version.js` \ No newline at end of file diff --git a/example-slack-message/README.md b/example-slack-message/README.md new file mode 100644 index 000000000..a9b2bb07b --- /dev/null +++ b/example-slack-message/README.md @@ -0,0 +1,9 @@ +# "Slack" style UI example + +Credit and inspiration comes from [Slack](https://slack.com/). + +Screenshots to compare: + +| Default style | "Slack" style | +|:-------------:|:-------------:| +| Example with default style | Example with Slack style | diff --git a/example-slack-message/example-default-style.png b/example-slack-message/example-default-style.png new file mode 100644 index 000000000..fdf20398f Binary files /dev/null and b/example-slack-message/example-default-style.png differ diff --git a/example-slack-message/example-slack-style.png b/example-slack-message/example-slack-style.png new file mode 100644 index 000000000..b1da2ad74 Binary files /dev/null and b/example-slack-message/example-slack-style.png differ diff --git a/example-slack-message/src/App.js b/example-slack-message/src/App.js new file mode 100644 index 000000000..69a5018db --- /dev/null +++ b/example-slack-message/src/App.js @@ -0,0 +1,41 @@ +import React, { Component } from 'react'; +import { View, Platform } from 'react-native'; +import PropTypes from 'prop-types'; +import { GiftedChat } from 'react-native-gifted-chat'; +import emojiUtils from 'emoji-utils'; + +import SlackMessage from './SlackMessage'; + +class App extends Component { + + renderMessage(props) { + const { currentMessage: { text: currText } } = props; + + let messageTextStyle; + + // Make "pure emoji" messages much bigger than plain text. + if (currText && emojiUtils.isPureEmojiString(currText)) { + messageTextStyle = { + fontSize: 28, + // Emoji get clipped if lineHeight isn't increased; make it consistent across platforms. + lineHeight: Platform.OS === 'android' ? 34 : 30, + }; + } + + return ( + + ); + } + + render() { + return ( + + ); + } + +} + +export default App; diff --git a/example-slack-message/src/SlackBubble.js b/example-slack-message/src/SlackBubble.js new file mode 100644 index 000000000..ed523474a --- /dev/null +++ b/example-slack-message/src/SlackBubble.js @@ -0,0 +1,284 @@ +/* eslint-disable no-underscore-dangle, no-use-before-define */ + +import React from 'react'; +import { + Text, + Clipboard, + StyleSheet, + TouchableOpacity, + View, + ViewPropTypes, + Platform, +} from 'react-native'; + +import { MessageText, MessageImage, Time, utils } from 'react-native-gifted-chat'; + +const { isSameUser, isSameDay } = utils; + +export default class Bubble extends React.Component { + + constructor(props) { + super(props); + this.onLongPress = this.onLongPress.bind(this); + } + + onLongPress() { + if (this.props.onLongPress) { + this.props.onLongPress(this.context); + } else if (this.props.currentMessage.text) { + const options = [ + 'Copy Text', + 'Cancel', + ]; + const cancelButtonIndex = options.length - 1; + this.context.actionSheet().showActionSheetWithOptions( + { options, cancelButtonIndex }, + (buttonIndex) => { + if (buttonIndex === 0) { + Clipboard.setString(this.props.currentMessage.text); + } + }); + } + } + + renderMessageText() { + if (this.props.currentMessage.text) { + const { containerStyle, wrapperStyle, messageTextStyle, ...messageTextProps } = this.props; + if (this.props.renderMessageText) { + return this.props.renderMessageText(messageTextProps); + } + return ( + + ); + } + return null; + } + + renderMessageImage() { + if (this.props.currentMessage.image) { + const { containerStyle, wrapperStyle, ...messageImageProps } = this.props; + if (this.props.renderMessageImage) { + return this.props.renderMessageImage(messageImageProps); + } + return ; + } + return null; + } + + renderTicks() { + const { currentMessage } = this.props; + if (this.props.renderTicks) { + return this.props.renderTicks(currentMessage); + } + if (currentMessage.user._id !== this.props.user._id) { + return null; + } + if (currentMessage.sent || currentMessage.received) { + return ( + + {currentMessage.sent && } + {currentMessage.received && } + + ); + } + return null; + } + + renderUsername() { + const username = this.props.currentMessage.user.name; + if (username) { + const { containerStyle, wrapperStyle, ...usernameProps } = this.props; + if (this.props.renderUsername) { + return this.props.renderUsername(usernameProps); + } + return ( + + {username} + + ); + } + return null; + } + + renderTime() { + if (this.props.currentMessage.createdAt) { + const { containerStyle, wrapperStyle, ...timeProps } = this.props; + if (this.props.renderTime) { + return this.props.renderTime(timeProps); + } + return ( +