diff --git a/app/components/channel_drawer/channels_list/channel_item/channel_item.js b/app/components/channel_drawer/channels_list/channel_item/channel_item.js
index 079e3cad5..ab30d49b4 100644
--- a/app/components/channel_drawer/channels_list/channel_item/channel_item.js
+++ b/app/components/channel_drawer/channels_list/channel_item/channel_item.js
@@ -31,6 +31,7 @@ export default class ChannelItem extends PureComponent {
navigator: PropTypes.object,
onSelectChannel: PropTypes.func.isRequired,
status: PropTypes.string,
+ teammateDeletedAt: PropTypes.number,
type: PropTypes.string.isRequired,
theme: PropTypes.object.isRequired
};
@@ -79,6 +80,7 @@ export default class ChannelItem extends PureComponent {
isUnread,
mentions,
status,
+ teammateDeletedAt,
theme,
type
} = this.props;
@@ -133,6 +135,7 @@ export default class ChannelItem extends PureComponent {
membersCount={displayName.split(',').length}
size={16}
status={status}
+ teammateDeletedAt={teammateDeletedAt}
theme={theme}
type={type}
/>
diff --git a/app/components/channel_drawer/channels_list/channel_item/index.js b/app/components/channel_drawer/channels_list/channel_item/index.js
index 41ff2b4e6..9515a41f9 100644
--- a/app/components/channel_drawer/channels_list/channel_item/index.js
+++ b/app/components/channel_drawer/channels_list/channel_item/index.js
@@ -6,7 +6,7 @@ import {connect} from 'react-redux';
import {General} from 'mattermost-redux/constants';
import {getCurrentChannelId, makeGetChannel, getMyChannelMember} from 'mattermost-redux/selectors/entities/channels';
import {getTheme} from 'mattermost-redux/selectors/entities/preferences';
-import {getCurrentUserId} from 'mattermost-redux/selectors/entities/users';
+import {getCurrentUserId, getUser} from 'mattermost-redux/selectors/entities/users';
import ChannelItem from './channel_item';
@@ -22,8 +22,13 @@ function makeMapStateToProps() {
const currentUserId = getCurrentUserId(state);
let isMyUser = false;
+ let teammateDeletedAt = 0;
if (channel.type === General.DM_CHANNEL && channel.teammate_id) {
isMyUser = channel.teammate_id === currentUserId;
+ const teammate = getUser(state, channel.teammate_id);
+ if (teammate && teammate.delete_at) {
+ teammateDeletedAt = teammate.delete_at;
+ }
}
return {
@@ -33,6 +38,7 @@ function makeMapStateToProps() {
isMyUser,
mentions: member ? member.mention_count : 0,
status: channel.status,
+ teammateDeletedAt,
theme: getTheme(state),
type: channel.type
};
diff --git a/app/components/channel_icon.js b/app/components/channel_icon.js
index edc78b16d..ff4585d7f 100644
--- a/app/components/channel_icon.js
+++ b/app/components/channel_icon.js
@@ -9,7 +9,13 @@ import {
} from 'react-native';
import Icon from 'react-native-vector-icons/FontAwesome';
-import {AwayAvatar, DndAvatar, OfflineAvatar, OnlineAvatar} from 'app/components/status_icons';
+import {
+ ArchiveIcon,
+ AwayAvatar,
+ DndAvatar,
+ OfflineAvatar,
+ OnlineAvatar
+} from 'app/components/status_icons';
import {General} from 'mattermost-redux/constants';
@@ -23,6 +29,7 @@ export default class ChannelIcon extends React.PureComponent {
membersCount: PropTypes.number,
size: PropTypes.number,
status: PropTypes.string,
+ teammateDeletedAt: PropTypes.number,
theme: PropTypes.object.isRequired,
type: PropTypes.string.isRequired
};
@@ -35,7 +42,17 @@ export default class ChannelIcon extends React.PureComponent {
};
render() {
- const {isActive, isUnread, isInfo, membersCount, size, status, theme, type} = this.props;
+ const {
+ isActive,
+ isUnread,
+ isInfo,
+ membersCount,
+ size,
+ status,
+ teammateDeletedAt,
+ theme,
+ type
+ } = this.props;
const style = getStyleSheet(theme);
let activeIcon;
@@ -89,6 +106,14 @@ export default class ChannelIcon extends React.PureComponent {
);
+ } else if (type === General.DM_CHANNEL && teammateDeletedAt) {
+ icon = (
+
+ );
} else if (type === General.DM_CHANNEL) {
switch (status) {
case General.AWAY:
diff --git a/app/components/channel_intro/index.js b/app/components/channel_intro/index.js
index 51900a86c..496af524b 100644
--- a/app/components/channel_intro/index.js
+++ b/app/components/channel_intro/index.js
@@ -10,24 +10,14 @@ import {getCurrentUserId, getUser, makeGetProfilesInChannel} from 'mattermost-re
import {getTheme} from 'mattermost-redux/selectors/entities/preferences';
+import {getChannelMembersForDm} from 'app/selectors/channel';
+
import ChannelIntro from './channel_intro';
function makeMapStateToProps() {
const getChannel = makeGetChannel();
const getProfilesInChannel = makeGetProfilesInChannel();
- const getOtherUserIdForDm = createSelector(
- (state, channel) => channel,
- getCurrentUserId,
- (channel, currentUserId) => {
- if (!channel) {
- return '';
- }
-
- return channel.name.split('__').find((m) => m !== currentUserId) || currentUserId;
- }
- );
-
const getChannelMembers = createSelector(
getCurrentUserId,
(state, channel) => getProfilesInChannel(state, channel.id),
@@ -37,17 +27,6 @@ function makeMapStateToProps() {
}
);
- const getChannelMembersForDm = createSelector(
- (state, channel) => getUser(state, getOtherUserIdForDm(state, channel)),
- (otherUser) => {
- if (!otherUser) {
- return [];
- }
-
- return [otherUser];
- }
- );
-
return function mapStateToProps(state, ownProps) {
const currentChannel = getChannel(state, {id: ownProps.channelId}) || {};
const {status: getPostsRequestStatus} = state.requests.posts.getPosts;
diff --git a/app/components/post_textbox/index.js b/app/components/post_textbox/index.js
index 37f538472..02a0fe526 100644
--- a/app/components/post_textbox/index.js
+++ b/app/components/post_textbox/index.js
@@ -4,8 +4,10 @@
import {bindActionCreators} from 'redux';
import {connect} from 'react-redux';
+import {General} from 'mattermost-redux/constants';
+
import {createPost} from 'mattermost-redux/actions/posts';
-import {getCurrentChannelId} from 'mattermost-redux/selectors/entities/channels';
+import {getCurrentChannel} from 'mattermost-redux/selectors/entities/channels';
import {canUploadFilesOnMobile} from 'mattermost-redux/selectors/entities/general';
import {getTheme} from 'mattermost-redux/selectors/entities/preferences';
import {getCurrentUserId} from 'mattermost-redux/selectors/entities/users';
@@ -17,17 +19,28 @@ import {handleClearFiles, handleClearFailedFiles, handleRemoveLastFile, handleUp
import {handleCommentDraftChanged, handleCommentDraftSelectionChanged} from 'app/actions/views/thread';
import {userTyping} from 'app/actions/views/typing';
import {getCurrentChannelDraft, getThreadDraft} from 'app/selectors/views';
+import {getChannelMembersForDm} from 'app/selectors/channel';
import PostTextbox from './post_textbox';
function mapStateToProps(state, ownProps) {
const currentDraft = ownProps.rootId ? getThreadDraft(state, ownProps.rootId) : getCurrentChannelDraft(state);
+ const currentChannel = getCurrentChannel(state);
+ let deactivatedChannel = false;
+ if (currentChannel.type === General.DM_CHANNEL) {
+ const teammate = getChannelMembersForDm(state, currentChannel);
+ if (teammate.length && teammate[0].delete_at) {
+ deactivatedChannel = true;
+ }
+ }
+
return {
- channelId: ownProps.channelId || getCurrentChannelId(state),
+ channelId: ownProps.channelId || currentChannel.id,
canUploadFiles: canUploadFilesOnMobile(state),
channelIsLoading: state.views.channel.loading,
currentUserId: getCurrentUserId(state),
+ deactivatedChannel,
files: currentDraft.files,
theme: getTheme(state),
uploadFileRequestStatus: state.requests.files.uploadFiles.status,
diff --git a/app/components/post_textbox/post_textbox.js b/app/components/post_textbox/post_textbox.js
index 736b0f588..23ff73016 100644
--- a/app/components/post_textbox/post_textbox.js
+++ b/app/components/post_textbox/post_textbox.js
@@ -3,7 +3,7 @@
import React, {PureComponent} from 'react';
import PropTypes from 'prop-types';
-import {Alert, BackHandler, Keyboard, Platform, TextInput, TouchableOpacity, View} from 'react-native';
+import {Alert, BackHandler, Keyboard, Platform, Text, TextInput, TouchableOpacity, View} from 'react-native';
import {intlShape} from 'react-intl';
import {RequestStatus} from 'mattermost-redux/constants';
@@ -41,6 +41,7 @@ export default class PostTextbox extends PureComponent {
channelId: PropTypes.string.isRequired,
channelIsLoading: PropTypes.bool.isRequired,
currentUserId: PropTypes.string.isRequired,
+ deactivatedChannel: PropTypes.bool.isRequired,
files: PropTypes.array,
navigator: PropTypes.object,
rootId: PropTypes.string,
@@ -95,7 +96,9 @@ export default class PostTextbox extends PureComponent {
};
blur = () => {
- this.refs.input.blur();
+ if (this.refs.input) {
+ this.refs.input.blur();
+ }
};
canSend = () => {
@@ -409,16 +412,28 @@ export default class PostTextbox extends PureComponent {
canUploadFiles,
channelId,
channelIsLoading,
+ deactivatedChannel,
files,
navigator,
rootId,
theme
} = this.props;
- const {showFileMaxWarning} = this.state;
const style = getStyleSheet(theme);
- const textInputHeight = Math.min(this.state.contentHeight, MAX_CONTENT_HEIGHT);
+ if (deactivatedChannel) {
+ return (
+
+ {intl.formatMessage({
+ id: 'create_post.deactivated',
+ defaultMessage: 'You are viewing an archived channel with a deactivated user.'
+ })}
+
+ );
+ }
+ const {showFileMaxWarning} = this.state;
+
+ const textInputHeight = Math.min(this.state.contentHeight, MAX_CONTENT_HEIGHT);
const textValue = channelIsLoading ? '' : this.state.value;
let placeholder;
@@ -533,6 +548,19 @@ const getStyleSheet = makeStyleSheetFromTheme((theme) => {
borderTopWidth: 1,
borderTopColor: changeOpacity(theme.centerChannelColor, 0.20)
},
+ deactivatedMessage: {
+ color: changeOpacity(theme.centerChannelColor, 0.8),
+ fontSize: 15,
+ lineHeight: 22,
+ alignItems: 'flex-end',
+ flexDirection: 'row',
+ paddingVertical: 4,
+ backgroundColor: theme.centerChannelBg,
+ borderTopWidth: 1,
+ borderTopColor: changeOpacity(theme.centerChannelColor, 0.20),
+ marginLeft: 10,
+ marginRight: 10
+ },
sendButtonContainer: {
justifyContent: 'flex-end',
paddingHorizontal: 5,
diff --git a/app/components/status_icons/archive_icon.js b/app/components/status_icons/archive_icon.js
new file mode 100644
index 000000000..126102b4a
--- /dev/null
+++ b/app/components/status_icons/archive_icon.js
@@ -0,0 +1,36 @@
+// Copyright (c) 2018-present Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+import React, {PureComponent} from 'react';
+import PropTypes from 'prop-types';
+import {View} from 'react-native';
+import Svg, {
+ Path
+} from 'react-native-svg';
+
+export default class ArchiveIcon extends PureComponent {
+ static propTypes = {
+ width: PropTypes.number.isRequired,
+ height: PropTypes.number.isRequired,
+ color: PropTypes.string.isRequired
+ };
+
+ render() {
+ const {color, height, width} = this.props;
+
+ return (
+
+
+
+ );
+ }
+}
diff --git a/app/components/status_icons/index.js b/app/components/status_icons/index.js
index c989b3f1e..615c5983a 100644
--- a/app/components/status_icons/index.js
+++ b/app/components/status_icons/index.js
@@ -1,6 +1,7 @@
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
+import ArchiveIcon from './archive_icon';
import AwayAvatar from './away_avatar';
import AwayIcon from './away_icon';
import DndAvatar from './dnd_avatar';
@@ -11,6 +12,7 @@ import OnlineAvatar from './online_avatar';
import OnlineIcon from './online_icon';
export {
+ ArchiveIcon,
AwayAvatar,
AwayIcon,
DndAvatar,
diff --git a/app/selectors/channel.js b/app/selectors/channel.js
new file mode 100644
index 000000000..eef0a06d4
--- /dev/null
+++ b/app/selectors/channel.js
@@ -0,0 +1,29 @@
+// Copyright (c) 2018-present Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+import {createSelector} from 'reselect';
+
+import {getCurrentUserId, getUser} from 'mattermost-redux/selectors/entities/users';
+
+const getOtherUserIdForDm = createSelector(
+ (state, channel) => channel,
+ getCurrentUserId,
+ (channel, currentUserId) => {
+ if (!channel) {
+ return '';
+ }
+
+ return channel.name.split('__').find((m) => m !== currentUserId) || currentUserId;
+ }
+);
+
+export const getChannelMembersForDm = createSelector(
+ (state, channel) => getUser(state, getOtherUserIdForDm(state, channel)),
+ (otherUser) => {
+ if (!otherUser) {
+ return [];
+ }
+
+ return [otherUser];
+ }
+);