diff --git a/android/app/src/main/java/com/mattermost/MainApplication.java b/android/app/src/main/java/com/mattermost/MainApplication.java
index 7b06dbb5c..f3ffff7a5 100644
--- a/android/app/src/main/java/com/mattermost/MainApplication.java
+++ b/android/app/src/main/java/com/mattermost/MainApplication.java
@@ -9,7 +9,7 @@ import com.learnium.RNDeviceInfo.RNDeviceInfo;
import com.psykar.cookiemanager.CookieManagerPackage;
import com.dieam.reactnativepushnotification.ReactNativePushNotificationPackage;
import com.oblador.vectoricons.VectorIconsPackage;
-import com.horcrux.svg.RNSvgPackage;
+import com.horcrux.svg.SvgPackage;
import com.facebook.react.ReactInstanceManager;
import com.facebook.react.ReactNativeHost;
import com.facebook.react.ReactPackage;
@@ -41,7 +41,7 @@ public class MainApplication extends Application implements ReactApplication {
new CookieManagerPackage(),
new ReactNativePushNotificationPackage(),
new VectorIconsPackage(),
- new RNSvgPackage(),
+ new SvgPackage(),
new LinearGradientPackage(),
new PickerPackage(),
new OrientationPackage()
diff --git a/app/actions/navigation/index.js b/app/actions/navigation/index.js
index e2255f461..da4c1bf3c 100644
--- a/app/actions/navigation/index.js
+++ b/app/actions/navigation/index.js
@@ -281,7 +281,6 @@ export function showOptionsModal(options) {
export function showMoreChannelsModal() {
return async (dispatch, getState) => {
- closeDrawers()(dispatch, getState);
dispatch({
type: NavigationTypes.NAVIGATION_MODAL,
route: Routes.MoreChannels
@@ -291,7 +290,6 @@ export function showMoreChannelsModal() {
export function showDirectMessagesModal() {
return async (dispatch, getState) => {
- closeDrawers()(dispatch, getState);
dispatch({
type: NavigationTypes.NAVIGATION_MODAL,
route: Routes.MoreDirectMessages
diff --git a/app/components/channel_drawer_list/channel_drawer_list.js b/app/components/channel_drawer_list/channel_drawer_list.js
index 180233709..3539fb75c 100644
--- a/app/components/channel_drawer_list/channel_drawer_list.js
+++ b/app/components/channel_drawer_list/channel_drawer_list.js
@@ -37,6 +37,7 @@ class ChannelDrawerList extends Component {
showDirectMessagesModal: PropTypes.func.isRequired,
showMoreChannelsModal: PropTypes.func.isRequired
}).isRequired,
+ canCreatePrivateChannels: PropTypes.bool.isRequired,
channels: PropTypes.object.isRequired,
channelMembers: PropTypes.object,
currentTeam: PropTypes.object.isRequired,
@@ -186,7 +187,7 @@ class ChannelDrawerList extends Component {
return data;
}
- const {theme} = this.props;
+ const {canCreatePrivateChannels, theme} = this.props;
const styles = getStyleSheet(theme);
const {
@@ -208,8 +209,12 @@ class ChannelDrawerList extends Component {
...publicChannels
);
+ let createPrivateChannel;
+ if (canCreatePrivateChannels) {
+ createPrivateChannel = this.createPrivateChannel;
+ }
data.push(
- this.renderTitle(styles, 'sidebar.pg', 'PRIVATE CHANNELS', this.createPrivateChannel, privateChannels.length > 0),
+ this.renderTitle(styles, 'sidebar.pg', 'PRIVATE CHANNELS', createPrivateChannel, privateChannels.length > 0),
...privateChannels
);
diff --git a/app/components/channel_drawer_list/channel_drawer_list_container.js b/app/components/channel_drawer_list/channel_drawer_list_container.js
index 9d21f29f7..2fc2114be 100644
--- a/app/components/channel_drawer_list/channel_drawer_list_container.js
+++ b/app/components/channel_drawer_list/channel_drawer_list_container.js
@@ -20,10 +20,19 @@ import {
unmarkFavorite
} from 'app/actions/views/channel';
+import {Constants} from 'mattermost-redux/constants';
+import {getCurrentUserRoles} from 'mattermost-redux/selectors/entities/users';
+import {showCreateOption} from 'mattermost-redux/utils/channel_utils';
+import {isAdmin, isSystemAdmin} from 'mattermost-redux/utils/user_utils';
+
import ChannelDrawerList from './channel_drawer_list';
function mapStateToProps(state, ownProps) {
+ const {config, license} = state.entities.general;
+ const roles = getCurrentUserRoles(state);
+
return {
+ canCreatePrivateChannels: showCreateOption(config, license, Constants.PRIVATE_CHANNEL, isAdmin(roles), isSystemAdmin(roles)),
...ownProps
};
}
diff --git a/app/components/custom_list/index.js b/app/components/custom_list/index.js
index 7d2e1288a..6ec659bb3 100644
--- a/app/components/custom_list/index.js
+++ b/app/components/custom_list/index.js
@@ -23,7 +23,8 @@ export default class CustomList extends PureComponent {
selectable: PropTypes.bool,
onRowSelect: PropTypes.func,
renderRow: PropTypes.func.isRequired,
- createSections: PropTypes.func
+ createSections: PropTypes.func,
+ showNoResults: PropTypes.bool
};
static defaultProps = {
@@ -37,7 +38,8 @@ export default class CustomList extends PureComponent {
loadingText: null,
onRowSelect: () => true,
createSections: () => true,
- showSections: true
+ showSections: true,
+ showNoResults: true
};
constructor(props) {
@@ -48,7 +50,6 @@ export default class CustomList extends PureComponent {
componentWillReceiveProps(nextProps) {
const {data, showSections, searching} = nextProps;
- this.showNoResults = true;
if (searching || searching !== this.props.searching) {
this.setState(this.buildDataSource(nextProps));
@@ -161,7 +162,7 @@ export default class CustomList extends PureComponent {
noResults = this.props.data.length === 0;
}
- if (this.showNoResults && !this.props.loading && noResults) {
+ if (this.props.showNoResults && !this.props.loading && noResults) {
return (
{
+ this.setState({canEdit: false});
+ };
+
handlePostDelete = () => {
const {formatMessage} = this.props.intl;
const {currentTeamId, post, actions} = this.props;
@@ -74,6 +104,7 @@ class Post extends PureComponent {
text: formatMessage({id: 'post_info.del', defaultMessage: 'Delete'}),
style: 'destructive',
onPress: () => {
+ this.editDisableAction.cancel();
actions.deletePost(currentTeamId, post);
}
}]
@@ -183,7 +214,7 @@ class Post extends PureComponent {
renderMessage = (style, messageStyle, replyBar = false) => {
const {formatMessage} = this.props.intl;
- const {currentUserId, isFlagged, post, roles, theme} = this.props;
+ const {isFlagged, post, theme} = this.props;
const {flagPost, unflagPost} = this.props.actions;
const actions = [];
@@ -200,11 +231,11 @@ class Post extends PureComponent {
});
}
- if (post.user_id === currentUserId) {
+ if (this.state.canEdit) {
actions.push({text: formatMessage({id: 'post_info.edit', defaultMessage: 'Edit'}), onPress: () => this.handlePostEdit()});
}
- if (post.user_id === currentUserId || isAdmin(roles)) {
+ if (this.state.canDelete && post.state !== Constants.POST_DELETED) {
actions.push({text: formatMessage({id: 'post_info.del', defaultMessage: 'Delete'}), onPress: () => this.handlePostDelete()});
}
diff --git a/app/components/post/post_container.js b/app/components/post/post_container.js
index 88faaf635..1ad1fdd52 100644
--- a/app/components/post/post_container.js
+++ b/app/components/post/post_container.js
@@ -22,16 +22,18 @@ function makeMapStateToProps() {
const commentedOnUser = ownProps.commentedOnPost ? getUser(state, ownProps.commentedOnPost.user_id) : null;
const user = getUser(state, ownProps.post.user_id);
const myPreferences = getMyPreferences(state);
+ const {config, license} = state.entities.general;
return {
...ownProps,
- config: state.entities.general.config,
+ config,
commentCount: getCommentCountForPost(state, ownProps),
commentedOnDisplayName: displayUsername(commentedOnUser, myPreferences),
currentTeamId: getCurrentTeamId(state),
currentUserId: getCurrentUserId(state),
displayName: displayUsername(user, myPreferences),
isFlagged: isPostFlagged(ownProps.post.id, myPreferences),
+ license,
roles: getCurrentUserRoles(state),
theme: getTheme(state),
user
diff --git a/app/scenes/channel_info/channel_info.js b/app/scenes/channel_info/channel_info.js
index 4ed5e6c89..26515d764 100644
--- a/app/scenes/channel_info/channel_info.js
+++ b/app/scenes/channel_info/channel_info.js
@@ -20,6 +20,7 @@ import ChannelInfoRow from './channel_info_row';
class ChannelInfo extends PureComponent {
static propTypes = {
intl: intlShape.isRequired,
+ canDeleteChannel: PropTypes.bool.isRequired,
currentTeamId: PropTypes.string.isRequired,
currentChannel: PropTypes.object.isRequired,
currentChannelCreatorName: PropTypes.string,
@@ -152,6 +153,7 @@ class ChannelInfo extends PureComponent {
render() {
const {
+ canDeleteChannel,
currentChannel,
currentChannelCreatorName,
currentChannelMemberCount,
@@ -245,7 +247,7 @@ class ChannelInfo extends PureComponent {
theme={theme}
/>
- {this.renderLeaveOrDeleteChannelRow() &&
+ {this.renderLeaveOrDeleteChannelRow() && canDeleteChannel &&
this.handleDeleteOrLeave('delete')}
diff --git a/app/scenes/channel_info/channel_info_container.js b/app/scenes/channel_info/channel_info_container.js
index 0e5fd5c9f..c75e0c32f 100644
--- a/app/scenes/channel_info/channel_info_container.js
+++ b/app/scenes/channel_info/channel_info_container.js
@@ -22,12 +22,14 @@ import {
getChannelsByCategory,
canManageChannelMembers
} from 'mattermost-redux/selectors/entities/channels';
-import {getCurrentUserId, getUser, getStatusForUserId} from 'mattermost-redux/selectors/entities/users';
-import {getUserIdFromChannelName} from 'mattermost-redux/utils/channel_utils';
+import {getCurrentUserId, getUser, getStatusForUserId, getCurrentUserRoles} from 'mattermost-redux/selectors/entities/users';
+import {getUserIdFromChannelName, showDeleteOption} from 'mattermost-redux/utils/channel_utils';
+import {isAdmin, isChannelAdmin, isSystemAdmin} from 'mattermost-redux/utils/user_utils';
import ChannelInfo from './channel_info';
function mapStateToProps(state, ownProps) {
+ const {config, license} = state.entities.general;
const currentChannel = getCurrentChannel(state);
const currentChannelCreator = getUser(state, currentChannel.creator_id);
const currentChannelCreatorName = currentChannelCreator && currentChannelCreator.username;
@@ -37,6 +39,7 @@ function mapStateToProps(state, ownProps) {
const isCurrent = currentChannel.id === state.entities.channels.currentChannelId;
const isFavorite = favoriteChannels.indexOf(currentChannel.id) > -1;
const leaveChannelRequest = state.requests.channels.leaveChannel;
+ const roles = getCurrentUserRoles(state);
let status;
if (currentChannel.type === Constants.DM_CHANNEL) {
@@ -46,6 +49,7 @@ function mapStateToProps(state, ownProps) {
return {
...ownProps,
+ canDeleteChannel: showDeleteOption(config, license, currentChannel, isAdmin(roles), isSystemAdmin(roles), isChannelAdmin(roles)),
currentTeamId: state.entities.teams.currentTeamId,
currentChannel,
currentChannelCreatorName,
diff --git a/app/scenes/more_channels/create_button.js b/app/scenes/more_channels/create_button.js
index f8c701be6..59da6b84c 100644
--- a/app/scenes/more_channels/create_button.js
+++ b/app/scenes/more_channels/create_button.js
@@ -9,10 +9,18 @@ import {
} from 'react-native';
import FormattedText from 'app/components/formatted_text';
-
import {getTheme} from 'app/selectors/preferences';
+import {Constants} from 'mattermost-redux/constants';
+import {getCurrentUserRoles} from 'mattermost-redux/selectors/entities/users';
+import {showCreateOption} from 'mattermost-redux/utils/channel_utils';
+import {isAdmin, isSystemAdmin} from 'mattermost-redux/utils/user_utils';
+
function CreateButton(props) {
+ if (!props.canCreateChannels) {
+ return null;
+ }
+
return (
{
+ // set the timeout to 400 cause is the time that the modal takes to open
+ // Somehow interactionManager doesn't care
+ setTimeout(() => {
this.props.actions.getMoreChannels(this.props.currentTeamId, 0);
- });
+ }, 400);
}
componentWillUnmount() {
@@ -208,6 +212,7 @@ class MoreChannels extends PureComponent {
id);
await this.props.actions.handleSelectChannel(id);
+ this.props.actions.closeDrawers();
InteractionManager.runAfterInteractions(() => {
this.props.actions.goBack();
});
@@ -218,13 +223,15 @@ class MoreChannels extends PureComponent {
};
render() {
- const {formatMessage} = this.props.intl;
- const isLoading = this.props.requestStatus.status === RequestStatus.STARTED;
- const style = getStyleFromTheme(this.props.theme);
- const more = this.state.searching ? () => true : this.loadMoreChannels;
+ const {intl, requestStatus, theme} = this.props;
+ const {adding, channels, searching} = this.state;
+ const {formatMessage} = intl;
+ const isLoading = requestStatus.status === RequestStatus.STARTED || requestStatus.status === RequestStatus.NOT_STARTED;
+ const style = getStyleFromTheme(theme);
+ const more = searching ? () => true : this.loadMoreChannels;
let content;
- if (this.state.adding) {
+ if (adding) {
content = (
@@ -250,9 +257,9 @@ class MoreChannels extends PureComponent {
/>
);
diff --git a/app/scenes/more_dms/index.js b/app/scenes/more_dms/index.js
index 6534dbbba..5f431d5a3 100644
--- a/app/scenes/more_dms/index.js
+++ b/app/scenes/more_dms/index.js
@@ -5,7 +5,7 @@ import {bindActionCreators} from 'redux';
import navigationSceneConnect from '../navigationSceneConnect';
-import {goBack} from 'app/actions/navigation';
+import {closeDrawers, goBack} from 'app/actions/navigation';
import {makeDirectChannel} from 'app/actions/views/more_dms';
import {getTheme} from 'app/selectors/preferences';
import {getProfiles, searchProfiles} from 'mattermost-redux/actions/users';
@@ -41,6 +41,7 @@ function mapStateToProps(state, ownProps) {
function mapDispatchToProps(dispatch) {
return {
actions: bindActionCreators({
+ closeDrawers,
goBack,
makeDirectChannel,
getProfiles,
diff --git a/app/scenes/more_dms/more_dms.js b/app/scenes/more_dms/more_dms.js
index d2a98adca..f825237ef 100644
--- a/app/scenes/more_dms/more_dms.js
+++ b/app/scenes/more_dms/more_dms.js
@@ -31,6 +31,7 @@ class MoreDirectMessages extends PureComponent {
subscribeToHeaderEvent: React.PropTypes.func.isRequired,
unsubscribeFromHeaderEvent: React.PropTypes.func.isRequired,
actions: PropTypes.shape({
+ closeDrawers: PropTypes.func.isRequired,
goBack: PropTypes.func.isRequired,
makeDirectChannel: PropTypes.func.isRequired,
getProfiles: PropTypes.func.isRequired,
@@ -61,11 +62,12 @@ class MoreDirectMessages extends PureComponent {
this.searchTimeoutId = 0;
this.state = {
- profiles: [],
+ profiles: props.profiles,
page: 0,
adding: false,
next: true,
- searching: false
+ searching: false,
+ showNoResults: false
};
}
@@ -79,7 +81,7 @@ class MoreDirectMessages extends PureComponent {
nextProps.requestStatus.status === RequestStatus.SUCCESS) {
const {page} = this.state;
const profiles = nextProps.profiles.splice(0, (page + 1) * Constants.PROFILE_CHUNK_SIZE);
- this.setState({profiles});
+ this.setState({profiles, showNoResults: true});
} else if (this.state.searching &&
nextProps.searchRequest.status === RequestStatus.SUCCESS) {
const results = nextProps.profiles.filter((p) => {
@@ -87,7 +89,7 @@ class MoreDirectMessages extends PureComponent {
return p.username.toLowerCase().includes(term) || p.email.toLowerCase().includes(term) ||
p.first_name.toLowerCase().includes(term) || p.last_name.toLowerCase().includes(term);
});
- this.setState({profiles: results});
+ this.setState({profiles: results, showNoResults: true});
}
}
@@ -96,9 +98,11 @@ class MoreDirectMessages extends PureComponent {
Keyboard.addListener('keyboardDidHide', this.handleAndroidKeyboard);
}
- InteractionManager.runAfterInteractions(() => {
+ // set the timeout to 400 cause is the time that the modal takes to open
+ // Somehow interactionManager doesn't care
+ setTimeout(() => {
this.props.actions.getProfiles(0);
- });
+ }, 400);
}
componentWillUnmount() {
@@ -166,20 +170,24 @@ class MoreDirectMessages extends PureComponent {
this.setState({adding: true});
this.searchBar.blur();
await this.props.actions.makeDirectChannel(id);
+
+ this.props.actions.closeDrawers();
InteractionManager.runAfterInteractions(() => {
this.props.actions.goBack();
});
};
render() {
- const {formatMessage} = this.props.intl;
- const isLoading = (this.props.requestStatus.status === RequestStatus.STARTED) ||
- (this.props.searchRequest.status === RequestStatus.STARTED);
- const style = getStyleFromTheme(this.props.theme);
+ const {intl, preferences, requestStatus, searchRequest, theme} = this.props;
+ const {adding, profiles, searching, showNoResults} = this.state;
+ const {formatMessage} = intl;
+ const isLoading = (requestStatus.status === RequestStatus.STARTED) || (requestStatus.status === RequestStatus.NOT_STARTED) ||
+ (searchRequest.status === RequestStatus.STARTED);
+ const style = getStyleFromTheme(theme);
const more = this.state.searching ? () => true : this.loadMoreProfiles;
let content;
- if (this.state.adding) {
+ if (adding) {
content = (
@@ -205,11 +213,11 @@ class MoreDirectMessages extends PureComponent {
/>
);
diff --git a/app/scenes/user_profile/user_profile.js b/app/scenes/user_profile/user_profile.js
index 5120698f2..f5bfe950f 100644
--- a/app/scenes/user_profile/user_profile.js
+++ b/app/scenes/user_profile/user_profile.js
@@ -15,6 +15,95 @@ import {getFullName} from 'mattermost-redux/utils/user_utils';
import UserProfileRow from './user_profile_row';
+export default class UserProfile extends PureComponent {
+ static propTypes = {
+ actions: PropTypes.shape({
+ handleSendMessage: PropTypes.func.isRequired
+ }).isRequired,
+ config: PropTypes.object.isRequired,
+ currentUserId: PropTypes.string.isRequired,
+ theme: PropTypes.object.isRequired,
+ user: PropTypes.object.isRequired
+ };
+
+ state = {
+ isFavorite: false
+ };
+
+ getDisplayName = () => {
+ const {theme, user} = this.props;
+ const style = createStyleSheet(theme);
+
+ const displayName = getFullName(user);
+
+ if (displayName) {
+ return {displayName};
+ }
+
+ return null;
+ };
+
+ buildDisplayBlock = (property) => {
+ const {theme, user} = this.props;
+ const style = createStyleSheet(theme);
+
+ if (user.hasOwnProperty(property) && user[property].length > 0) {
+ return (
+
+ {property.toUpperCase()}
+ {user[property]}
+
+ );
+ }
+
+ return null;
+ };
+
+ sendMessage = () => {
+ this.props.actions.handleSendMessage(this.props.user.id);
+ };
+
+ render() {
+ const {config, currentUserId, theme, user} = this.props;
+ const style = createStyleSheet(theme);
+
+ return (
+
+
+
+
+ {this.getDisplayName()}
+ {`@${user.username}`}
+
+
+ {this.buildDisplayBlock('username')}
+ {config.ShowEmailAddress === 'true' && this.buildDisplayBlock('email')}
+ {this.buildDisplayBlock('position')}
+
+ {currentUserId !== user.id &&
+
+ }
+
+
+ );
+ }
+}
+
const createStyleSheet = makeStyleSheetFromTheme((theme) => {
return StyleSheet.create({
container: {
@@ -60,91 +149,3 @@ const createStyleSheet = makeStyleSheetFromTheme((theme) => {
}
});
});
-
-export default class UserProfile extends PureComponent {
- static propTypes = {
- currentUserId: PropTypes.string.isRequired,
- theme: PropTypes.object.isRequired,
- user: PropTypes.object.isRequired,
- actions: PropTypes.shape({
- handleSendMessage: PropTypes.func.isRequired
- }).isRequired
- };
-
- state = {
- isFavorite: false
- }
-
- getDisplayName = () => {
- const {theme, user} = this.props;
- const style = createStyleSheet(theme);
-
- const displayName = getFullName(user);
-
- if (displayName) {
- return {displayName};
- }
-
- return null;
- }
-
- buildDisplayBlock = (property) => {
- const {theme, user} = this.props;
- const style = createStyleSheet(theme);
-
- if (user.hasOwnProperty(property) && user[property].length > 0) {
- return (
-
- {property.toUpperCase()}
- {user[property]}
-
- );
- }
-
- return null;
- }
-
- sendMessage = () => {
- this.props.actions.handleSendMessage(this.props.user.id);
- }
-
- render() {
- const {theme, user} = this.props;
- const style = createStyleSheet(theme);
-
- return (
-
-
-
-
- {this.getDisplayName()}
- {`@${user.username}`}
-
-
- {this.buildDisplayBlock('username')}
- {this.buildDisplayBlock('email')}
- {this.buildDisplayBlock('position')}
-
- {this.props.currentUserId !== this.props.user.id &&
-
- }
-
-
- );
- }
-}
diff --git a/app/scenes/user_profile/user_profile_container.js b/app/scenes/user_profile/user_profile_container.js
index 903bd1de5..bde83361e 100644
--- a/app/scenes/user_profile/user_profile_container.js
+++ b/app/scenes/user_profile/user_profile_container.js
@@ -4,15 +4,19 @@
import {bindActionCreators} from 'redux';
import {handleSendMessage} from 'app/actions/views/user_profile';
+import navigationSceneConnect from 'app/scenes/navigationSceneConnect';
import {getTheme} from 'app/selectors/preferences';
+import {getCurrentUserId} from 'mattermost-redux/selectors/entities/users';
+
import UserProfile from './user_profile';
-import navigationSceneConnect from '../navigationSceneConnect';
-
function mapStateToProps(state, ownProps) {
+ const {config} = state.entities.general;
+
return {
- currentUserId: state.entities.users.currentUserId,
+ config,
+ currentUserId: getCurrentUserId(state),
user: state.entities.users.profiles[ownProps.userId],
theme: getTheme(state)
};