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