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:
parent
7e513ee73a
commit
3733f776fc
12
app/index.js
12
app/index.js
|
@ -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
|
||||
});
|
||||
|
|
|
@ -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'
|
||||
};
|
||||
}
|
|
@ -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>
|
||||
);
|
||||
}
|
||||
}
|
|
@ -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>
|
||||
);
|
||||
}
|
||||
}
|
|
@ -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>
|
||||
);
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
});
|
|
@ -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'
|
||||
}
|
||||
],
|
||||
|
|
|
@ -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>
|
||||
);
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
});
|
|
@ -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>
|
||||
);
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
});
|
Loading…
Reference in New Issue