issue #799 merger message views (#876)

On Room Actions, we have Files, Mentions, Starred and Pinned.
They have similar APIs and logic.
All of those could be merged into one generic view (MessagesView).
Maybe even Search could be in this merge.

Note: They're similar, but have own rules (unstar, unpin, etc).

This change may reduce 1MB to our release bundle size, since we're going to remove a lot of boilerplate.
This commit is contained in:
IlarionHalushka 2019-05-10 20:09:07 +03:00 committed by Diego Mello
parent 7e513ee73a
commit 3733f776fc
12 changed files with 281 additions and 735 deletions

View File

@ -24,12 +24,9 @@ import RoomActionsView from './views/RoomActionsView';
import RoomInfoView from './views/RoomInfoView';
import RoomInfoEditView from './views/RoomInfoEditView';
import RoomMembersView from './views/RoomMembersView';
import RoomFilesView from './views/RoomFilesView';
import MentionedMessagesView from './views/MentionedMessagesView';
import StarredMessagesView from './views/StarredMessagesView';
import SearchMessagesView from './views/SearchMessagesView';
import PinnedMessagesView from './views/PinnedMessagesView';
import ThreadMessagesView from './views/ThreadMessagesView';
import MessagesView from './views/MessagesView';
import SelectedUsersView from './views/SelectedUsersView';
import CreateChannelView from './views/CreateChannelView';
import LegalView from './views/LegalView';
@ -108,13 +105,10 @@ const ChatsStack = createStackNavigator({
RoomInfoView,
RoomInfoEditView,
RoomMembersView,
RoomFilesView,
MentionedMessagesView,
StarredMessagesView,
SearchMessagesView,
PinnedMessagesView,
SelectedUsersView,
ThreadMessagesView
ThreadMessagesView,
MessagesView
}, {
defaultNavigationOptions: defaultHeader
});

View File

@ -0,0 +1,15 @@
export default function(message) {
if (/image/.test(message.type)) {
return { image_url: message.url };
}
if (/audio/.test(message.type)) {
return { audio_url: message.url };
}
if (/video/.test(message.type)) {
return { video_url: message.url };
}
return {
title_link: message.url,
type: 'file'
};
}

View File

@ -1,139 +0,0 @@
import React from 'react';
import PropTypes from 'prop-types';
import { FlatList, View, Text } from 'react-native';
import { connect } from 'react-redux';
import { SafeAreaView } from 'react-navigation';
import equal from 'deep-equal';
import LoggedView from '../View';
import styles from './styles';
import Message from '../../containers/message/Message';
import RCActivityIndicator from '../../containers/ActivityIndicator';
import I18n from '../../i18n';
import RocketChat from '../../lib/rocketchat';
import StatusBar from '../../containers/StatusBar';
@connect(state => ({
baseUrl: state.settings.Site_Url || state.server ? state.server.server : '',
customEmojis: state.customEmojis,
user: {
id: state.login.user && state.login.user.id,
username: state.login.user && state.login.user.username,
token: state.login.user && state.login.user.token
}
}))
/** @extends React.Component */
export default class MentionedMessagesView extends LoggedView {
static navigationOptions = {
title: I18n.t('Mentions')
}
static propTypes = {
user: PropTypes.object,
baseUrl: PropTypes.string,
customEmojis: PropTypes.object,
navigation: PropTypes.object
}
constructor(props) {
super('MentionedMessagesView', props);
this.state = {
loading: false,
messages: []
};
this.rid = props.navigation.getParam('rid');
this.t = props.navigation.getParam('t');
}
componentDidMount() {
this.load();
}
shouldComponentUpdate(nextProps, nextState) {
const { loading, messages } = this.state;
if (nextState.loading !== loading) {
return true;
}
if (!equal(nextState.messages, messages)) {
return true;
}
return false;
}
load = async() => {
const {
messages, total, loading
} = this.state;
const { user } = this.props;
if (messages.length === total || loading) {
return;
}
this.setState({ loading: true });
try {
const result = await RocketChat.getMessages(
this.rid,
this.t,
{ 'mentions._id': { $in: [user.id] } },
messages.length
);
if (result.success) {
this.setState(prevState => ({
messages: [...prevState.messages, ...result.messages],
total: result.total,
loading: false
}));
}
} catch (error) {
this.setState({ loading: false });
console.log('MentionedMessagesView -> load -> catch -> error', error);
}
}
renderEmpty = () => (
<View style={styles.listEmptyContainer} testID='mentioned-messages-view'>
<Text style={styles.noDataFound}>{I18n.t('No_mentioned_messages')}</Text>
</View>
)
renderItem = ({ item }) => {
const { user, customEmojis, baseUrl } = this.props;
return (
<Message
customEmojis={customEmojis}
baseUrl={baseUrl}
user={user}
author={item.u}
ts={item.ts}
msg={item.msg}
attachments={item.attachments || []}
timeFormat='MMM Do YYYY, h:mm:ss a'
edited={!!item.editedAt}
header
/>
);
}
render() {
const { messages, loading } = this.state;
if (!loading && messages.length === 0) {
return this.renderEmpty();
}
return (
<SafeAreaView style={styles.list} testID='mentioned-messages-view' forceInset={{ bottom: 'never' }}>
<StatusBar />
<FlatList
data={messages}
renderItem={this.renderItem}
style={styles.list}
keyExtractor={item => item._id}
onEndReached={this.load}
ListFooterComponent={loading ? <RCActivityIndicator /> : null}
/>
</SafeAreaView>
);
}
}

View File

@ -0,0 +1,255 @@
import React from 'react';
import PropTypes from 'prop-types';
import { FlatList, View, Text } from 'react-native';
import { connect } from 'react-redux';
import { SafeAreaView } from 'react-navigation';
import equal from 'deep-equal';
import ActionSheet from 'react-native-action-sheet';
import LoggedView from '../View';
import styles from './styles';
import Message from '../../containers/message/Message';
import RCActivityIndicator from '../../containers/ActivityIndicator';
import I18n from '../../i18n';
import RocketChat from '../../lib/rocketchat';
import StatusBar from '../../containers/StatusBar';
import getFileUrlFromMessage from '../../lib/methods/helpers/getFileUrlFromMessage';
const ACTION_INDEX = 0;
const CANCEL_INDEX = 1;
@connect(state => ({
baseUrl: state.settings.Site_Url || state.server ? state.server.server : '',
customEmojis: state.customEmojis,
user: {
id: state.login.user && state.login.user.id,
username: state.login.user && state.login.user.username,
token: state.login.user && state.login.user.token
}
}))
/** @extends React.Component */
export default class MessagesView extends LoggedView {
static navigationOptions = ({ navigation }) => ({
title: navigation.state.params.name
});
static propTypes = {
user: PropTypes.object,
baseUrl: PropTypes.string,
customEmojis: PropTypes.object,
navigation: PropTypes.object
}
constructor(props) {
super('MessagesView', props);
this.state = {
loading: false,
messages: []
};
this.rid = props.navigation.getParam('rid');
this.t = props.navigation.getParam('t');
this.content = this.defineMessagesViewContent(props.navigation.getParam('name'));
}
componentDidMount() {
this.load();
}
shouldComponentUpdate(nextProps, nextState) {
const { loading, messages } = this.state;
if (nextState.loading !== loading) {
return true;
}
if (!equal(nextState.messages, messages)) {
return true;
}
return false;
}
defineMessagesViewContent = (name) => {
const { messages } = this.state;
const { user, baseUrl, customEmojis } = this.props;
const renderItemCommonProps = item => ({
customEmojis,
baseUrl,
user,
author: item.u || item.user,
ts: item.ts || item.uploadedAt,
timeFormat: 'MMM Do YYYY, h:mm:ss a',
edited: !!item.editedAt,
header: true,
attachments: item.attachments || []
});
return ({
// Files Messages Screen
Files: {
name: I18n.t('Files'),
fetchFunc: async() => {
const result = await RocketChat.getFiles(this.rid, this.t, messages.length);
return { ...result, messages: result.files };
},
noDataMsg: I18n.t('No_files'),
testID: 'room-files-view',
renderItem: (item) => {
const url = getFileUrlFromMessage(item);
return (
<Message
{...renderItemCommonProps(item)}
attachments={[{
title: item.name,
description: item.description,
...url
}]}
/>
);
}
},
// Mentions Messages Screen
Mentions: {
name: I18n.t('Mentions'),
fetchFunc: () => RocketChat.getMessages(
this.rid,
this.t,
{ 'mentions._id': { $in: [user.id] } },
messages.length
),
noDataMsg: I18n.t('No_mentioned_messages'),
testID: 'mentioned-messages-view',
renderItem: item => (
<Message
{...renderItemCommonProps(item)}
msg={item.msg}
/>
)
},
// Starred Messages Screen
Starred: {
name: I18n.t('Starred'),
fetchFunc: () => RocketChat.getMessages(
this.rid,
this.t,
{ 'starred._id': { $in: [user.id] } },
messages.length
),
noDataMsg: I18n.t('No_starred_messages'),
testID: 'starred-messages-view',
renderItem: item => (
<Message
{...renderItemCommonProps(item)}
msg={item.msg}
onLongPress={() => this.onLongPress(item)}
/>
),
actionTitle: I18n.t('Unstar'),
handleActionPress: message => RocketChat.toggleStarMessage(message)
},
// Pinned Messages Screen
Pinned: {
name: I18n.t('Pinned'),
fetchFunc: () => RocketChat.getMessages(this.rid, this.t, { pinned: true }, messages.length),
noDataMsg: I18n.t('No_pinned_messages'),
testID: 'pinned-messages-view',
renderItem: item => (
<Message
{...renderItemCommonProps(item)}
msg={item.msg}
onLongPress={() => this.onLongPress(item)}
/>
),
actionTitle: I18n.t('Unpin'),
handleActionPress: message => RocketChat.togglePinMessage(message)
}
}[name]);
}
load = async() => {
const {
messages, total, loading
} = this.state;
if (messages.length === total || loading) {
return;
}
this.setState({ loading: true });
try {
const result = await this.content.fetchFunc();
if (result.success) {
this.setState({
messages: [...messages, ...result.messages],
total: result.total,
loading: false
});
}
} catch (error) {
this.setState({ loading: false });
console.warn('MessagesView -> catch -> error', error);
}
}
onLongPress = (message) => {
this.setState({ message });
this.showActionSheet();
}
showActionSheet = () => {
ActionSheet.showActionSheetWithOptions({
options: [this.content.actionTitle, I18n.t('Cancel')],
cancelButtonIndex: CANCEL_INDEX,
title: I18n.t('Actions')
}, (actionIndex) => {
this.handleActionPress(actionIndex);
});
}
handleActionPress = async(actionIndex) => {
if (actionIndex === ACTION_INDEX) {
const { message } = this.state;
try {
const result = await this.content.handleActionPress(message);
if (result.success) {
this.setState(prevState => ({
messages: prevState.messages.filter(item => item._id !== message._id),
total: prevState.total - 1
}));
}
} catch (error) {
console.warn('MessagesView -> handleActionPress -> catch -> error', error);
}
}
}
renderEmpty = () => (
<View style={styles.listEmptyContainer} testID={this.content.testID}>
<Text style={styles.noDataFound}>{this.content.noDataMsg}</Text>
</View>
)
renderItem = ({ item }) => this.content.renderItem(item)
render() {
const { messages, loading } = this.state;
if (!loading && messages.length === 0) {
return this.renderEmpty();
}
return (
<SafeAreaView style={styles.list} testID={this.content.testID} forceInset={{ bottom: 'never' }}>
<StatusBar />
<FlatList
data={messages}
renderItem={this.renderItem}
style={styles.list}
keyExtractor={item => item._id}
onEndReached={this.load}
ListFooterComponent={loading ? <RCActivityIndicator /> : null}
/>
</SafeAreaView>
);
}
}

View File

@ -1,178 +0,0 @@
import React from 'react';
import PropTypes from 'prop-types';
import { FlatList, View, Text } from 'react-native';
import { connect } from 'react-redux';
import ActionSheet from 'react-native-action-sheet';
import { SafeAreaView } from 'react-navigation';
import equal from 'deep-equal';
import LoggedView from '../View';
import styles from './styles';
import Message from '../../containers/message/Message';
import RCActivityIndicator from '../../containers/ActivityIndicator';
import I18n from '../../i18n';
import RocketChat from '../../lib/rocketchat';
import StatusBar from '../../containers/StatusBar';
const PIN_INDEX = 0;
const CANCEL_INDEX = 1;
const options = [I18n.t('Unpin'), I18n.t('Cancel')];
@connect(state => ({
baseUrl: state.settings.Site_Url || state.server ? state.server.server : '',
customEmojis: state.customEmojis,
user: {
id: state.login.user && state.login.user.id,
username: state.login.user && state.login.user.username,
token: state.login.user && state.login.user.token
}
}))
/** @extends React.Component */
export default class PinnedMessagesView extends LoggedView {
static navigationOptions = {
title: I18n.t('Pinned')
}
static propTypes = {
user: PropTypes.object,
baseUrl: PropTypes.string,
customEmojis: PropTypes.object,
navigation: PropTypes.object
}
constructor(props) {
super('PinnedMessagesView', props);
this.state = {
loading: false,
messages: []
};
this.rid = props.navigation.getParam('rid');
this.t = props.navigation.getParam('t');
}
componentDidMount() {
this.load();
}
shouldComponentUpdate(nextProps, nextState) {
const { loading, messages } = this.state;
if (nextState.loading !== loading) {
return true;
}
if (!equal(nextState.messages, messages)) {
return true;
}
return false;
}
onLongPress = (message) => {
this.setState({ message });
this.showActionSheet();
}
showActionSheet = () => {
ActionSheet.showActionSheetWithOptions({
options,
cancelButtonIndex: CANCEL_INDEX,
title: I18n.t('Actions')
}, (actionIndex) => {
this.handleActionPress(actionIndex);
});
}
handleActionPress = (actionIndex) => {
switch (actionIndex) {
case PIN_INDEX:
this.unPin();
break;
default:
break;
}
}
unPin = async() => {
const { message } = this.state;
try {
const result = await RocketChat.togglePinMessage(message);
if (result.success) {
this.setState(prevState => ({
messages: prevState.messages.filter(item => item._id !== message._id)
}));
}
} catch (error) {
console.log('PinnedMessagesView -> unPin -> catch -> error', error);
}
}
load = async() => {
const {
messages, total, loading
} = this.state;
if (messages.length === total || loading) {
return;
}
this.setState({ loading: true });
try {
const result = await RocketChat.getMessages(this.rid, this.t, { pinned: true }, messages.length);
if (result.success) {
this.setState(prevState => ({
messages: [...prevState.messages, ...result.messages],
total: result.total,
loading: false
}));
}
} catch (error) {
this.setState({ loading: false });
console.log('PinnedMessagesView -> catch -> error', error);
}
}
renderEmpty = () => (
<View style={styles.listEmptyContainer} testID='pinned-messages-view'>
<Text style={styles.noDataFound}>{I18n.t('No_pinned_messages')}</Text>
</View>
)
renderItem = ({ item }) => {
const { user, customEmojis, baseUrl } = this.props;
return (
<Message
customEmojis={customEmojis}
baseUrl={baseUrl}
user={user}
author={item.u}
ts={item.ts}
msg={item.msg}
attachments={item.attachments || []}
timeFormat='MMM Do YYYY, h:mm:ss a'
header
edited={!!item.editedAt}
onLongPress={() => this.onLongPress(item)}
/>
);
}
render() {
const { messages, loading } = this.state;
if (!loading && messages.length === 0) {
return this.renderEmpty();
}
return (
<SafeAreaView style={styles.list} testID='pinned-messages-view' forceInset={{ bottom: 'never' }}>
<StatusBar />
<FlatList
data={messages}
renderItem={this.renderItem}
style={styles.list}
keyExtractor={item => item._id}
onEndReached={this.load}
ListFooterComponent={loading ? <RCActivityIndicator /> : null}
/>
</SafeAreaView>
);
}
}

View File

@ -1,22 +0,0 @@
import { StyleSheet } from 'react-native';
import sharedStyles from '../Styles';
import { COLOR_WHITE } from '../../constants/colors';
export default StyleSheet.create({
list: {
flex: 1,
backgroundColor: COLOR_WHITE
},
listEmptyContainer: {
flex: 1,
alignItems: 'center',
justifyContent: 'center',
backgroundColor: COLOR_WHITE
},
noDataFound: {
fontSize: 14,
...sharedStyles.textRegular,
...sharedStyles.textColorNormal
}
});

View File

@ -215,22 +215,22 @@ export default class RoomActionsView extends LoggedView {
{
icon: 'file-generic',
name: I18n.t('Files'),
route: 'RoomFilesView',
params: { rid, t },
route: 'MessagesView',
params: { rid, t, name: 'Files' },
testID: 'room-actions-files'
},
{
icon: 'at',
name: I18n.t('Mentions'),
route: 'MentionedMessagesView',
params: { rid, t },
route: 'MessagesView',
params: { rid, t, name: 'Mentions' },
testID: 'room-actions-mentioned'
},
{
icon: 'star',
name: I18n.t('Starred'),
route: 'StarredMessagesView',
params: { rid, t },
route: 'MessagesView',
params: { rid, t, name: 'Starred' },
testID: 'room-actions-starred'
},
{
@ -249,8 +249,8 @@ export default class RoomActionsView extends LoggedView {
{
icon: 'pin',
name: I18n.t('Pinned'),
route: 'PinnedMessagesView',
params: { rid, t },
route: 'MessagesView',
params: { rid, t, name: 'Pinned' },
testID: 'room-actions-pinned'
}
],

View File

@ -1,151 +0,0 @@
import React from 'react';
import PropTypes from 'prop-types';
import { FlatList, View, Text } from 'react-native';
import { connect } from 'react-redux';
import { SafeAreaView } from 'react-navigation';
import equal from 'deep-equal';
import LoggedView from '../View';
import styles from './styles';
import Message from '../../containers/message/Message';
import RCActivityIndicator from '../../containers/ActivityIndicator';
import I18n from '../../i18n';
import RocketChat from '../../lib/rocketchat';
import StatusBar from '../../containers/StatusBar';
@connect(state => ({
baseUrl: state.settings.Site_Url || state.server ? state.server.server : '',
customEmojis: state.customEmojis,
user: {
id: state.login.user && state.login.user.id,
username: state.login.user && state.login.user.username,
token: state.login.user && state.login.user.token
}
}))
/** @extends React.Component */
export default class RoomFilesView extends LoggedView {
static navigationOptions = {
title: I18n.t('Files')
}
static propTypes = {
user: PropTypes.object,
baseUrl: PropTypes.string,
customEmojis: PropTypes.object,
navigation: PropTypes.object
}
constructor(props) {
super('RoomFilesView', props);
this.state = {
loading: false,
messages: []
};
this.rid = props.navigation.getParam('rid');
this.t = props.navigation.getParam('t');
}
componentDidMount() {
this.load();
}
shouldComponentUpdate(nextProps, nextState) {
const { loading, messages } = this.state;
if (nextState.loading !== loading) {
return true;
}
if (!equal(nextState.messages, messages)) {
return true;
}
return false;
}
load = async() => {
const {
messages, total, loading
} = this.state;
if (messages.length === total || loading) {
return;
}
this.setState({ loading: true });
try {
const result = await RocketChat.getFiles(this.rid, this.t, messages.length);
if (result.success) {
this.setState(prevState => ({
messages: [...prevState.messages, ...result.files],
total: result.total,
loading: false
}));
}
} catch (error) {
this.setState({ loading: false });
console.log('RoomFilesView -> catch -> error', error);
}
}
renderEmpty = () => (
<View style={styles.listEmptyContainer} testID='room-files-view'>
<Text style={styles.noDataFound}>{I18n.t('No_files')}</Text>
</View>
)
renderItem = ({ item }) => {
const { user, baseUrl, customEmojis } = this.props;
let url = {};
if (/image/.test(item.type)) {
url = { image_url: item.url };
} else if (/audio/.test(item.type)) {
url = { audio_url: item.url };
} else if (/video/.test(item.type)) {
url = { video_url: item.url };
} else {
url = {
title_link: item.url,
type: 'file'
};
}
return (
<Message
customEmojis={customEmojis}
baseUrl={baseUrl}
user={user}
author={item.user}
ts={item.uploadedAt}
attachments={[{
title: item.name,
description: item.description,
...url
}]}
timeFormat='MMM Do YYYY, h:mm:ss a'
edited={!!item.editedAt}
header
/>
);
}
render() {
const { messages, loading } = this.state;
if (!loading && messages.length === 0) {
return this.renderEmpty();
}
return (
<SafeAreaView style={styles.list} testID='room-files-view' forceInset={{ bottom: 'never' }}>
<StatusBar />
<FlatList
data={messages}
renderItem={this.renderItem}
style={styles.list}
keyExtractor={item => item._id}
onEndReached={this.load}
ListFooterComponent={loading ? <RCActivityIndicator /> : null}
/>
</SafeAreaView>
);
}
}

View File

@ -1,22 +0,0 @@
import { StyleSheet } from 'react-native';
import sharedStyles from '../Styles';
import { COLOR_WHITE } from '../../constants/colors';
export default StyleSheet.create({
list: {
flex: 1,
backgroundColor: COLOR_WHITE
},
listEmptyContainer: {
flex: 1,
alignItems: 'center',
justifyContent: 'center',
backgroundColor: COLOR_WHITE
},
noDataFound: {
fontSize: 14,
...sharedStyles.textRegular,
...sharedStyles.textColorNormal
}
});

View File

@ -1,184 +0,0 @@
import React from 'react';
import PropTypes from 'prop-types';
import { FlatList, View, Text } from 'react-native';
import { connect } from 'react-redux';
import ActionSheet from 'react-native-action-sheet';
import { SafeAreaView } from 'react-navigation';
import equal from 'deep-equal';
import LoggedView from '../View';
import styles from './styles';
import Message from '../../containers/message/Message';
import RCActivityIndicator from '../../containers/ActivityIndicator';
import I18n from '../../i18n';
import RocketChat from '../../lib/rocketchat';
import StatusBar from '../../containers/StatusBar';
const STAR_INDEX = 0;
const CANCEL_INDEX = 1;
const options = [I18n.t('Unstar'), I18n.t('Cancel')];
@connect(state => ({
baseUrl: state.settings.Site_Url || state.server ? state.server.server : '',
customEmojis: state.customEmojis,
user: {
id: state.login.user && state.login.user.id,
username: state.login.user && state.login.user.username,
token: state.login.user && state.login.user.token
}
}))
/** @extends React.Component */
export default class StarredMessagesView extends LoggedView {
static navigationOptions = {
title: I18n.t('Starred')
}
static propTypes = {
user: PropTypes.object,
baseUrl: PropTypes.string,
customEmojis: PropTypes.object,
navigation: PropTypes.object
}
constructor(props) {
super('StarredMessagesView', props);
this.state = {
loading: false,
messages: []
};
this.rid = props.navigation.getParam('rid');
this.t = props.navigation.getParam('t');
}
componentDidMount() {
this.load();
}
shouldComponentUpdate(nextProps, nextState) {
const { loading, messages } = this.state;
if (nextState.loading !== loading) {
return true;
}
if (!equal(nextState.messages, messages)) {
return true;
}
return false;
}
onLongPress = (message) => {
this.setState({ message });
this.showActionSheet();
}
showActionSheet = () => {
ActionSheet.showActionSheetWithOptions({
options,
cancelButtonIndex: CANCEL_INDEX,
title: I18n.t('Actions')
}, (actionIndex) => {
this.handleActionPress(actionIndex);
});
}
handleActionPress = (actionIndex) => {
switch (actionIndex) {
case STAR_INDEX:
this.unStar();
break;
default:
break;
}
}
unStar = async() => {
const { message } = this.state;
try {
const result = await RocketChat.toggleStarMessage(message);
if (result.success) {
this.setState(prevState => ({
messages: prevState.messages.filter(item => item._id !== message._id)
}));
}
} catch (error) {
console.log('StarredMessagesView -> unStar -> catch -> error', error);
}
}
load = async() => {
const {
messages, total, loading
} = this.state;
const { user } = this.props;
if (messages.length === total || loading) {
return;
}
this.setState({ loading: true });
try {
const result = await RocketChat.getMessages(
this.rid,
this.t,
{ 'starred._id': { $in: [user.id] } },
messages.length
);
if (result.success) {
this.setState(prevState => ({
messages: [...prevState.messages, ...result.messages],
total: result.total,
loading: false
}));
}
} catch (error) {
this.setState({ loading: false });
console.log('StarredMessagesView -> load -> catch -> error', error);
}
}
renderEmpty = () => (
<View style={styles.listEmptyContainer} testID='starred-messages-view'>
<Text style={styles.noDataFound}>{I18n.t('No_starred_messages')}</Text>
</View>
)
renderItem = ({ item }) => {
const { user, customEmojis, baseUrl } = this.props;
return (
<Message
customEmojis={customEmojis}
baseUrl={baseUrl}
user={user}
author={item.u}
ts={item.ts}
msg={item.msg}
attachments={item.attachments || []}
timeFormat='MMM Do YYYY, h:mm:ss a'
edited={!!item.editedAt}
header
onLongPress={() => this.onLongPress(item)}
/>
);
}
render() {
const { messages, loading } = this.state;
if (!loading && messages.length === 0) {
return this.renderEmpty();
}
return (
<SafeAreaView style={styles.list} testID='starred-messages-view' forceInset={{ bottom: 'never' }}>
<StatusBar />
<FlatList
data={messages}
renderItem={this.renderItem}
style={styles.list}
keyExtractor={item => item._id}
onEndReached={this.load}
ListFooterComponent={loading ? <RCActivityIndicator /> : null}
/>
</SafeAreaView>
);
}
}

View File

@ -1,22 +0,0 @@
import { StyleSheet } from 'react-native';
import sharedStyles from '../Styles';
import { COLOR_WHITE } from '../../constants/colors';
export default StyleSheet.create({
list: {
flex: 1,
backgroundColor: COLOR_WHITE
},
listEmptyContainer: {
flex: 1,
alignItems: 'center',
justifyContent: 'center',
backgroundColor: COLOR_WHITE
},
noDataFound: {
fontSize: 14,
...sharedStyles.textRegular,
...sharedStyles.textColorNormal
}
});