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:
parent
2f3a6f98ac
commit
0d0dfb1057
23 changed files with 364 additions and 125 deletions
|
|
@ -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],
|
||||
|
|
|
|||
|
|
@ -17,6 +17,8 @@ describe('AnnouncementBanner', () => {
|
|||
bannerEnabled: true,
|
||||
bannerText: 'Banner Text',
|
||||
bannerTextColor: '#fff',
|
||||
navigator: {},
|
||||
theme: {},
|
||||
};
|
||||
|
||||
test('should match snapshot', () => {
|
||||
|
|
|
|||
|
|
@ -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'
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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',
|
||||
},
|
||||
};
|
||||
});
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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}
|
||||
/>
|
||||
);
|
||||
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@ describe('ChannelItem', () => {
|
|||
sidebarTextHoverBg: '#aaa',
|
||||
},
|
||||
unreadMsgs: 1,
|
||||
isArchived: false,
|
||||
};
|
||||
|
||||
test('should match snapshot', () => {
|
||||
|
|
|
|||
|
|
@ -72,6 +72,7 @@ function makeMapStateToProps() {
|
|||
theme: getTheme(state),
|
||||
type: channel.type,
|
||||
unreadMsgs,
|
||||
isArchived: channel.delete_at !== 0,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
},
|
||||
};
|
||||
});
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
},
|
||||
};
|
||||
});
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ function makeMapStateToProps() {
|
|||
rootId: ownProps.rootId,
|
||||
postIds: getPostIdsForThread(state, ownProps.rootId),
|
||||
theme: getTheme(state),
|
||||
channelIsArchived: channel ? channel.delete_at !== 0 : false,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
4
package-lock.json
generated
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
Loading…
Reference in a new issue