diff --git a/app/lib/methods/getPermissions.js b/app/lib/methods/getPermissions.js
index d23268f39..589aa6bd1 100644
--- a/app/lib/methods/getPermissions.js
+++ b/app/lib/methods/getPermissions.js
@@ -18,6 +18,10 @@ const PERMISSIONS = [
'archive-room',
'auto-translate',
'create-invite-links',
+ 'create-c',
+ 'create-p',
+ 'create-d',
+ 'start-discussion',
'create-team',
'delete-c',
'delete-message',
diff --git a/app/lib/rocketchat.js b/app/lib/rocketchat.js
index fa8a47b18..e0c16ac6c 100644
--- a/app/lib/rocketchat.js
+++ b/app/lib/rocketchat.js
@@ -1390,17 +1390,19 @@ const RocketChat = {
* Returns an array of boolean for each permission from permissions arg
*/
async hasPermission(permissions, rid) {
- const db = database.active;
- const subsCollection = db.get('subscriptions');
let roomRoles = [];
- try {
- // get the room from database
- const room = await subsCollection.find(rid);
- // get room roles
- roomRoles = room.roles || [];
- } catch (error) {
- console.log('hasPermission -> Room not found');
- return permissions.map(() => false);
+ if (rid) {
+ const db = database.active;
+ const subsCollection = db.get('subscriptions');
+ try {
+ // get the room from database
+ const room = await subsCollection.find(rid);
+ // get room roles
+ roomRoles = room.roles || [];
+ } catch (error) {
+ console.log('hasPermission -> Room not found');
+ return permissions.map(() => false);
+ }
}
try {
@@ -1547,11 +1549,13 @@ const RocketChat = {
messageId
});
},
- searchMessages(roomId, searchText) {
+ searchMessages(roomId, searchText, count, offset) {
// RC 0.60.0
return this.sdk.get('chat.search', {
roomId,
- searchText
+ searchText,
+ count,
+ offset
});
},
toggleFollowMessage(mid, follow) {
diff --git a/app/views/AuthLoadingView.js b/app/views/AuthLoadingView.tsx
similarity index 81%
rename from app/views/AuthLoadingView.js
rename to app/views/AuthLoadingView.tsx
index e97c10d94..ef6e7b943 100644
--- a/app/views/AuthLoadingView.js
+++ b/app/views/AuthLoadingView.tsx
@@ -1,6 +1,5 @@
import React from 'react';
import { ActivityIndicator, StyleSheet, Text, View } from 'react-native';
-import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import I18n from '../i18n';
@@ -23,25 +22,25 @@ const styles = StyleSheet.create({
}
});
-const AuthLoadingView = React.memo(({ theme, text }) => (
+interface IAuthLoadingView {
+ theme: string;
+ text: string;
+}
+
+const AuthLoadingView = React.memo(({ theme, text }: IAuthLoadingView) => (
- {text && (
+ {text ? (
<>
{`${text}\n${I18n.t('Please_wait')}`}
>
- )}
+ ) : null}
));
-const mapStateToProps = state => ({
+const mapStateToProps = (state: any) => ({
text: state.app.text
});
-AuthLoadingView.propTypes = {
- theme: PropTypes.string,
- text: PropTypes.string
-};
-
export default connect(mapStateToProps)(withTheme(AuthLoadingView));
diff --git a/app/views/CreateChannelView.js b/app/views/CreateChannelView.js
index af17961fb..0aa3c7035 100644
--- a/app/views/CreateChannelView.js
+++ b/app/views/CreateChannelView.js
@@ -21,6 +21,7 @@ import { Review } from '../utils/review';
import { getUserSelector } from '../selectors/login';
import { events, logEvent } from '../utils/log';
import SafeAreaView from '../containers/SafeAreaView';
+import RocketChat from '../lib/rocketchat';
import sharedStyles from './Styles';
const styles = StyleSheet.create({
@@ -79,10 +80,13 @@ class CreateChannelView extends React.Component {
users: PropTypes.array.isRequired,
user: PropTypes.shape({
id: PropTypes.string,
- token: PropTypes.string
+ token: PropTypes.string,
+ roles: PropTypes.array
}),
theme: PropTypes.string,
- teamId: PropTypes.string
+ teamId: PropTypes.string,
+ createPublicChannelPermission: PropTypes.array,
+ createPrivateChannelPermission: PropTypes.array
};
constructor(props) {
@@ -96,14 +100,20 @@ class CreateChannelView extends React.Component {
readOnly: false,
encrypted: false,
broadcast: false,
- isTeam
+ isTeam,
+ permissions: []
};
this.setHeader();
}
+ componentDidMount() {
+ this.handleHasPermission();
+ }
+
shouldComponentUpdate(nextProps, nextState) {
- const { channelName, type, readOnly, broadcast, encrypted } = this.state;
- const { users, isFetching, encryptionEnabled, theme } = this.props;
+ const { channelName, type, readOnly, broadcast, encrypted, permissions } = this.state;
+ const { users, isFetching, encryptionEnabled, theme, createPublicChannelPermission, createPrivateChannelPermission } =
+ this.props;
if (nextProps.theme !== theme) {
return true;
}
@@ -122,18 +132,37 @@ class CreateChannelView extends React.Component {
if (nextState.broadcast !== broadcast) {
return true;
}
+ if (nextState.permissions !== permissions) {
+ return true;
+ }
if (nextProps.isFetching !== isFetching) {
return true;
}
if (nextProps.encryptionEnabled !== encryptionEnabled) {
return true;
}
+ if (!dequal(nextProps.createPublicChannelPermission, createPublicChannelPermission)) {
+ return true;
+ }
+ if (!dequal(nextProps.createPrivateChannelPermission, createPrivateChannelPermission)) {
+ return true;
+ }
if (!dequal(nextProps.users, users)) {
return true;
}
return false;
}
+ componentDidUpdate(prevProps) {
+ const { createPublicChannelPermission, createPrivateChannelPermission } = this.props;
+ if (
+ !dequal(createPublicChannelPermission, prevProps.createPublicChannelPermission) ||
+ !dequal(createPrivateChannelPermission, prevProps.createPrivateChannelPermission)
+ ) {
+ this.handleHasPermission();
+ }
+ }
+
setHeader = () => {
const { navigation } = this.props;
const { isTeam } = this.state;
@@ -208,12 +237,21 @@ class CreateChannelView extends React.Component {
);
};
+ handleHasPermission = async () => {
+ const { createPublicChannelPermission, createPrivateChannelPermission } = this.props;
+ const permissions = [createPublicChannelPermission, createPrivateChannelPermission];
+ const permissionsToCreate = await RocketChat.hasPermission(permissions);
+ this.setState({ permissions: permissionsToCreate });
+ };
+
renderType() {
- const { type, isTeam } = this.state;
+ const { type, isTeam, permissions } = this.state;
+ const isDisabled = permissions.filter(r => r === true).length <= 1;
return this.renderSwitch({
id: 'type',
- value: type,
+ value: permissions[1] ? type : false,
+ disabled: isDisabled,
label: isTeam ? 'Private_Team' : 'Private_Channel',
onValueChange: value => {
logEvent(events.CR_TOGGLE_TYPE);
@@ -373,7 +411,9 @@ const mapStateToProps = state => ({
isFetching: state.createChannel.isFetching,
encryptionEnabled: state.encryption.enabled,
users: state.selectedUsers.users,
- user: getUserSelector(state)
+ user: getUserSelector(state),
+ createPublicChannelPermission: state.permissions['create-c'],
+ createPrivateChannelPermission: state.permissions['create-p']
});
const mapDispatchToProps = dispatch => ({
diff --git a/app/views/NewMessageView.js b/app/views/NewMessageView.js
index 4b210e7cb..020588ffa 100644
--- a/app/views/NewMessageView.js
+++ b/app/views/NewMessageView.js
@@ -3,8 +3,9 @@ import PropTypes from 'prop-types';
import { FlatList, StyleSheet, Text, View } from 'react-native';
import { connect } from 'react-redux';
import { Q } from '@nozbe/watermelondb';
-
+import { dequal } from 'dequal';
import * as List from '../containers/List';
+
import Touch from '../utils/touch';
import database from '../lib/database';
import RocketChat from '../lib/rocketchat';
@@ -57,13 +58,19 @@ class NewMessageView extends React.Component {
baseUrl: PropTypes.string,
user: PropTypes.shape({
id: PropTypes.string,
- token: PropTypes.string
+ token: PropTypes.string,
+ roles: PropTypes.array
}),
create: PropTypes.func,
maxUsers: PropTypes.number,
theme: PropTypes.string,
isMasterDetail: PropTypes.bool,
- serverVersion: PropTypes.string
+ serverVersion: PropTypes.string,
+ createTeamPermission: PropTypes.array,
+ createDirectMessagePermission: PropTypes.array,
+ createPublicChannelPermission: PropTypes.array,
+ createPrivateChannelPermission: PropTypes.array,
+ createDiscussionPermission: PropTypes.array
};
constructor(props) {
@@ -71,7 +78,8 @@ class NewMessageView extends React.Component {
this.init();
this.state = {
search: [],
- chats: []
+ chats: [],
+ permissions: []
};
}
@@ -90,6 +98,30 @@ class NewMessageView extends React.Component {
}
};
+ componentDidMount() {
+ this.handleHasPermission();
+ }
+
+ componentDidUpdate(prevProps) {
+ const {
+ createTeamPermission,
+ createPublicChannelPermission,
+ createPrivateChannelPermission,
+ createDirectMessagePermission,
+ createDiscussionPermission
+ } = this.props;
+
+ if (
+ !dequal(createTeamPermission, prevProps.createTeamPermission) ||
+ !dequal(createPublicChannelPermission, prevProps.createPublicChannelPermission) ||
+ !dequal(createPrivateChannelPermission, prevProps.createPrivateChannelPermission) ||
+ !dequal(createDirectMessagePermission, prevProps.createDirectMessagePermission) ||
+ !dequal(createDiscussionPermission, prevProps.createDiscussionPermission)
+ ) {
+ this.handleHasPermission();
+ }
+ }
+
onSearchChangeText(text) {
this.search(text);
}
@@ -161,20 +193,43 @@ class NewMessageView extends React.Component {
Navigation.navigate('CreateDiscussionView');
};
+ handleHasPermission = async () => {
+ const {
+ createTeamPermission,
+ createDirectMessagePermission,
+ createPublicChannelPermission,
+ createPrivateChannelPermission,
+ createDiscussionPermission
+ } = this.props;
+ const permissions = [
+ createPublicChannelPermission,
+ createPrivateChannelPermission,
+ createTeamPermission,
+ createDirectMessagePermission,
+ createDiscussionPermission
+ ];
+ const permissionsToCreate = await RocketChat.hasPermission(permissions);
+ this.setState({ permissions: permissionsToCreate });
+ };
+
renderHeader = () => {
const { maxUsers, theme, serverVersion } = this.props;
+ const { permissions } = this.state;
+
return (
this.onSearchChangeText(text)} testID='new-message-view-search' />
- {this.renderButton({
- onPress: this.createChannel,
- title: I18n.t('Create_Channel'),
- icon: 'channel-public',
- testID: 'new-message-view-create-channel',
- first: true
- })}
- {compareServerVersion(serverVersion, '3.13.0', methods.greaterThanOrEqualTo)
+ {permissions[0] || permissions[1]
+ ? this.renderButton({
+ onPress: this.createChannel,
+ title: I18n.t('Create_Channel'),
+ icon: 'channel-public',
+ testID: 'new-message-view-create-channel',
+ first: true
+ })
+ : null}
+ {compareServerVersion(serverVersion, '3.13.0', methods.greaterThanOrEqualTo) && permissions[2]
? this.renderButton({
onPress: this.createTeam,
title: I18n.t('Create_Team'),
@@ -182,7 +237,7 @@ class NewMessageView extends React.Component {
testID: 'new-message-view-create-team'
})
: null}
- {maxUsers > 2
+ {maxUsers > 2 && permissions[3]
? this.renderButton({
onPress: this.createGroupChat,
title: I18n.t('Create_Direct_Messages'),
@@ -190,12 +245,14 @@ class NewMessageView extends React.Component {
testID: 'new-message-view-create-direct-message'
})
: null}
- {this.renderButton({
- onPress: this.createDiscussion,
- title: I18n.t('Create_Discussion'),
- icon: 'discussions',
- testID: 'new-message-view-create-discussion'
- })}
+ {permissions[4]
+ ? this.renderButton({
+ onPress: this.createDiscussion,
+ title: I18n.t('Create_Discussion'),
+ icon: 'discussions',
+ testID: 'new-message-view-create-discussion'
+ })
+ : null}
);
@@ -261,7 +318,12 @@ const mapStateToProps = state => ({
isMasterDetail: state.app.isMasterDetail,
baseUrl: state.server.server,
maxUsers: state.settings.DirectMesssage_maxUsers || 1,
- user: getUserSelector(state)
+ user: getUserSelector(state),
+ createTeamPermission: state.permissions['create-team'],
+ createDirectMessagePermission: state.permissions['create-d'],
+ createPublicChannelPermission: state.permissions['create-c'],
+ createPrivateChannelPermission: state.permissions['create-p'],
+ createDiscussionPermission: state.permissions['start-discussion']
});
const mapDispatchToProps = dispatch => ({
diff --git a/app/views/RoomView/RightButtons.js b/app/views/RoomView/RightButtons.js
index 70e18bd68..9bbf717fd 100644
--- a/app/views/RoomView/RightButtons.js
+++ b/app/views/RoomView/RightButtons.js
@@ -20,7 +20,8 @@ class RightButtonsContainer extends Component {
navigation: PropTypes.object,
isMasterDetail: PropTypes.bool,
toggleFollowThread: PropTypes.func,
- joined: PropTypes.bool
+ joined: PropTypes.bool,
+ encrypted: PropTypes.bool
};
constructor(props) {
@@ -137,11 +138,14 @@ class RightButtonsContainer extends Component {
goSearchView = () => {
logEvent(events.ROOM_GO_SEARCH);
- const { rid, t, navigation, isMasterDetail } = this.props;
+ const { rid, t, navigation, isMasterDetail, encrypted } = this.props;
if (isMasterDetail) {
- navigation.navigate('ModalStackNavigator', { screen: 'SearchMessagesView', params: { rid, showCloseModal: true } });
+ navigation.navigate('ModalStackNavigator', {
+ screen: 'SearchMessagesView',
+ params: { rid, showCloseModal: true, encrypted }
+ });
} else {
- navigation.navigate('SearchMessagesView', { rid, t });
+ navigation.navigate('SearchMessagesView', { rid, t, encrypted });
}
};
diff --git a/app/views/RoomView/index.js b/app/views/RoomView/index.js
index 95e5da195..713be5615 100644
--- a/app/views/RoomView/index.js
+++ b/app/views/RoomView/index.js
@@ -362,6 +362,7 @@ class RoomView extends React.Component {
const t = room?.t;
const teamMain = room?.teamMain;
const teamId = room?.teamId;
+ const encrypted = room?.encrypted;
const { id: userId, token } = user;
const avatar = room?.name;
const visitor = room?.visitor;
@@ -424,6 +425,7 @@ class RoomView extends React.Component {
teamMain={teamMain}
joined={joined}
t={t}
+ encrypted={encrypted}
navigation={navigation}
toggleFollowThread={this.toggleFollowThread}
/>
diff --git a/app/views/RoomsListView/index.js b/app/views/RoomsListView/index.js
index 5f4d12553..4c9b48269 100644
--- a/app/views/RoomsListView/index.js
+++ b/app/views/RoomsListView/index.js
@@ -89,7 +89,12 @@ const shouldUpdateProps = [
'refreshing',
'queueSize',
'inquiryEnabled',
- 'encryptionBanner'
+ 'encryptionBanner',
+ 'createTeamPermission',
+ 'createDirectMessagePermission',
+ 'createPublicChannelPermission',
+ 'createPrivateChannelPermission',
+ 'createDiscussionPermission'
];
const getItemLayout = (data, index) => ({
length: ROW_HEIGHT,
@@ -106,7 +111,7 @@ class RoomsListView extends React.Component {
username: PropTypes.string,
token: PropTypes.string,
statusLivechat: PropTypes.string,
- roles: PropTypes.object
+ roles: PropTypes.array
}),
server: PropTypes.string,
searchText: PropTypes.string,
@@ -135,6 +140,11 @@ class RoomsListView extends React.Component {
queueSize: PropTypes.number,
inquiryEnabled: PropTypes.bool,
encryptionBanner: PropTypes.string,
+ createTeamPermission: PropTypes.array,
+ createDirectMessagePermission: PropTypes.array,
+ createPublicChannelPermission: PropTypes.array,
+ createPrivateChannelPermission: PropTypes.array,
+ createDiscussionPermission: PropTypes.array,
initAdd: PropTypes.func
};
@@ -152,7 +162,8 @@ class RoomsListView extends React.Component {
loading: true,
chatsUpdate: [],
chats: [],
- item: {}
+ item: {},
+ canCreateRoom: false
};
this.setHeader();
this.getSubscriptions();
@@ -160,6 +171,7 @@ class RoomsListView extends React.Component {
componentDidMount() {
const { navigation, closeServerDropdown } = this.props;
+ this.handleHasPermission();
this.mounted = true;
if (isTablet) {
@@ -203,7 +215,7 @@ class RoomsListView extends React.Component {
}
shouldComponentUpdate(nextProps, nextState) {
- const { chatsUpdate, searching, item } = this.state;
+ const { chatsUpdate, searching, item, canCreateRoom } = this.state;
// eslint-disable-next-line react/destructuring-assignment
const propsUpdated = shouldUpdateProps.some(key => nextProps[key] !== this.props[key]);
if (propsUpdated) {
@@ -222,6 +234,10 @@ class RoomsListView extends React.Component {
return true;
}
+ if (nextState.canCreateRoom !== canCreateRoom) {
+ return true;
+ }
+
if (nextState.item?.rid !== item?.rid) {
return true;
}
@@ -257,7 +273,20 @@ class RoomsListView extends React.Component {
}
componentDidUpdate(prevProps) {
- const { sortBy, groupByType, showFavorites, showUnread, rooms, isMasterDetail, insets } = this.props;
+ const {
+ sortBy,
+ groupByType,
+ showFavorites,
+ showUnread,
+ rooms,
+ isMasterDetail,
+ insets,
+ createTeamPermission,
+ createPublicChannelPermission,
+ createPrivateChannelPermission,
+ createDirectMessagePermission,
+ createDiscussionPermission
+ } = this.props;
const { item } = this.state;
if (
@@ -278,6 +307,17 @@ class RoomsListView extends React.Component {
if (insets.left !== prevProps.insets.left || insets.right !== prevProps.insets.right) {
this.setHeader();
}
+
+ if (
+ !dequal(createTeamPermission, prevProps.createTeamPermission) ||
+ !dequal(createPublicChannelPermission, prevProps.createPublicChannelPermission) ||
+ !dequal(createPrivateChannelPermission, prevProps.createPrivateChannelPermission) ||
+ !dequal(createDirectMessagePermission, prevProps.createDirectMessagePermission) ||
+ !dequal(createDiscussionPermission, prevProps.createDiscussionPermission)
+ ) {
+ this.handleHasPermission();
+ this.setHeader();
+ }
}
componentWillUnmount() {
@@ -297,10 +337,31 @@ class RoomsListView extends React.Component {
console.countReset(`${this.constructor.name}.render calls`);
}
+ handleHasPermission = async () => {
+ const {
+ createTeamPermission,
+ createDirectMessagePermission,
+ createPublicChannelPermission,
+ createPrivateChannelPermission,
+ createDiscussionPermission
+ } = this.props;
+ const permissions = [
+ createPublicChannelPermission,
+ createPrivateChannelPermission,
+ createTeamPermission,
+ createDirectMessagePermission,
+ createDiscussionPermission
+ ];
+ const permissionsToCreate = await RocketChat.hasPermission(permissions);
+ const canCreateRoom = permissionsToCreate.filter(r => r === true).length > 0;
+ this.setState({ canCreateRoom }, () => this.setHeader());
+ };
+
getHeader = () => {
- const { searching } = this.state;
+ const { searching, canCreateRoom } = this.state;
const { navigation, isMasterDetail, insets } = this.props;
const headerTitlePosition = getHeaderTitlePosition({ insets, numIconsRight: searching ? 0 : 3 });
+
return {
headerTitleAlign: 'left',
headerLeft: () =>
@@ -327,7 +388,9 @@ class RoomsListView extends React.Component {
headerRight: () =>
searching ? null : (
-
+ {canCreateRoom ? (
+
+ ) : null}
@@ -963,7 +1026,12 @@ const mapStateToProps = state => ({
rooms: state.room.rooms,
queueSize: getInquiryQueueSelector(state).length,
inquiryEnabled: state.inquiry.enabled,
- encryptionBanner: state.encryption.banner
+ encryptionBanner: state.encryption.banner,
+ createTeamPermission: state.permissions['create-team'],
+ createDirectMessagePermission: state.permissions['create-d'],
+ createPublicChannelPermission: state.permissions['create-c'],
+ createPrivateChannelPermission: state.permissions['create-p'],
+ createDiscussionPermission: state.permissions['start-discussion']
});
const mapDispatchToProps = dispatch => ({
diff --git a/app/views/SearchMessagesView/index.js b/app/views/SearchMessagesView/index.js
index f8131ec1d..c36425edd 100644
--- a/app/views/SearchMessagesView/index.js
+++ b/app/views/SearchMessagesView/index.js
@@ -24,8 +24,11 @@ import database from '../../lib/database';
import { sanitizeLikeString } from '../../lib/database/utils';
import getThreadName from '../../lib/methods/getThreadName';
import getRoomInfo from '../../lib/methods/getRoomInfo';
+import { isIOS } from '../../utils/deviceInfo';
+import { compareServerVersion, methods } from '../../lib/utils';
import styles from './styles';
+const QUERY_SIZE = 50;
class SearchMessagesView extends React.Component {
static navigationOptions = ({ navigation, route }) => {
const options = {
@@ -43,6 +46,7 @@ class SearchMessagesView extends React.Component {
route: PropTypes.object,
user: PropTypes.object,
baseUrl: PropTypes.string,
+ serverVersion: PropTypes.string,
customEmojis: PropTypes.object,
theme: PropTypes.string,
useRealName: PropTypes.bool
@@ -55,6 +59,7 @@ class SearchMessagesView extends React.Component {
messages: [],
searchText: ''
};
+ this.offset = 0;
this.rid = props.route.params?.rid;
this.t = props.route.params?.t;
this.encrypted = props.route.params?.encrypted;
@@ -88,6 +93,9 @@ class SearchMessagesView extends React.Component {
// Handle encrypted rooms search messages
searchMessages = async searchText => {
+ if (!searchText) {
+ return [];
+ }
// If it's a encrypted, room we'll search only on the local stored messages
if (this.encrypted) {
const db = database.active;
@@ -103,25 +111,33 @@ class SearchMessagesView extends React.Component {
.fetch();
}
// If it's not a encrypted room, search messages on the server
- const result = await RocketChat.searchMessages(this.rid, searchText);
+ const result = await RocketChat.searchMessages(this.rid, searchText, QUERY_SIZE, this.offset);
if (result.success) {
return result.messages;
}
};
- search = debounce(async searchText => {
- this.setState({ searchText, loading: true, messages: [] });
-
+ getMessages = async (searchText, debounced) => {
try {
const messages = await this.searchMessages(searchText);
- this.setState({
- messages: messages || [],
+ this.setState(prevState => ({
+ messages: debounced ? messages : [...prevState.messages, ...messages],
loading: false
- });
+ }));
} catch (e) {
this.setState({ loading: false });
log(e);
}
+ };
+
+ search = searchText => {
+ this.offset = 0;
+ this.setState({ searchText, loading: true, messages: [] });
+ this.searchDebounced(searchText);
+ };
+
+ searchDebounced = debounce(async searchText => {
+ await this.getMessages(searchText, true);
}, 1000);
getCustomEmoji = name => {
@@ -168,6 +184,23 @@ class SearchMessagesView extends React.Component {
}
};
+ onEndReached = async () => {
+ const { serverVersion } = this.props;
+ const { searchText, messages, loading } = this.state;
+ if (
+ messages.length < this.offset ||
+ this.encrypted ||
+ loading ||
+ compareServerVersion(serverVersion, '3.17.0', methods.lowerThan)
+ ) {
+ return;
+ }
+ this.setState({ loading: true });
+ this.offset += QUERY_SIZE;
+
+ await this.getMessages(searchText);
+ };
+
renderEmpty = () => {
const { theme } = this.props;
return (
@@ -212,8 +245,10 @@ class SearchMessagesView extends React.Component {
renderItem={this.renderItem}
style={[styles.list, { backgroundColor: themes[theme].backgroundColor }]}
keyExtractor={item => item._id}
- onEndReached={this.load}
+ onEndReached={this.onEndReached}
ListFooterComponent={loading ? : null}
+ onEndReachedThreshold={0.5}
+ removeClippedSubviews={isIOS}
{...scrollPersistTaps}
/>
);
@@ -243,6 +278,7 @@ class SearchMessagesView extends React.Component {
}
const mapStateToProps = state => ({
+ serverVersion: state.server.version,
baseUrl: state.server.server,
user: getUserSelector(state),
useRealName: state.settings.UI_Use_Real_Name,