MM-11070: Adds ability to view archived channels. (#1927)

* MM-11070: Adds ability to view archived channels.

* MM-11070: Switches to existing selector.

* MM-11070: Uses boolean prop instead of object.

* MM-11070: Reuses web translations.

* MM-11070: Adds archived message to thread view. Switches to selector for postIDs of archived channels.

* MM-11070: Removed unused import.

* MM-11070: Removes edit and delete options from longpress of archived posts.

* MM-11070: Moves closure variable.

* MM-11070: Switch from hard-coded to theme color.

* MM-11070: Hides actions in header of archived channels.

* MM-11070: Updates Redux.

* MM-11070: Re-adds the 'Leave Channel' option for archived channels.
This commit is contained in:
Martin Kraft 2018-07-31 15:27:38 -04:00 committed by Harrison Healey
parent 2f3a6f98ac
commit 0d0dfb1057
23 changed files with 364 additions and 125 deletions

View file

@ -10,6 +10,8 @@ ShallowWrapper {
bannerEnabled={true}
bannerText="Banner Text"
bannerTextColor="#fff"
navigator={Object {}}
theme={Object {}}
/>,
Symbol(enzyme.__renderer__): Object {
"batchedUpdates": [Function],
@ -362,6 +364,8 @@ ShallowWrapper {
bannerEnabled={false}
bannerText="Banner Text"
bannerTextColor="#fff"
navigator={Object {}}
theme={Object {}}
/>,
Symbol(enzyme.__renderer__): Object {
"batchedUpdates": [Function],

View file

@ -17,6 +17,8 @@ describe('AnnouncementBanner', () => {
bannerEnabled: true,
bannerText: 'Banner Text',
bannerTextColor: '#fff',
navigator: {},
theme: {},
};
test('should match snapshot', () => {

View file

@ -25,6 +25,7 @@ export default class ChannelIcon extends React.PureComponent {
teammateDeletedAt: PropTypes.number,
theme: PropTypes.object.isRequired,
type: PropTypes.string.isRequired,
isArchived: PropTypes.bool.isRequired,
};
static defaultProps = {
@ -45,6 +46,7 @@ export default class ChannelIcon extends React.PureComponent {
teammateDeletedAt,
theme,
type,
isArchived,
} = this.props;
const style = getStyleSheet(theme);
@ -76,8 +78,14 @@ export default class ChannelIcon extends React.PureComponent {
}
let icon;
if (type === General.OPEN_CHANNEL) {
if (isArchived) {
icon = (
<Icon
name='archive'
style={[style.icon, unreadIcon, activeIcon, {fontSize: size}]}
/>
);
} else if (type === General.OPEN_CHANNEL) {
icon = (
<Icon
name='globe'

View file

@ -34,6 +34,7 @@ const POST_TIMEOUT = 20000;
function mapStateToProps(state, ownProps) {
const post = getPost(state, ownProps.postId);
const channel = getChannel(state, post.channel_id) || {};
const channelIsArchived = channel ? channel.delete_at !== 0 : false;
const teamId = channel.team_id;
let canAddReaction = true;
@ -68,8 +69,10 @@ function mapStateToProps(state, ownProps) {
let canDelete = false;
let canEdit = false;
if (post) {
canDelete = canDeletePost(state, config, license, currentTeamId, currentChannelId, currentUserId, post, isAdmin, isSystemAdmin);
canEdit = canEditPost(state, config, license, currentTeamId, currentChannelId, currentUserId, post);
if (!channelIsArchived) {
canDelete = canDeletePost(state, config, license, currentTeamId, currentChannelId, currentUserId, post, isAdmin, isSystemAdmin);
canEdit = canEditPost(state, config, license, currentTeamId, currentChannelId, currentUserId, post);
}
}
let isPostAddChannelMember = false;

View file

@ -7,14 +7,14 @@ import {connect} from 'react-redux';
import {General} from 'mattermost-redux/constants';
import {createPost} from 'mattermost-redux/actions/posts';
import {setStatus} from 'mattermost-redux/actions/users';
import {getCurrentChannel, isCurrentChannelReadOnly} from 'mattermost-redux/selectors/entities/channels';
import {getCurrentChannel, isCurrentChannelReadOnly, getDefaultChannel} from 'mattermost-redux/selectors/entities/channels';
import {canUploadFilesOnMobile, getConfig} from 'mattermost-redux/selectors/entities/general';
import {getTheme} from 'mattermost-redux/selectors/entities/preferences';
import {getCurrentUserId, getStatusForUserId} from 'mattermost-redux/selectors/entities/users';
import {executeCommand} from 'app/actions/views/command';
import {addReactionToLatestPost} from 'app/actions/views/emoji';
import {handlePostDraftChanged} from 'app/actions/views/channel';
import {handlePostDraftChanged, setChannelDisplayName, setChannelLoading} from 'app/actions/views/channel';
import {handleClearFiles, handleClearFailedFiles, handleRemoveLastFile, initUploadFiles} from 'app/actions/views/file_upload';
import {handleCommentDraftChanged, handleCommentDraftSelectionChanged} from 'app/actions/views/thread';
import {userTyping} from 'app/actions/views/typing';
@ -47,6 +47,7 @@ function mapStateToProps(state, ownProps) {
canUploadFiles: canUploadFilesOnMobile(state),
channelIsLoading: state.views.channel.loading,
channelIsReadOnly: isCurrentChannelReadOnly(state),
channelIsArchived: ownProps.channelIsArchived || (currentChannel ? currentChannel.delete_at !== 0 : false),
currentUserId,
userIsOutOfOffice,
deactivatedChannel,
@ -55,6 +56,7 @@ function mapStateToProps(state, ownProps) {
theme: getTheme(state),
uploadFileRequestStatus: state.requests.files.uploadFiles.status,
value: currentDraft.draft,
defaultChannel: getDefaultChannel(state),
};
}
@ -73,6 +75,8 @@ function mapDispatchToProps(dispatch) {
userTyping,
handleCommentDraftSelectionChanged,
setStatus,
setChannelDisplayName,
setChannelLoading,
}, dispatch),
};
}

View file

@ -5,6 +5,7 @@ import React, {PureComponent} from 'react';
import PropTypes from 'prop-types';
import {Alert, BackHandler, Keyboard, Platform, Text, TextInput, TouchableOpacity, View} from 'react-native';
import {intlShape} from 'react-intl';
import Button from 'react-native-button';
import {General, RequestStatus} from 'mattermost-redux/constants';
import EventEmitter from 'mattermost-redux/utils/event_emitter';
@ -15,6 +16,8 @@ import QuickTextInput from 'app/components/quick_text_input';
import {INITIAL_HEIGHT, INSERT_TO_COMMENT, INSERT_TO_DRAFT, IS_REACTION_REGEX, MAX_CONTENT_HEIGHT, MAX_FILE_COUNT} from 'app/constants/post_textbox';
import {confirmOutOfOfficeDisabled} from 'app/utils/status';
import {changeOpacity, makeStyleSheetFromTheme} from 'app/utils/theme';
import FormattedMarkdownText from 'app/components/formatted_markdown_text';
import FormattedText from 'app/components/formatted_text';
import Typing from './components/typing';
@ -35,6 +38,8 @@ export default class PostTextbox extends PureComponent {
userTyping: PropTypes.func.isRequired,
handleCommentDraftSelectionChanged: PropTypes.func.isRequired,
setStatus: PropTypes.func.isRequired,
setChannelDisplayName: PropTypes.func.isRequired,
setChannelLoading: PropTypes.func.isRequired,
}).isRequired,
canUploadFiles: PropTypes.bool.isRequired,
channelId: PropTypes.string.isRequired,
@ -50,6 +55,9 @@ export default class PostTextbox extends PureComponent {
uploadFileRequestStatus: PropTypes.string.isRequired,
value: PropTypes.string.isRequired,
userIsOutOfOffice: PropTypes.bool.isRequired,
channelIsArchived: PropTypes.bool,
defaultChannel: PropTypes.object,
onCloseChannel: PropTypes.func,
};
static defaultProps = {
@ -451,6 +459,38 @@ export default class PostTextbox extends PureComponent {
});
};
onCloseChannelPress = () => {
const {defaultChannel, channelId, onCloseChannel} = this.props;
const {setChannelDisplayName, setChannelLoading} = this.props.actions;
setChannelLoading(true);
setChannelDisplayName(defaultChannel.display_name);
EventEmitter.emit('switch_channel', defaultChannel, channelId);
if (onCloseChannel) {
onCloseChannel();
}
};
archivedView = (theme, style) => {
return (<View style={style.archivedWrapper}>
<FormattedMarkdownText
id='archivedChannelMessage'
defaultMessage='You are viewing an **archived channel**. New messages cannot be posted.'
theme={theme}
style={style.archivedText}
/>
<Button
containerStyle={style.closeButton}
onPress={this.onCloseChannelPress}
>
<FormattedText
id='center_panel.archived.closeChannel'
defaultMessage='Close Channel'
style={style.closeButtonText}
/>
</Button>
</View>);
};
render() {
const {intl} = this.context;
const {
@ -463,6 +503,7 @@ export default class PostTextbox extends PureComponent {
navigator,
rootId,
theme,
channelIsArchived,
} = this.props;
const style = getStyleSheet(theme);
@ -528,7 +569,7 @@ export default class PostTextbox extends PureComponent {
value={this.state.value}
rootId={rootId}
/>
<View style={style.inputWrapper}>
{!channelIsArchived && <View style={style.inputWrapper}>
{!channelIsReadOnly && attachmentButton}
<View style={[inputContainerStyle, (channelIsReadOnly && {marginLeft: 10})]}>
<InputComponent
@ -551,7 +592,8 @@ export default class PostTextbox extends PureComponent {
/>
{this.renderSendButton()}
</View>
</View>
</View>}
{channelIsArchived && this.archivedView(theme, style)}
</View>
);
}
@ -624,5 +666,31 @@ const getStyleSheet = makeStyleSheetFromTheme((theme) => {
alignItems: 'center',
justifyContent: 'center',
},
archivedWrapper: {
paddingLeft: 20,
paddingRight: 20,
paddingTop: 10,
paddingBottom: 10,
borderTopWidth: 1,
borderTopColor: changeOpacity(theme.centerChannelColor, 0.20),
},
archivedText: {
textAlign: 'center',
color: theme.centerChannelColor,
},
closeButton: {
backgroundColor: theme.buttonBg,
alignItems: 'center',
paddingTop: 5,
paddingBottom: 5,
borderRadius: 4,
marginTop: 10,
height: 40,
},
closeButtonText: {
marginTop: 7,
color: 'white',
fontWeight: 'bold',
},
};
});

View file

@ -23,10 +23,14 @@ function makeMapStateToProps() {
const post = getPost(state, ownProps.postId);
const channel = getChannel(state, post.channel_id) || {};
const teamId = channel.team_id;
const channelIsArchived = channel.delete_at !== 0;
let canAddReaction = true;
let canRemoveReaction = true;
if (hasNewPermissions(state)) {
if (channelIsArchived) {
canAddReaction = false;
canRemoveReaction = false;
} else if (hasNewPermissions(state)) {
canAddReaction = haveIChannelPermission(state, {
team: teamId,
channel: post.channel_id,

View file

@ -68,25 +68,28 @@ export default class Reactions extends PureComponent {
};
render() {
const {position, reactions} = this.props;
const {position, reactions, canAddReaction} = this.props;
const styles = getStyleSheet(this.props.theme);
if (!reactions.size) {
return null;
}
const addMoreReactions = (
<TouchableOpacity
key='addReaction'
onPress={this.props.onAddReaction}
style={[styles.reaction]}
>
<Image
source={addReactionIcon}
style={styles.addReaction}
/>
</TouchableOpacity>
);
let addMoreReactions = null;
if (canAddReaction) {
addMoreReactions = (
<TouchableOpacity
key='addReaction'
onPress={this.props.onAddReaction}
style={[styles.reaction]}
>
<Image
source={addReactionIcon}
style={styles.addReaction}
/>
</TouchableOpacity>
);
}
const reactionElements = [];
switch (position) {

View file

@ -9,6 +9,7 @@ ShallowWrapper {
currentChannelId="current_channel_id"
displayName="display_name"
fake={false}
isArchived={false}
isChannelMuted={false}
isMyUser={true}
isUnread={true}
@ -81,6 +82,7 @@ ShallowWrapper {
<ChannelIcon
channelId="channel_id"
isActive={false}
isArchived={false}
isInfo={false}
isUnread={true}
membersCount={1}
@ -159,6 +161,7 @@ ShallowWrapper {
<ChannelIcon
channelId="channel_id"
isActive={false}
isArchived={false}
isInfo={false}
isUnread={true}
membersCount={1}
@ -229,6 +232,7 @@ ShallowWrapper {
<ChannelIcon
channelId="channel_id"
isActive={false}
isArchived={false}
isInfo={false}
isUnread={true}
membersCount={1}
@ -291,6 +295,7 @@ ShallowWrapper {
<ChannelIcon
channelId="channel_id"
isActive={false}
isArchived={false}
isInfo={false}
isUnread={true}
membersCount={1}
@ -351,6 +356,7 @@ ShallowWrapper {
"props": Object {
"channelId": "channel_id",
"isActive": false,
"isArchived": false,
"isInfo": false,
"isUnread": true,
"membersCount": 1,
@ -451,6 +457,7 @@ ShallowWrapper {
<ChannelIcon
channelId="channel_id"
isActive={false}
isArchived={false}
isInfo={false}
isUnread={true}
membersCount={1}
@ -529,6 +536,7 @@ ShallowWrapper {
<ChannelIcon
channelId="channel_id"
isActive={false}
isArchived={false}
isInfo={false}
isUnread={true}
membersCount={1}
@ -599,6 +607,7 @@ ShallowWrapper {
<ChannelIcon
channelId="channel_id"
isActive={false}
isArchived={false}
isInfo={false}
isUnread={true}
membersCount={1}
@ -661,6 +670,7 @@ ShallowWrapper {
<ChannelIcon
channelId="channel_id"
isActive={false}
isArchived={false}
isInfo={false}
isUnread={true}
membersCount={1}
@ -721,6 +731,7 @@ ShallowWrapper {
"props": Object {
"channelId": "channel_id",
"isActive": false,
"isArchived": false,
"isInfo": false,
"isUnread": true,
"membersCount": 1,

View file

@ -38,6 +38,7 @@ export default class ChannelItem extends PureComponent {
type: PropTypes.string.isRequired,
theme: PropTypes.object.isRequired,
unreadMsgs: PropTypes.number.isRequired,
isArchived: PropTypes.bool.isRequired,
};
static defaultProps = {
@ -97,8 +98,15 @@ export default class ChannelItem extends PureComponent {
teammateDeletedAt,
theme,
type,
isArchived,
} = this.props;
// Only ever show an archived channel if it's the currently viewed channel.
// It should disappear as soon as one navigates to another channel.
if (isArchived && (currentChannelId !== channelId)) {
return null;
}
if (!this.showChannelAsUnread() && shouldHideChannel) {
return null;
}
@ -161,6 +169,7 @@ export default class ChannelItem extends PureComponent {
teammateDeletedAt={teammateDeletedAt}
theme={theme}
type={type}
isArchived={isArchived}
/>
);

View file

@ -34,6 +34,7 @@ describe('ChannelItem', () => {
sidebarTextHoverBg: '#aaa',
},
unreadMsgs: 1,
isArchived: false,
};
test('should match snapshot', () => {

View file

@ -72,6 +72,7 @@ function makeMapStateToProps() {
theme: getTheme(state),
type: channel.type,
unreadMsgs,
isArchived: channel.delete_at !== 0,
};
};
}

View file

@ -20,6 +20,7 @@ export default class ChannelTitle extends PureComponent {
isChannelMuted: PropTypes.bool,
onPress: PropTypes.func,
theme: PropTypes.object,
isArchived: PropTypes.bool,
};
static defaultProps = {
@ -28,6 +29,19 @@ export default class ChannelTitle extends PureComponent {
theme: {},
};
archiveIcon(style) {
let content = null;
if (this.props.isArchived) {
content = (
<Icon
name='archive'
style={[style.archiveIcon]}
/>
);
}
return content;
}
render() {
const {currentChannelName, displayName, isChannelMuted, onPress, theme} = this.props;
const channelName = displayName || currentChannelName;
@ -60,6 +74,7 @@ export default class ChannelTitle extends PureComponent {
onPress={onPress}
>
<View style={style.wrapper}>
{this.archiveIcon(style)}
<Text
ellipsizeMode='tail'
numberOfLines={1}
@ -104,5 +119,10 @@ const getStyle = makeStyleSheetFromTheme((theme) => {
opacity: 0.6,
marginLeft: 0,
},
archiveIcon: {
fontSize: 17,
color: theme.sidebarHeaderTextColor,
paddingRight: 7,
},
};
});

View file

@ -15,6 +15,7 @@ function mapStateToProps(state) {
return {
currentChannelName: currentChannel ? currentChannel.display_name : '',
isArchived: currentChannel ? currentChannel.delete_at !== 0 : false,
displayName: state.views.channel.displayName,
isChannelMuted: isChannelMuted(myChannelMember),
theme: getTheme(state),

View file

@ -328,20 +328,105 @@ export default class ChannelInfo extends PureComponent {
return isDirectMessage || isGroupMessage;
};
actionsRows = (style, channelIsArchived) => {
const {
currentChannelMemberCount,
canManageUsers,
canEditChannel,
theme,
} = this.props;
if (channelIsArchived) {
return null;
}
return (<View>
<ChannelInfoRow
action={this.handleFavorite}
defaultMessage='Favorite'
detail={this.state.isFavorite}
icon='star-o'
textId='mobile.routes.channelInfo.favorite'
togglable={true}
theme={theme}
/>
<View style={style.separator}/>
<ChannelInfoRow
action={this.handleMuteChannel}
defaultMessage='Mute channel'
detail={this.state.isMuted}
icon='bell-slash-o'
textId='channel_notifications.muteChannel.settings'
togglable={true}
theme={theme}
/>
{
/**
<ChannelInfoRow
action={() => true}
defaultMessage='Notification Preferences'
icon='bell-o'
textId='channel_header.notificationPreferences'
theme={theme}
/>
<View style={style.separator}/>
**/
}
{this.renderViewOrManageMembersRow() &&
<View>
<View style={style.separator}/>
<ChannelInfoRow
action={this.goToChannelMembers}
defaultMessage={canManageUsers ? 'Manage Members' : 'View Members'}
detail={currentChannelMemberCount}
icon='users'
textId={canManageUsers ? 'channel_header.manageMembers' : 'channel_header.viewMembers'}
theme={theme}
/>
</View>
}
{canManageUsers &&
<View>
<View style={style.separator}/>
<ChannelInfoRow
action={this.goToChannelAddMembers}
defaultMessage='Add Members'
icon='user-plus'
textId='channel_header.addMembers'
theme={theme}
/>
</View>
}
{canEditChannel && (
<View>
<View style={style.separator}/>
<ChannelInfoRow
action={this.handleChannelEdit}
defaultMessage='Edit Channel'
icon='edit'
textId='mobile.channel_info.edit'
theme={theme}
/>
</View>
)}
<View style={style.separator}/>
</View>);
};
render() {
const {
canDeleteChannel,
currentChannel,
currentChannelCreatorName,
currentChannelMemberCount,
canManageUsers,
canEditChannel,
navigator,
status,
theme,
} = this.props;
const style = getStyleSheet(theme);
const channelIsArchived = currentChannel.delete_at !== 0;
let i18nId;
let defaultMessage;
@ -363,93 +448,24 @@ export default class ChannelInfo extends PureComponent {
style={style.scrollView}
>
{currentChannel.hasOwnProperty('id') &&
<ChannelInfoHeader
createAt={currentChannel.create_at}
creator={currentChannelCreatorName}
displayName={currentChannel.display_name}
header={currentChannel.header}
memberCount={currentChannelMemberCount}
navigator={navigator}
onPermalinkPress={this.handlePermalinkPress}
purpose={currentChannel.purpose}
status={status}
theme={theme}
type={currentChannel.type}
/>
<ChannelInfoHeader
createAt={currentChannel.create_at}
creator={currentChannelCreatorName}
displayName={currentChannel.display_name}
header={currentChannel.header}
memberCount={currentChannelMemberCount}
navigator={navigator}
onPermalinkPress={this.handlePermalinkPress}
purpose={currentChannel.purpose}
status={status}
theme={theme}
type={currentChannel.type}
/>
}
<View style={style.rowsContainer}>
<ChannelInfoRow
action={this.handleFavorite}
defaultMessage='Favorite'
detail={this.state.isFavorite}
icon='star-o'
textId='mobile.routes.channelInfo.favorite'
togglable={true}
theme={theme}
/>
<View style={style.separator}/>
<ChannelInfoRow
action={this.handleMuteChannel}
defaultMessage='Mute channel'
detail={this.state.isMuted}
icon='bell-slash-o'
textId='channel_notifications.muteChannel.settings'
togglable={true}
theme={theme}
/>
{
/**
<ChannelInfoRow
action={() => true}
defaultMessage='Notification Preferences'
icon='bell-o'
textId='channel_header.notificationPreferences'
theme={theme}
/>
<View style={style.separator}/>
**/
}
{this.renderViewOrManageMembersRow() &&
<View>
<View style={style.separator}/>
<ChannelInfoRow
action={this.goToChannelMembers}
defaultMessage={canManageUsers ? 'Manage Members' : 'View Members'}
detail={currentChannelMemberCount}
icon='users'
textId={canManageUsers ? 'channel_header.manageMembers' : 'channel_header.viewMembers'}
theme={theme}
/>
</View>
}
{canManageUsers &&
<View>
<View style={style.separator}/>
<ChannelInfoRow
action={this.goToChannelAddMembers}
defaultMessage='Add Members'
icon='user-plus'
textId='channel_header.addMembers'
theme={theme}
/>
</View>
}
{canEditChannel && (
<View>
<View style={style.separator}/>
<ChannelInfoRow
action={this.handleChannelEdit}
defaultMessage='Edit Channel'
icon='edit'
textId='mobile.channel_info.edit'
theme={theme}
/>
</View>
)}
{this.actionsRows(style, channelIsArchived)}
{this.renderLeaveOrDeleteChannelRow() &&
<View>
<View style={style.separator}/>
<ChannelInfoRow
action={this.handleLeave}
defaultMessage='Leave Channel'
@ -460,7 +476,7 @@ export default class ChannelInfo extends PureComponent {
</View>
}
</View>
{this.renderLeaveOrDeleteChannelRow() && canDeleteChannel &&
{this.renderLeaveOrDeleteChannelRow() && canDeleteChannel && !channelIsArchived &&
<View style={style.footer}>
<ChannelInfoRow
action={this.handleDelete}
@ -474,17 +490,17 @@ export default class ChannelInfo extends PureComponent {
</View>
}
{this.renderCloseDirect() &&
<View style={style.footer}>
<ChannelInfoRow
action={this.handleClose}
defaultMessage={defaultMessage}
icon='times'
iconColor='#CA3B27'
textId={i18nId}
textColor='#CA3B27'
theme={theme}
/>
</View>
<View style={style.footer}>
<ChannelInfoRow
action={this.handleClose}
defaultMessage={defaultMessage}
icon='times'
iconColor='#CA3B27'
textId={i18nId}
textColor='#CA3B27'
theme={theme}
/>
</View>
}
</ScrollView>
</View>

View file

@ -44,6 +44,7 @@ function makeMapStateToProps() {
return {
channelId: channel ? channel.id : '',
channelIsArchived: channel ? channel.delete_at !== 0 : false,
channelName: channel ? channel.display_name : '',
channelTeamId: channel ? channel.team_id : '',
currentTeamId: getCurrentTeamId(state),

View file

@ -12,6 +12,7 @@ import {
import {intlShape} from 'react-intl';
import * as Animatable from 'react-native-animatable';
import MaterialIcon from 'react-native-vector-icons/MaterialIcons';
import AwesomeIcon from 'react-native-vector-icons/FontAwesome';
import {General} from 'mattermost-redux/constants';
import EventEmitter from 'mattermost-redux/utils/event_emitter';
@ -59,6 +60,7 @@ export default class Permalink extends PureComponent {
setChannelLoading: PropTypes.func.isRequired,
}).isRequired,
channelId: PropTypes.string,
channelIsArchived: PropTypes.bool,
channelName: PropTypes.string,
channelTeamId: PropTypes.string,
currentTeamId: PropTypes.string.isRequired,
@ -301,6 +303,20 @@ export default class Permalink extends PureComponent {
}
};
archivedIcon = (style) => {
let ico = null;
if (this.props.channelIsArchived) {
ico = (<Text>
<AwesomeIcon
name='archive'
style={[style.archiveIcon]}
/>
{' '}
</Text>);
}
return ico;
};
render() {
const {
currentUserId,
@ -386,6 +402,7 @@ export default class Permalink extends PureComponent {
numberOfLines={1}
style={style.title}
>
{this.archivedIcon(style)}
{title}
</Text>
</View>
@ -492,5 +509,10 @@ const getStyleSheet = makeStyleSheetFromTheme((theme) => {
color: changeOpacity(theme.centerChannelColor, 0.4),
fontSize: 15,
},
archiveIcon: {
color: theme.centerChannelColor,
fontSize: 16,
paddingRight: 20,
},
};
});

View file

@ -6,7 +6,7 @@ import {connect} from 'react-redux';
import {selectFocusedPostId, selectPost} from 'mattermost-redux/actions/posts';
import {clearSearch, removeSearchTerms, searchPosts} from 'mattermost-redux/actions/search';
import {getCurrentChannelId} from 'mattermost-redux/selectors/entities/channels';
import {getCurrentChannelId, filterPostIds} from 'mattermost-redux/selectors/entities/channels';
import {getCurrentTeamId} from 'mattermost-redux/selectors/entities/teams';
import {getTheme} from 'mattermost-redux/selectors/entities/preferences';
@ -19,9 +19,11 @@ import Search from './search';
function makeMapStateToProps() {
const preparePostIds = makePreparePostIdsForSearchPosts();
const filterPostIdsByDeletedChannels = filterPostIds((c) => c && c.delete_at !== 0);
return (state) => {
const postIds = preparePostIds(state, state.entities.search.results);
const currentTeamId = getCurrentTeamId(state);
const currentChannelId = getCurrentChannelId(state);
const {recent} = state.entities.search;
@ -32,6 +34,7 @@ function makeMapStateToProps() {
currentChannelId,
isLandscape: isLandscape(state),
postIds,
archivedPostIds: filterPostIdsByDeletedChannels(state, postIds),
recent: recent[currentTeamId],
searchingStatus: searchRequest.status,
theme: getTheme(state),

View file

@ -56,10 +56,10 @@ export default class Search extends PureComponent {
selectPost: PropTypes.func.isRequired,
}).isRequired,
currentTeamId: PropTypes.string.isRequired,
currentChannelId: PropTypes.string.isRequired,
isLandscape: PropTypes.bool.isRequired,
navigator: PropTypes.object,
postIds: PropTypes.array,
archivedPostIds: PropTypes.arrayOf(PropTypes.string),
recent: PropTypes.array.isRequired,
searchingStatus: PropTypes.string,
theme: PropTypes.object.isRequired,
@ -68,6 +68,7 @@ export default class Search extends PureComponent {
static defaultProps = {
postIds: [],
recent: [],
archivedPostIds: [],
};
static contextTypes = {
@ -294,6 +295,30 @@ export default class Search extends PureComponent {
);
};
archivedIndicator = (postID, style) => {
const channelIsArchived = this.props.archivedPostIds.includes(postID);
let archivedIndicator = null;
if (channelIsArchived) {
archivedIndicator = (
<View style={style.archivedIndicator}>
<Text>
<AwesomeIcon
name='archive'
style={style.archivedText}
/>
{' '}
<FormattedText
style={style.archivedText}
id='search_item.channelArchived'
defaultMessage='Archived'
/>
</Text>
</View>
);
}
return archivedIndicator;
};
renderPost = ({item, index}) => {
const {postIds, theme} = this.props;
const {managedConfig} = this.state;
@ -325,6 +350,7 @@ export default class Search extends PureComponent {
return (
<View>
<ChannelDisplayName postId={item}/>
{this.archivedIndicator(postIds[index], style)}
<SearchResultPost
postId={item}
previewPost={this.previewPost}
@ -440,7 +466,7 @@ export default class Search extends PureComponent {
},
});
actions.searchPosts(currentTeamId, terms.trim(), isOrSearch);
actions.searchPosts(currentTeamId, terms.trim(), isOrSearch, true);
};
handleSearchButtonPress = preventDoubleTap((text) => {
@ -749,6 +775,16 @@ const getStyleFromTheme = makeStyleSheetFromTheme((theme) => {
searching: {
marginTop: 65,
},
archivedIndicator: {
alignItems: 'flex-end',
width: 150,
alignSelf: 'flex-end',
marginTop: -17,
marginRight: 10,
},
archivedText: {
color: changeOpacity(theme.centerChannelColor, 0.4),
},
};
});

View file

@ -27,6 +27,7 @@ function makeMapStateToProps() {
rootId: ownProps.rootId,
postIds: getPostIdsForThread(state, ownProps.rootId),
theme: getTheme(state),
channelIsArchived: channel ? channel.delete_at !== 0 : false,
};
};
}

View file

@ -30,6 +30,7 @@ class Thread extends PureComponent {
rootId: PropTypes.string.isRequired,
theme: PropTypes.object.isRequired,
postIds: PropTypes.array.isRequired,
channelIsArchived: PropTypes.bool,
};
state = {};
@ -95,6 +96,23 @@ class Thread extends PureComponent {
return null;
}
onCloseChannel = () => {
this.props.navigator.resetTo({
screen: 'Channel',
title: '',
animated: false,
backButtonTitle: '',
navigatorStyle: {
animated: true,
animationType: 'fade',
navBarHidden: true,
statusBarHidden: false,
statusBarHideWithNavBar: false,
screenBackgroundColor: 'transparent',
},
});
}
render() {
const {
channelId,
@ -103,6 +121,7 @@ class Thread extends PureComponent {
postIds,
rootId,
theme,
channelIsArchived,
} = this.props;
const style = getStyle(theme);
@ -127,9 +146,11 @@ class Thread extends PureComponent {
/>
{this.hasRootPost() &&
<PostTextbox
channelIsArchived={channelIsArchived}
rootId={rootId}
channelId={channelId}
navigator={navigator}
onCloseChannel={this.onCloseChannel}
/>}
</KeyboardLayout>
</SafeAreaView>

4
package-lock.json generated
View file

@ -10108,8 +10108,8 @@
}
},
"mattermost-redux": {
"version": "github:mattermost/mattermost-redux#707dc6926ae3eecb4c7b4ae08dca9be17b933568",
"from": "github:mattermost/mattermost-redux#707dc6926ae3eecb4c7b4ae08dca9be17b933568",
"version": "github:mattermost/mattermost-redux#12353bc88f20dde53b6191747790c6a82b8dd527",
"from": "github:mattermost/mattermost-redux#12353bc88f20dde53b6191747790c6a82b8dd527",
"requires": {
"deep-equal": "1.0.1",
"eslint-plugin-header": "1.2.0",

View file

@ -15,7 +15,7 @@
"intl": "1.2.5",
"jail-monkey": "1.0.0",
"jsc-android": "216113.0.3",
"mattermost-redux": "github:mattermost/mattermost-redux#707dc6926ae3eecb4c7b4ae08dca9be17b933568",
"mattermost-redux": "github:mattermost/mattermost-redux#12353bc88f20dde53b6191747790c6a82b8dd527",
"mime-db": "1.33.0",
"prop-types": "15.6.1",
"react": "16.3.2",