[MM-16136] Update channel related screens (#2915)

* Update screens

* Update login tests

* Remove done

* Fix failing tests

* Update screens and components

* Check styles fix

* Update tests

* Prevent setState call after component unmounts

* Add empty setButtons func to dummy navigator

* Remove platform check

* Remove Platform import

* Update react-native-navigation version

* Add separate showModalOverCurrentContext function

* check-style fixes

* Remove overriding of AppDelegate's window

* Fix modal over current context animation

* Add showSearchModal navigation action

* Check-style fix

* Address review comments

* Update SettingsSidebar and children

* Update EditProfile screen

* Update SettingsSidebar

* Keep track of latest componentId to appear

* Track componentId in state to use in navigation actions

* Update FlaggedPosts and children

* Update RecentMentions

* Update Settings

* Store componentIds in ephemeral store

* Update AttachmentButton

* Remove unnecessary dismissModal

* Fix typo

* Upate NotificationSettings

* Update NotificationSettingsAutoResponder

* Update NotificationSettingsMentions

* Update NotificationSettingsMobile

* Update CreateChannel and children

* Update MoreChannels

* Update ChannelPeek and children

* Update ChannelNavBar and children

* Update ChannelPostList and children

* Update ChannelInfo and children

* Update ChannelAddMembers

* Update ChannelMembers

* Update EditChannel

* Fix setting of top bar buttons

* Update Channel screen and children

* Fix typo

* Use goToScreen for Android too

* Remove navigationComponentId
This commit is contained in:
Miguel Alatzar 2019-06-26 13:46:05 -07:00 committed by Harrison Healey
parent 74c7805e65
commit b65ce5d283
37 changed files with 738 additions and 774 deletions

View file

@ -312,10 +312,8 @@ export function peek(name, passProps = {}, options = {}) {
};
}
export function setButtons(buttons = {leftButtons: [], rightButtons: []}) {
export function setButtons(componentId, buttons = {leftButtons: [], rightButtons: []}) {
return () => {
const componentId = EphemeralStore.getTopComponentId();
Navigation.mergeOptions(componentId, {
topBar: {
...buttons,

View file

@ -18,12 +18,14 @@ const {View: AnimatedView} = Animated;
export default class AnnouncementBanner extends PureComponent {
static propTypes = {
actions: PropTypes.shape({
goToScreen: PropTypes.func.isRequired,
}).isRequired,
bannerColor: PropTypes.string,
bannerDismissed: PropTypes.bool,
bannerEnabled: PropTypes.bool,
bannerText: PropTypes.string,
bannerTextColor: PropTypes.string,
navigator: PropTypes.object.isRequired,
theme: PropTypes.object.isRequired,
};
@ -52,23 +54,16 @@ export default class AnnouncementBanner extends PureComponent {
}
handlePress = () => {
const {navigator, theme} = this.props;
const {actions} = this.props;
const {intl} = this.context;
navigator.push({
screen: 'ExpandedAnnouncementBanner',
title: this.context.intl.formatMessage({
id: 'mobile.announcement_banner.title',
defaultMessage: 'Announcement',
}),
animated: true,
backButtonTitle: '',
navigatorStyle: {
navBarTextColor: theme.sidebarHeaderTextColor,
navBarBackgroundColor: theme.sidebarHeaderBg,
navBarButtonColor: theme.sidebarHeaderTextColor,
screenBackgroundColor: theme.centerChannelBg,
},
const screen = 'ExpandedAnnouncementBanner';
const title = intl.formatMessage({
id: 'mobile.announcement_banner.title',
defaultMessage: 'Announcement',
});
actions.goToScreen(screen, title);
};
toggleBanner = (show = true) => {

View file

@ -12,12 +12,14 @@ jest.useFakeTimers();
describe('AnnouncementBanner', () => {
const baseProps = {
actions: {
goToScreen: jest.fn(),
},
bannerColor: '#ddd',
bannerDismissed: false,
bannerEnabled: true,
bannerText: 'Banner Text',
bannerTextColor: '#fff',
navigator: {},
theme: Preferences.THEMES.default,
};

View file

@ -1,11 +1,14 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {bindActionCreators} from 'redux';
import {connect} from 'react-redux';
import {getConfig, getLicense} from 'mattermost-redux/selectors/entities/general';
import {getTheme} from 'mattermost-redux/selectors/entities/preferences';
import {goToScreen} from 'app/actions/navigation';
import AnnouncementBanner from './announcement_banner';
function mapStateToProps(state) {
@ -23,4 +26,12 @@ function mapStateToProps(state) {
};
}
export default connect(mapStateToProps)(AnnouncementBanner);
function mapDispatchToProps(dispatch) {
return {
actions: bindActionCreators({
goToScreen,
}, dispatch),
};
}
export default connect(mapStateToProps, mapDispatchToProps)(AnnouncementBanner);

View file

@ -4,7 +4,6 @@
import React, {PureComponent} from 'react';
import PropTypes from 'prop-types';
import {
Platform,
Text,
TouchableOpacity,
View,
@ -21,11 +20,13 @@ import {t} from 'app/utils/i18n';
class ChannelIntro extends PureComponent {
static propTypes = {
actions: PropTypes.shape({
goToScreen: PropTypes.func.isRequired,
}).isRequired,
creator: PropTypes.object,
currentChannel: PropTypes.object.isRequired,
currentChannelMembers: PropTypes.array.isRequired,
intl: intlShape.isRequired,
navigator: PropTypes.object.isRequired,
theme: PropTypes.object.isRequired,
};
@ -34,28 +35,14 @@ class ChannelIntro extends PureComponent {
};
goToUserProfile = (userId) => {
const {intl, navigator, theme} = this.props;
const options = {
screen: 'UserProfile',
title: intl.formatMessage({id: 'mobile.routes.user_profile', defaultMessage: 'Profile'}),
animated: true,
backButtonTitle: '',
passProps: {
userId,
},
navigatorStyle: {
navBarTextColor: theme.sidebarHeaderTextColor,
navBarBackgroundColor: theme.sidebarHeaderBg,
navBarButtonColor: theme.sidebarHeaderTextColor,
screenBackgroundColor: theme.centerChannelBg,
},
const {actions, intl} = this.props;
const screen = 'UserProfile';
const title = intl.formatMessage({id: 'mobile.routes.user_profile', defaultMessage: 'Profile'});
const passProps = {
userId,
};
if (Platform.OS === 'ios') {
navigator.push(options);
} else {
navigator.showModal(options);
}
actions.goToScreen(screen, title, passProps);
};
getDisplayName = (member) => {

View file

@ -1,6 +1,7 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {bindActionCreators} from 'redux';
import {connect} from 'react-redux';
import {createSelector} from 'reselect';
@ -10,6 +11,7 @@ import {getCurrentUserId, getUser, makeGetProfilesInChannel} from 'mattermost-re
import {getTheme} from 'mattermost-redux/selectors/entities/preferences';
import {goToScreen} from 'app/actions/navigation';
import {getChannelMembersForDm} from 'app/selectors/channel';
import ChannelIntro from './channel_intro';
@ -52,4 +54,12 @@ function makeMapStateToProps() {
};
}
export default connect(makeMapStateToProps)(ChannelIntro);
function mapDispatchToProps(dispatch) {
return {
actions: bindActionCreators({
goToScreen,
}, dispatch),
};
}
export default connect(makeMapStateToProps, mapDispatchToProps)(ChannelIntro);

View file

@ -0,0 +1,415 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import React, {PureComponent} from 'react';
import PropTypes from 'prop-types';
import {
Platform,
TouchableWithoutFeedback,
View,
Text,
findNodeHandle,
} from 'react-native';
import {KeyboardAwareScrollView} from 'react-native-keyboard-aware-scroll-view';
import ErrorText from 'app/components/error_text';
import FormattedText from 'app/components/formatted_text';
import Loading from 'app/components/loading';
import StatusBar from 'app/components/status_bar';
import TextInputWithLocalizedPlaceholder from 'app/components/text_input_with_localized_placeholder';
import {changeOpacity, makeStyleSheetFromTheme} from 'app/utils/theme';
import {General} from 'mattermost-redux/constants';
import {getShortenedURL} from 'app/utils/url';
import {t} from 'app/utils/i18n';
export default class EditChannelInfo extends PureComponent {
static propTypes = {
actions: PropTypes.shape({
dismissModal: PropTypes.func.isRequired,
popTopScreen: PropTypes.func.isRequired,
}),
theme: PropTypes.object.isRequired,
deviceWidth: PropTypes.number.isRequired,
deviceHeight: PropTypes.number.isRequired,
channelType: PropTypes.string,
enableRightButton: PropTypes.func,
saving: PropTypes.bool.isRequired,
editing: PropTypes.bool,
error: PropTypes.oneOfType([PropTypes.string, PropTypes.object]),
displayName: PropTypes.string,
currentTeamUrl: PropTypes.string,
channelURL: PropTypes.string,
purpose: PropTypes.string,
header: PropTypes.string,
onDisplayNameChange: PropTypes.func,
onChannelURLChange: PropTypes.func,
onPurposeChange: PropTypes.func,
onHeaderChange: PropTypes.func,
oldDisplayName: PropTypes.string,
oldChannelURL: PropTypes.string,
oldHeader: PropTypes.string,
oldPurpose: PropTypes.string,
};
static defaultProps = {
editing: false,
};
constructor(props) {
super(props);
this.nameInput = React.createRef();
this.urlInput = React.createRef();
this.purposeInput = React.createRef();
this.headerInput = React.createRef();
this.lastText = React.createRef();
this.scroll = React.createRef();
}
blur = () => {
if (this.nameInput?.current) {
this.nameInput.current.blur();
}
// TODO: uncomment below once the channel URL field is added
// if (this.urlInput?.current) {
// this.urlInput.current.blur();
// }
if (this.purposeInput?.current) {
this.purposeInput.current.blur();
}
if (this.headerInput?.current) {
this.headerInput.current.blur();
}
if (this.scroll?.current) {
this.scroll.current.scrollToPosition(0, 0, true);
}
};
close = (goBack = false) => {
const {actions} = this.props;
if (goBack) {
actions.popTopScreen();
} else {
actions.dismissModal();
}
};
canUpdate = (displayName, channelURL, purpose, header) => {
const {
oldDisplayName,
oldChannelURL,
oldPurpose,
oldHeader,
} = this.props;
return displayName !== oldDisplayName || channelURL !== oldChannelURL ||
purpose !== oldPurpose || header !== oldHeader;
};
enableRightButton = (enable = false) => {
this.props.enableRightButton(enable);
};
onDisplayNameChangeText = (displayName) => {
const {editing, onDisplayNameChange} = this.props;
onDisplayNameChange(displayName);
if (editing) {
const {channelURL, purpose, header} = this.props;
const canUpdate = this.canUpdate(displayName, channelURL, purpose, header);
this.enableRightButton(canUpdate);
return;
}
const displayNameExists = displayName && displayName.length >= 2;
this.props.enableRightButton(displayNameExists);
};
onDisplayURLChangeText = (channelURL) => {
const {editing, onChannelURLChange} = this.props;
onChannelURLChange(channelURL);
if (editing) {
const {displayName, purpose, header} = this.props;
const canUpdate = this.canUpdate(displayName, channelURL, purpose, header);
this.enableRightButton(canUpdate);
}
};
onPurposeChangeText = (purpose) => {
const {editing, onPurposeChange} = this.props;
onPurposeChange(purpose);
if (editing) {
const {displayName, channelURL, header} = this.props;
const canUpdate = this.canUpdate(displayName, channelURL, purpose, header);
this.enableRightButton(canUpdate);
}
};
onHeaderChangeText = (header) => {
const {editing, onHeaderChange} = this.props;
onHeaderChange(header);
if (editing) {
const {displayName, channelURL, purpose} = this.props;
const canUpdate = this.canUpdate(displayName, channelURL, purpose, header);
this.enableRightButton(canUpdate);
}
};
scrollToEnd = () => {
if (this.scroll?.current && this.lastText?.current) {
this.scroll.current.scrollToFocusedInput(findNodeHandle(this.lastText.current));
}
};
render() {
const {
theme,
editing,
channelType,
currentTeamUrl,
deviceWidth,
deviceHeight,
displayName,
channelURL,
header,
purpose,
} = this.props;
const {error, saving} = this.props;
const fullUrl = currentTeamUrl + '/channels';
const shortUrl = getShortenedURL(fullUrl, 35);
const style = getStyleSheet(theme);
const displayHeaderOnly = channelType === General.DM_CHANNEL ||
channelType === General.GM_CHANNEL;
if (saving) {
return (
<View style={style.container}>
<StatusBar/>
<Loading/>
</View>
);
}
let displayError;
if (error) {
displayError = (
<View style={[style.errorContainer, {width: deviceWidth}]}>
<View style={style.errorWrapper}>
<ErrorText error={error}/>
</View>
</View>
);
}
return (
<View style={style.container}>
<StatusBar/>
<KeyboardAwareScrollView
ref={this.scroll}
style={style.container}
>
{displayError}
<TouchableWithoutFeedback onPress={this.blur}>
<View style={[style.scrollView, {height: deviceHeight + (Platform.OS === 'android' ? 200 : 0)}]}>
{!displayHeaderOnly && (
<View>
<View>
<FormattedText
style={style.title}
id='channel_modal.name'
defaultMessage='Name'
/>
</View>
<View style={style.inputContainer}>
<TextInputWithLocalizedPlaceholder
ref={this.nameInput}
value={displayName}
onChangeText={this.onDisplayNameChangeText}
style={style.input}
autoCapitalize='none'
autoCorrect={false}
placeholder={{id: t('channel_modal.nameEx'), defaultMessage: 'E.g.: "Bugs", "Marketing", "客户支持"'}}
placeholderTextColor={changeOpacity('#000', 0.5)}
underlineColorAndroid='transparent'
disableFullscreenUI={true}
/>
</View>
</View>
)}
{/*TODO: Hide channel url field until it's added to CreateChannel */}
{false && editing && !displayHeaderOnly && (
<View>
<View style={style.titleContainer30}>
<FormattedText
style={style.title}
id='rename_channel.url'
defaultMessage='URL'
/>
<Text style={style.optional}>
{shortUrl}
</Text>
</View>
<View style={style.inputContainer}>
<TextInputWithLocalizedPlaceholder
ref={this.urlInput}
value={channelURL}
onChangeText={this.onDisplayURLChangeText}
style={style.input}
autoCapitalize='none'
autoCorrect={false}
placeholder={{id: t('rename_channel.handleHolder'), defaultMessage: 'lowercase alphanumeric characters'}}
placeholderTextColor={changeOpacity('#000', 0.5)}
underlineColorAndroid='transparent'
disableFullscreenUI={true}
/>
</View>
</View>
)}
{!displayHeaderOnly && (
<View>
<View style={style.titleContainer30}>
<FormattedText
style={style.title}
id='channel_modal.purpose'
defaultMessage='Purpose'
/>
<FormattedText
style={style.optional}
id='channel_modal.optional'
defaultMessage='(optional)'
/>
</View>
<View style={style.inputContainer}>
<TextInputWithLocalizedPlaceholder
ref={this.purposeInput}
value={purpose}
onChangeText={this.onPurposeChangeText}
style={[style.input, {height: 110}]}
autoCapitalize='none'
autoCorrect={false}
placeholder={{id: t('channel_modal.purposeEx'), defaultMessage: 'E.g.: "A channel to file bugs and improvements"'}}
placeholderTextColor={changeOpacity('#000', 0.5)}
multiline={true}
blurOnSubmit={false}
textAlignVertical='top'
underlineColorAndroid='transparent'
disableFullscreenUI={true}
/>
</View>
<View>
<FormattedText
style={style.helpText}
id='channel_modal.descriptionHelp'
defaultMessage='Describe how this channel should be used.'
/>
</View>
</View>
)}
<View style={style.titleContainer15}>
<FormattedText
style={style.title}
id='channel_modal.header'
defaultMessage='Header'
/>
<FormattedText
style={style.optional}
id='channel_modal.optional'
defaultMessage='(optional)'
/>
</View>
<View style={style.inputContainer}>
<TextInputWithLocalizedPlaceholder
ref={this.headerInput}
value={header}
onChangeText={this.onHeaderChangeText}
style={[style.input, {height: 110}]}
autoCapitalize='none'
autoCorrect={false}
placeholder={{id: t('channel_modal.headerEx'), defaultMessage: 'E.g.: "[Link Title](http://example.com)"'}}
placeholderTextColor={changeOpacity('#000', 0.5)}
multiline={true}
blurOnSubmit={false}
onFocus={this.scrollToEnd}
textAlignVertical='top'
underlineColorAndroid='transparent'
disableFullscreenUI={true}
/>
</View>
<View ref={this.lastText}>
<FormattedText
style={style.helpText}
id='channel_modal.headerHelp'
defaultMessage={'Set text that will appear in the header of the channel beside the channel name. For example, include frequently used links by typing [Link Title](http://example.com).'}
/>
</View>
</View>
</TouchableWithoutFeedback>
</KeyboardAwareScrollView>
</View>
);
}
}
const getStyleSheet = makeStyleSheetFromTheme((theme) => {
return {
container: {
flex: 1,
backgroundColor: theme.centerChannelBg,
},
scrollView: {
flex: 1,
backgroundColor: changeOpacity(theme.centerChannelColor, 0.03),
paddingTop: 10,
},
errorContainer: {
backgroundColor: changeOpacity(theme.centerChannelColor, 0.03),
},
errorWrapper: {
justifyContent: 'center',
alignItems: 'center',
},
inputContainer: {
marginTop: 10,
backgroundColor: '#fff',
},
input: {
color: '#333',
fontSize: 14,
height: 40,
paddingHorizontal: 15,
},
titleContainer30: {
flexDirection: 'row',
marginTop: 30,
},
titleContainer15: {
flexDirection: 'row',
marginTop: 15,
},
title: {
fontSize: 14,
color: theme.centerChannelColor,
marginLeft: 15,
},
optional: {
color: changeOpacity(theme.centerChannelColor, 0.5),
fontSize: 14,
marginLeft: 5,
},
helpText: {
fontSize: 14,
color: changeOpacity(theme.centerChannelColor, 0.5),
marginTop: 10,
marginHorizontal: 15,
},
};
});

View file

@ -1,414 +1,23 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import React, {PureComponent} from 'react';
import PropTypes from 'prop-types';
import {bindActionCreators} from 'redux';
import {connect} from 'react-redux';
import {
Platform,
TouchableWithoutFeedback,
View,
Text,
findNodeHandle,
} from 'react-native';
import {KeyboardAwareScrollView} from 'react-native-keyboard-aware-scroll-view';
dismissModal,
popTopScreen,
} from 'app/actions/navigation';
import ErrorText from 'app/components/error_text';
import FormattedText from 'app/components/formatted_text';
import Loading from 'app/components/loading';
import StatusBar from 'app/components/status_bar';
import TextInputWithLocalizedPlaceholder from 'app/components/text_input_with_localized_placeholder';
import EditChannelInfo from './edit_channel_info';
import {changeOpacity, makeStyleSheetFromTheme} from 'app/utils/theme';
import {General} from 'mattermost-redux/constants';
import {getShortenedURL} from 'app/utils/url';
import {t} from 'app/utils/i18n';
export default class EditChannelInfo extends PureComponent {
static propTypes = {
navigator: PropTypes.object.isRequired,
theme: PropTypes.object.isRequired,
deviceWidth: PropTypes.number.isRequired,
deviceHeight: PropTypes.number.isRequired,
channelType: PropTypes.string,
enableRightButton: PropTypes.func,
saving: PropTypes.bool.isRequired,
editing: PropTypes.bool,
error: PropTypes.oneOfType([PropTypes.string, PropTypes.object]),
displayName: PropTypes.string,
currentTeamUrl: PropTypes.string,
channelURL: PropTypes.string,
purpose: PropTypes.string,
header: PropTypes.string,
onDisplayNameChange: PropTypes.func,
onChannelURLChange: PropTypes.func,
onPurposeChange: PropTypes.func,
onHeaderChange: PropTypes.func,
oldDisplayName: PropTypes.string,
oldChannelURL: PropTypes.string,
oldHeader: PropTypes.string,
oldPurpose: PropTypes.string,
function mapDispatchToProps(dispatch) {
return {
actions: bindActionCreators({
dismissModal,
popTopScreen,
}, dispatch),
};
static defaultProps = {
editing: false,
};
constructor(props) {
super(props);
this.nameInput = React.createRef();
this.urlInput = React.createRef();
this.purposeInput = React.createRef();
this.headerInput = React.createRef();
this.lastText = React.createRef();
this.scroll = React.createRef();
}
blur = () => {
if (this.nameInput?.current) {
this.nameInput.current.blur();
}
// TODO: uncomment below once the channel URL field is added
// if (this.urlInput?.current) {
// this.urlInput.current.blur();
// }
if (this.purposeInput?.current) {
this.purposeInput.current.blur();
}
if (this.headerInput?.current) {
this.headerInput.current.blur();
}
if (this.scroll?.current) {
this.scroll.current.scrollToPosition(0, 0, true);
}
};
close = (goBack = false) => {
if (goBack) {
this.props.navigator.pop({animated: true});
} else {
this.props.navigator.dismissModal({
animationType: 'slide-down',
});
}
};
canUpdate = (displayName, channelURL, purpose, header) => {
const {
oldDisplayName,
oldChannelURL,
oldPurpose,
oldHeader,
} = this.props;
return displayName !== oldDisplayName || channelURL !== oldChannelURL ||
purpose !== oldPurpose || header !== oldHeader;
};
enableRightButton = (enable = false) => {
this.props.enableRightButton(enable);
};
onDisplayNameChangeText = (displayName) => {
const {editing, onDisplayNameChange} = this.props;
onDisplayNameChange(displayName);
if (editing) {
const {channelURL, purpose, header} = this.props;
const canUpdate = this.canUpdate(displayName, channelURL, purpose, header);
this.enableRightButton(canUpdate);
return;
}
const displayNameExists = displayName && displayName.length >= 2;
this.props.enableRightButton(displayNameExists);
};
onDisplayURLChangeText = (channelURL) => {
const {editing, onChannelURLChange} = this.props;
onChannelURLChange(channelURL);
if (editing) {
const {displayName, purpose, header} = this.props;
const canUpdate = this.canUpdate(displayName, channelURL, purpose, header);
this.enableRightButton(canUpdate);
}
};
onPurposeChangeText = (purpose) => {
const {editing, onPurposeChange} = this.props;
onPurposeChange(purpose);
if (editing) {
const {displayName, channelURL, header} = this.props;
const canUpdate = this.canUpdate(displayName, channelURL, purpose, header);
this.enableRightButton(canUpdate);
}
};
onHeaderChangeText = (header) => {
const {editing, onHeaderChange} = this.props;
onHeaderChange(header);
if (editing) {
const {displayName, channelURL, purpose} = this.props;
const canUpdate = this.canUpdate(displayName, channelURL, purpose, header);
this.enableRightButton(canUpdate);
}
};
scrollToEnd = () => {
if (this.scroll?.current && this.lastText?.current) {
this.scroll.current.scrollToFocusedInput(findNodeHandle(this.lastText.current));
}
};
render() {
const {
theme,
editing,
channelType,
currentTeamUrl,
deviceWidth,
deviceHeight,
displayName,
channelURL,
header,
purpose,
} = this.props;
const {error, saving} = this.props;
const fullUrl = currentTeamUrl + '/channels';
const shortUrl = getShortenedURL(fullUrl, 35);
const style = getStyleSheet(theme);
const displayHeaderOnly = channelType === General.DM_CHANNEL ||
channelType === General.GM_CHANNEL;
if (saving) {
return (
<View style={style.container}>
<StatusBar/>
<Loading/>
</View>
);
}
let displayError;
if (error) {
displayError = (
<View style={[style.errorContainer, {width: deviceWidth}]}>
<View style={style.errorWrapper}>
<ErrorText error={error}/>
</View>
</View>
);
}
return (
<View style={style.container}>
<StatusBar/>
<KeyboardAwareScrollView
ref={this.scroll}
style={style.container}
>
{displayError}
<TouchableWithoutFeedback onPress={this.blur}>
<View style={[style.scrollView, {height: deviceHeight + (Platform.OS === 'android' ? 200 : 0)}]}>
{!displayHeaderOnly && (
<View>
<View>
<FormattedText
style={style.title}
id='channel_modal.name'
defaultMessage='Name'
/>
</View>
<View style={style.inputContainer}>
<TextInputWithLocalizedPlaceholder
ref={this.nameInput}
value={displayName}
onChangeText={this.onDisplayNameChangeText}
style={style.input}
autoCapitalize='none'
autoCorrect={false}
placeholder={{id: t('channel_modal.nameEx'), defaultMessage: 'E.g.: "Bugs", "Marketing", "客户支持"'}}
placeholderTextColor={changeOpacity('#000', 0.5)}
underlineColorAndroid='transparent'
disableFullscreenUI={true}
/>
</View>
</View>
)}
{/*TODO: Hide channel url field until it's added to CreateChannel */}
{false && editing && !displayHeaderOnly && (
<View>
<View style={style.titleContainer30}>
<FormattedText
style={style.title}
id='rename_channel.url'
defaultMessage='URL'
/>
<Text style={style.optional}>
{shortUrl}
</Text>
</View>
<View style={style.inputContainer}>
<TextInputWithLocalizedPlaceholder
ref={this.urlInput}
value={channelURL}
onChangeText={this.onDisplayURLChangeText}
style={style.input}
autoCapitalize='none'
autoCorrect={false}
placeholder={{id: t('rename_channel.handleHolder'), defaultMessage: 'lowercase alphanumeric characters'}}
placeholderTextColor={changeOpacity('#000', 0.5)}
underlineColorAndroid='transparent'
disableFullscreenUI={true}
/>
</View>
</View>
)}
{!displayHeaderOnly && (
<View>
<View style={style.titleContainer30}>
<FormattedText
style={style.title}
id='channel_modal.purpose'
defaultMessage='Purpose'
/>
<FormattedText
style={style.optional}
id='channel_modal.optional'
defaultMessage='(optional)'
/>
</View>
<View style={style.inputContainer}>
<TextInputWithLocalizedPlaceholder
ref={this.purposeInput}
value={purpose}
onChangeText={this.onPurposeChangeText}
style={[style.input, {height: 110}]}
autoCapitalize='none'
autoCorrect={false}
placeholder={{id: t('channel_modal.purposeEx'), defaultMessage: 'E.g.: "A channel to file bugs and improvements"'}}
placeholderTextColor={changeOpacity('#000', 0.5)}
multiline={true}
blurOnSubmit={false}
textAlignVertical='top'
underlineColorAndroid='transparent'
disableFullscreenUI={true}
/>
</View>
<View>
<FormattedText
style={style.helpText}
id='channel_modal.descriptionHelp'
defaultMessage='Describe how this channel should be used.'
/>
</View>
</View>
)}
<View style={style.titleContainer15}>
<FormattedText
style={style.title}
id='channel_modal.header'
defaultMessage='Header'
/>
<FormattedText
style={style.optional}
id='channel_modal.optional'
defaultMessage='(optional)'
/>
</View>
<View style={style.inputContainer}>
<TextInputWithLocalizedPlaceholder
ref={this.headerInput}
value={header}
onChangeText={this.onHeaderChangeText}
style={[style.input, {height: 110}]}
autoCapitalize='none'
autoCorrect={false}
placeholder={{id: t('channel_modal.headerEx'), defaultMessage: 'E.g.: "[Link Title](http://example.com)"'}}
placeholderTextColor={changeOpacity('#000', 0.5)}
multiline={true}
blurOnSubmit={false}
onFocus={this.scrollToEnd}
textAlignVertical='top'
underlineColorAndroid='transparent'
disableFullscreenUI={true}
/>
</View>
<View ref={this.lastText}>
<FormattedText
style={style.helpText}
id='channel_modal.headerHelp'
defaultMessage={'Set text that will appear in the header of the channel beside the channel name. For example, include frequently used links by typing [Link Title](http://example.com).'}
/>
</View>
</View>
</TouchableWithoutFeedback>
</KeyboardAwareScrollView>
</View>
);
}
}
const getStyleSheet = makeStyleSheetFromTheme((theme) => {
return {
container: {
flex: 1,
backgroundColor: theme.centerChannelBg,
},
scrollView: {
flex: 1,
backgroundColor: changeOpacity(theme.centerChannelColor, 0.03),
paddingTop: 10,
},
errorContainer: {
backgroundColor: changeOpacity(theme.centerChannelColor, 0.03),
},
errorWrapper: {
justifyContent: 'center',
alignItems: 'center',
},
inputContainer: {
marginTop: 10,
backgroundColor: '#fff',
},
input: {
color: '#333',
fontSize: 14,
height: 40,
paddingHorizontal: 15,
},
titleContainer30: {
flexDirection: 'row',
marginTop: 30,
},
titleContainer15: {
flexDirection: 'row',
marginTop: 15,
},
title: {
fontSize: 14,
color: theme.centerChannelColor,
marginLeft: 15,
},
optional: {
color: changeOpacity(theme.centerChannelColor, 0.5),
fontSize: 14,
marginLeft: 5,
},
helpText: {
fontSize: 14,
color: changeOpacity(theme.centerChannelColor, 0.5),
marginTop: 10,
marginHorizontal: 15,
},
};
});
export default connect(null, mapDispatchToProps)(EditChannelInfo);

View file

@ -1,8 +1,11 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {bindActionCreators} from 'redux';
import {connect} from 'react-redux';
import {showModal} from 'app/actions/navigation';
import InteractiveDialogController from './interactive_dialog_controller';
function mapStateToProps(state) {
@ -12,4 +15,12 @@ function mapStateToProps(state) {
};
}
export default connect(mapStateToProps)(InteractiveDialogController);
function mapDispatchToProps(dispatch) {
return {
actions: bindActionCreators({
showModal,
}, dispatch),
};
}
export default connect(mapStateToProps, mapDispatchToProps)(InteractiveDialogController);

View file

@ -7,9 +7,11 @@ import MaterialIcon from 'react-native-vector-icons/MaterialIcons';
export default class InteractiveDialogController extends PureComponent {
static propTypes = {
actions: PropTypes.shape({
showModal: PropTypes.func.isRequired,
}).isRequired,
triggerId: PropTypes.string,
dialog: PropTypes.object,
navigator: PropTypes.object,
theme: PropTypes.object,
};
@ -22,7 +24,7 @@ export default class InteractiveDialogController extends PureComponent {
}
componentDidUpdate(prevProps) {
const triggerId = this.props.triggerId;
const {actions, triggerId} = this.props;
if (!triggerId) {
return;
}
@ -41,33 +43,24 @@ export default class InteractiveDialogController extends PureComponent {
return;
}
const theme = this.props.theme;
this.props.navigator.showModal({
backButtonTitle: '',
screen: 'InteractiveDialog',
title: dialogData.dialog.title,
animated: true,
navigatorStyle: {
navBarTextColor: theme.sidebarHeaderTextColor,
navBarBackgroundColor: theme.sidebarHeaderBg,
navBarButtonColor: theme.sidebarHeaderTextColor,
screenBackgroundColor: theme.centerChannelBg,
},
navigatorButtons: {
const screen = 'InteractiveDialog';
const title = dialogData.dialog.title;
const passProps = {};
const options = {
topBar: {
leftButtons: [{
id: 'close-dialog',
icon: this.closeButton,
}],
rightButtons: [
{
id: 'submit-dialog',
showAsAction: 'always',
title: dialogData.dialog.submit_label,
},
],
rightButtons: [{
id: 'submit-dialog',
showAsAction: 'always',
text: dialogData.dialog.submit_label,
}],
},
});
};
actions.showModal(screen, title, passProps, options);
}
render() {

View file

@ -9,6 +9,7 @@ import {getConfig, getCurrentUrl} from 'mattermost-redux/selectors/entities/gene
import {getTheme} from 'mattermost-redux/selectors/entities/preferences';
import {makePreparePostIdsForPostList, START_OF_NEW_MESSAGES} from 'mattermost-redux/utils/post_list';
import {showModalOverCurrentContext} from 'app/actions/navigation';
import {handleSelectChannelByName, loadChannelsByTeamName, refreshChannelWithRetry} from 'app/actions/views/channel';
import {setDeepLinkURL} from 'app/actions/views/root';
@ -42,6 +43,7 @@ function mapDispatchToProps(dispatch) {
refreshChannelWithRetry,
selectFocusedPostId,
setDeepLinkURL,
showModalOverCurrentContext,
}, dispatch),
};
}

View file

@ -41,6 +41,7 @@ export default class PostList extends PureComponent {
refreshChannelWithRetry: PropTypes.func.isRequired,
selectFocusedPostId: PropTypes.func.isRequired,
setDeepLinkURL: PropTypes.func.isRequired,
showModalOverCurrentContext: PropTypes.func.isRequired,
}).isRequired,
channelId: PropTypes.string,
deepLinkURL: PropTypes.string,
@ -51,7 +52,6 @@ export default class PostList extends PureComponent {
isSearchResult: PropTypes.bool,
lastPostIndex: PropTypes.number.isRequired,
lastViewedAt: PropTypes.number, // Used by container // eslint-disable-line no-unused-prop-types
navigator: PropTypes.object,
onLoadMoreUp: PropTypes.func,
onHashtagPress: PropTypes.func,
onPermalinkPress: PropTypes.func,
@ -250,7 +250,6 @@ export default class PostList extends PureComponent {
isSearchResult: this.props.isSearchResult,
location: this.props.location,
managedConfig: mattermostManaged.getCachedConfig(),
navigator: this.props.navigator,
onHashtagPress: this.props.onHashtagPress,
onPermalinkPress: this.handlePermalinkPress,
onPress: this.props.onPostPress,
@ -303,29 +302,24 @@ export default class PostList extends PureComponent {
};
showPermalinkView = (postId) => {
const {actions, navigator} = this.props;
const {actions} = this.props;
actions.selectFocusedPostId(postId);
if (!this.showingPermalink) {
const screen = 'Permalink';
const passProps = {
isPermalink: true,
onClose: this.handleClosePermalink,
};
const options = {
screen: 'Permalink',
animationType: 'none',
backButtonTitle: '',
overrideBackPress: true,
navigatorStyle: {
navBarHidden: true,
screenBackgroundColor: changeOpacity('#000', 0.2),
modalPresentationStyle: 'overCurrentContext',
},
passProps: {
isPermalink: true,
onClose: this.handleClosePermalink,
layout: {
backgroundColor: changeOpacity('#000', 0.2),
},
};
this.showingPermalink = true;
navigator.showModal(options);
actions.showModalOverCurrentContext(screen, passProps, options);
}
};

View file

@ -18,11 +18,9 @@ describe('PostList', () => {
refreshChannelWithRetry: jest.fn(),
selectFocusedPostId: jest.fn(),
setDeepLinkURL: jest.fn(),
showModalOverCurrentContext: jest.fn(),
},
deepLinkURL: '',
navigator: {
showModal: jest.fn(),
},
lastPostIndex: -1,
postIds: ['post-id-1', 'post-id-2'],
serverURL,
@ -47,7 +45,7 @@ describe('PostList', () => {
wrapper.setProps({deepLinkURL: deepLinks.permalink});
expect(baseProps.actions.setDeepLinkURL).toHaveBeenCalled();
expect(baseProps.actions.selectFocusedPostId).toHaveBeenCalled();
expect(baseProps.navigator.showModal).toHaveBeenCalled();
expect(baseProps.actions.showModalOverCurrentContext).toHaveBeenCalled();
expect(wrapper.getElement()).toMatchSnapshot();
});

View file

@ -6,12 +6,12 @@ import PropTypes from 'prop-types';
import {intlShape} from 'react-intl';
import {
Keyboard,
Platform,
StyleSheet,
View,
} from 'react-native';
import {Navigation} from 'react-native-navigation';
import MaterialIcon from 'react-native-vector-icons/MaterialIcons';
import EventEmitter from 'mattermost-redux/utils/event_emitter';
import {app} from 'app/mattermost';
@ -40,12 +40,14 @@ export default class ChannelBase extends PureComponent {
selectInitialChannel: PropTypes.func.isRequired,
recordLoadTime: PropTypes.func.isRequired,
peek: PropTypes.func.isRequired,
goToScreen: PropTypes.func.isRequired,
showModalOverCurrentContext: PropTypes.func.isRequired,
}).isRequired,
componentId: PropTypes.string.isRequired,
currentChannelId: PropTypes.string,
channelsRequestFailed: PropTypes.bool,
currentTeamId: PropTypes.string,
isLandscape: PropTypes.bool,
navigator: PropTypes.object,
theme: PropTypes.object.isRequired,
showTermsOfService: PropTypes.bool,
disableTermsModal: PropTypes.bool,
@ -65,8 +67,10 @@ export default class ChannelBase extends PureComponent {
this.postTextbox = React.createRef();
this.keyboardTracker = React.createRef();
props.navigator.setStyle({
screenBackgroundColor: props.theme.centerChannelBg,
Navigation.mergeOptions(props.componentId, {
layout: {
backgroundColor: props.theme.centerChannelBg,
},
});
if (LocalConfig.EnableMobileClientUpgrade && !ClientUpgradeListener) {
@ -104,8 +108,10 @@ export default class ChannelBase extends PureComponent {
componentWillReceiveProps(nextProps) {
if (this.props.theme !== nextProps.theme) {
this.props.navigator.setStyle({
screenBackgroundColor: nextProps.theme.centerChannelBg,
Navigation.mergeOptions(this.props.componentId, {
layout: {
backgroundColor: nextProps.theme.centerChannelBg,
},
});
}
@ -161,53 +167,32 @@ export default class ChannelBase extends PureComponent {
};
showTermsOfServiceModal = async () => {
const {navigator, theme} = this.props;
const {actions, theme} = this.props;
const closeButton = await MaterialIcon.getImageSource('close', 20, theme.sidebarHeaderTextColor);
navigator.showModal({
screen: 'TermsOfService',
animationType: 'slide-up',
title: '',
backButtonTitle: '',
animated: true,
navigatorStyle: {
navBarTextColor: theme.centerChannelColor,
navBarBackgroundColor: theme.centerChannelBg,
navBarButtonColor: theme.buttonBg,
screenBackgroundColor: theme.centerChannelBg,
modalPresentationStyle: 'overCurrentContext',
},
overrideBackPress: true,
passProps: {
closeButton,
},
});
};
goToChannelInfo = preventDoubleTap(() => {
const {intl} = this.context;
const {navigator, theme} = this.props;
const screen = 'TermsOfService';
const passProps = {
closeButton,
};
const options = {
screen: 'ChannelInfo',
title: intl.formatMessage({id: 'mobile.routes.channelInfo', defaultMessage: 'Info'}),
animated: true,
backButtonTitle: '',
navigatorStyle: {
navBarTextColor: theme.sidebarHeaderTextColor,
navBarBackgroundColor: theme.sidebarHeaderBg,
navBarButtonColor: theme.sidebarHeaderTextColor,
screenBackgroundColor: theme.centerChannelBg,
layout: {
backgroundColor: theme.centerChannelBg,
},
};
actions.showModalOverCurrentContext(screen, passProps, options);
};
goToChannelInfo = preventDoubleTap(() => {
const {actions} = this.props;
const {intl} = this.context;
const screen = 'ChannelInfo';
const title = intl.formatMessage({id: 'mobile.routes.channelInfo', defaultMessage: 'Info'});
Keyboard.dismiss();
if (Platform.OS === 'android') {
navigator.showModal(options);
} else {
requestAnimationFrame(() => {
navigator.push(options);
});
}
requestAnimationFrame(() => {
actions.goToScreen(screen, title);
});
});
handleAutoComplete = (value) => {
@ -265,7 +250,6 @@ export default class ChannelBase extends PureComponent {
channelsRequestFailed,
currentChannelId,
isLandscape,
navigator,
theme,
} = this.props;
@ -282,7 +266,7 @@ export default class ChannelBase extends PureComponent {
const Loading = require('app/components/channel_loader').default;
return (
<SafeAreaView navigator={navigator}>
<SafeAreaView>
<View style={style.flex}>
<EmptyToolbar
theme={theme}
@ -298,18 +282,15 @@ export default class ChannelBase extends PureComponent {
<MainSidebar
ref={this.channelSidebarRef}
blurPostTextBox={this.blurPostTextBox}
navigator={navigator}
{...optionalProps}
>
<SettingsSidebar
ref={this.settingsSidebarRef}
blurPostTextBox={this.blurPostTextBox}
navigator={navigator}
>
{drawerContent}
</SettingsSidebar>
<InteractiveDialogController
navigator={navigator}
theme={theme}
/>
</MainSidebar>

View file

@ -24,7 +24,6 @@ const {
export default class ChannelNavBar extends PureComponent {
static propTypes = {
isLandscape: PropTypes.bool.isRequired,
navigator: PropTypes.object.isRequired,
openChannelDrawer: PropTypes.func.isRequired,
openSettingsDrawer: PropTypes.func.isRequired,
onPress: PropTypes.func.isRequired,
@ -32,7 +31,7 @@ export default class ChannelNavBar extends PureComponent {
};
render() {
const {isLandscape, navigator, onPress, theme} = this.props;
const {isLandscape, onPress, theme} = this.props;
const {openChannelDrawer, openSettingsDrawer} = this.props;
const style = getStyleFromTheme(theme);
const padding = {paddingHorizontal: 0};
@ -72,7 +71,6 @@ export default class ChannelNavBar extends PureComponent {
/>
<ChannelTitle onPress={onPress}/>
<ChannelSearchButton
navigator={navigator}
theme={theme}
/>
<SettingDrawerButton openDrawer={openSettingsDrawer}/>

View file

@ -19,7 +19,6 @@ export default class ChannelSearchButton extends PureComponent {
clearSearch: PropTypes.func.isRequired,
showSearchModal: PropTypes.func.isRequired,
}).isRequired,
navigator: PropTypes.object,
theme: PropTypes.object,
};

View file

@ -33,13 +33,14 @@ export default class ChannelPostList extends PureComponent {
selectPost: PropTypes.func.isRequired,
recordLoadTime: PropTypes.func.isRequired,
refreshChannelWithRetry: PropTypes.func.isRequired,
showModal: PropTypes.func.isRequired,
goToScreen: PropTypes.func.isRequired,
}).isRequired,
channelId: PropTypes.string.isRequired,
channelRefreshingFailed: PropTypes.bool,
currentUserId: PropTypes.string,
lastViewedAt: PropTypes.number,
loadMorePostsVisible: PropTypes.bool.isRequired,
navigator: PropTypes.object,
postIds: PropTypes.array,
postVisibility: PropTypes.number,
refreshing: PropTypes.bool.isRequired,
@ -105,36 +106,23 @@ export default class ChannelPostList extends PureComponent {
goToThread = (post) => {
telemetry.start(['post_list:thread']);
const {actions, channelId, navigator, theme} = this.props;
const {actions, channelId} = this.props;
const rootId = (post.root_id || post.id);
Keyboard.dismiss();
actions.loadThreadIfNecessary(rootId);
actions.selectPost(rootId);
const options = {
screen: 'Thread',
animated: true,
backButtonTitle: '',
navigatorStyle: {
navBarTextColor: theme.sidebarHeaderTextColor,
navBarBackgroundColor: theme.sidebarHeaderBg,
navBarButtonColor: theme.sidebarHeaderTextColor,
screenBackgroundColor: theme.centerChannelBg,
},
passProps: {
channelId,
rootId,
},
const screen = 'Thread';
const title = '';
const passProps = {
channelId,
rootId,
};
if (Platform.OS === 'android') {
navigator.showModal(options);
} else {
requestAnimationFrame(() => {
navigator.push(options);
});
}
requestAnimationFrame(() => {
actions.goToScreen(screen, title, passProps);
});
};
loadMorePostsTop = () => {
@ -181,7 +169,6 @@ export default class ChannelPostList extends PureComponent {
return (
<ChannelIntro
channelId={this.props.channelId}
navigator={this.props.navigator}
/>
);
};
@ -194,7 +181,6 @@ export default class ChannelPostList extends PureComponent {
currentUserId,
lastViewedAt,
loadMorePostsVisible,
navigator,
refreshing,
theme,
} = this.props;
@ -223,7 +209,6 @@ export default class ChannelPostList extends PureComponent {
currentUserId={currentUserId}
lastViewedAt={lastViewedAt}
channelId={channelId}
navigator={navigator}
renderFooter={this.renderFooter}
refreshing={refreshing}
scrollViewNativeID={channelId}
@ -234,7 +219,7 @@ export default class ChannelPostList extends PureComponent {
return (
<View style={style.container}>
{component}
<AnnouncementBanner navigator={navigator}/>
<AnnouncementBanner/>
<RetryBarIndicator/>
</View>
);

View file

@ -10,6 +10,7 @@ import {getCurrentChannelId} from 'mattermost-redux/selectors/entities/channels'
import {getCurrentUserId} from 'mattermost-redux/selectors/entities/users';
import {getTheme} from 'mattermost-redux/selectors/entities/preferences';
import {goToScreen} from 'app/actions/navigation';
import {loadPostsIfNecessaryWithRetry, loadThreadIfNecessary, increasePostVisibility, refreshChannelWithRetry} from 'app/actions/views/channel';
import {recordLoadTime} from 'app/actions/views/root';
@ -42,6 +43,7 @@ function mapDispatchToProps(dispatch) {
selectPost,
recordLoadTime,
refreshChannelWithRetry,
goToScreen,
}, dispatch),
};
}

View file

@ -19,7 +19,7 @@ import {
import {connection} from 'app/actions/device';
import {recordLoadTime} from 'app/actions/views/root';
import {selectDefaultTeam} from 'app/actions/views/select_team';
import {peek} from 'app/actions/navigation';
import {peek, goToScreen, showModalOverCurrentContext} from 'app/actions/navigation';
import {isLandscape} from 'app/selectors/device';
import Channel from './channel';
@ -50,6 +50,8 @@ function mapDispatchToProps(dispatch) {
startPeriodicStatusUpdates,
stopPeriodicStatusUpdates,
peek,
goToScreen,
showModalOverCurrentContext,
}, dispatch),
};
}

View file

@ -33,6 +33,8 @@ export default class ChannelAddMembers extends PureComponent {
getProfilesNotInChannel: PropTypes.func.isRequired,
handleAddChannelMembers: PropTypes.func.isRequired,
searchProfiles: PropTypes.func.isRequired,
setButtons: PropTypes.func.isRequired,
popTopScreen: PropTypes.func.isRequired,
}).isRequired,
componentId: PropTypes.string,
currentChannelId: PropTypes.string.isRequired,
@ -40,7 +42,6 @@ export default class ChannelAddMembers extends PureComponent {
currentTeamId: PropTypes.string.isRequired,
currentUserId: PropTypes.string.isRequired,
profilesNotInChannel: PropTypes.array.isRequired,
navigator: PropTypes.object,
theme: PropTypes.object.isRequired,
};
@ -68,13 +69,13 @@ export default class ChannelAddMembers extends PureComponent {
};
this.addButton = {
disabled: true,
enalbed: false,
id: 'add-members',
title: context.intl.formatMessage({id: 'integrations.add', defaultMessage: 'Add'}),
text: context.intl.formatMessage({id: 'integrations.add', defaultMessage: 'Add'}),
showAsAction: 'always',
};
props.navigator.setButtons({
props.actions.setButtons(props.componentId, {
rightButtons: [this.addButton],
});
}
@ -113,12 +114,13 @@ export default class ChannelAddMembers extends PureComponent {
};
close = () => {
this.props.navigator.pop({animated: true});
this.props.actions.popTopScreen();
};
enableAddOption = (enabled) => {
this.props.navigator.setButtons({
rightButtons: [{...this.addButton, disabled: !enabled}],
const {actions, componentId} = this.props;
actions.setButtons(componentId, {
rightButtons: [{...this.addButton, enabled}],
});
};

View file

@ -16,15 +16,13 @@ describe('ChannelAddMembers', () => {
getProfilesNotInChannel: jest.fn().mockResolvedValue({}),
handleAddChannelMembers: jest.fn().mockResolvedValue({}),
searchProfiles: jest.fn().mockResolvedValue({data: []}),
setButtons: jest.fn(),
popTopScreen: jest.fn(),
},
currentChannelId: 'current_channel_id',
currentTeamId: 'current_team_id',
currentUserId: 'current_user_id',
profilesNotInChannel: [],
navigator: {
pop: jest.fn(),
setButtons: jest.fn(),
},
theme: Preferences.THEMES.default,
componentId: 'component-id',
};
@ -35,10 +33,10 @@ describe('ChannelAddMembers', () => {
expect(baseProps.actions.getTeamStats).toBeCalledTimes(1);
expect(baseProps.actions.getTeamStats).toBeCalledWith(baseProps.currentTeamId);
const button = {disabled: true, id: 'add-members', title: 'Add', showAsAction: 'always'};
expect(baseProps.navigator.setButtons).toBeCalledTimes(2);
expect(baseProps.navigator.setButtons.mock.calls[0][0]).toEqual({rightButtons: [button]});
expect(baseProps.navigator.setButtons.mock.calls[1][0]).toEqual({rightButtons: [button]});
const button = {enabled: false, id: 'add-members', text: 'Add', showAsAction: 'always'};
expect(baseProps.actions.setButtons).toBeCalledTimes(2);
expect(baseProps.actions.setButtons.mock.calls[0][0]).toEqual(baseProps.componentId, {rightButtons: [button]});
expect(baseProps.actions.setButtons.mock.calls[1][0]).toEqual(baseProps.componentId, {rightButtons: [button]});
});
test('should match state on clearSearch', () => {
@ -50,12 +48,11 @@ describe('ChannelAddMembers', () => {
wrapper.setState({term: '', searchResults: []});
});
test('should call props.navigator on close', () => {
test('should call props.popTopScreen on close', () => {
const wrapper = shallowWithIntl(<ChannelAddMembers {...baseProps}/>);
wrapper.instance().close();
expect(baseProps.navigator.pop).toBeCalledTimes(1);
expect(baseProps.navigator.pop).toBeCalledWith({animated: true});
expect(baseProps.actions.popTopScreen).toBeCalledTimes(1);
});
test('should match state on onProfilesLoaded', () => {

View file

@ -11,6 +11,7 @@ import {getTheme} from 'mattermost-redux/selectors/entities/preferences';
import {getCurrentTeamId} from 'mattermost-redux/selectors/entities/teams';
import {getCurrentUserId, getProfilesNotInCurrentChannel} from 'mattermost-redux/selectors/entities/users';
import {setButtons, popTopScreen} from 'app/actions/navigation';
import {handleAddChannelMembers} from 'app/actions/views/channel_add_members';
import ChannelAddMembers from './channel_add_members';
@ -35,6 +36,8 @@ function mapDispatchToProps(dispatch) {
getProfilesNotInChannel,
handleAddChannelMembers,
searchProfiles,
setButtons,
popTopScreen,
}, dispatch),
};
}

View file

@ -42,6 +42,10 @@ export default class ChannelInfo extends PureComponent {
selectPenultimateChannel: PropTypes.func.isRequired,
handleSelectChannel: PropTypes.func.isRequired,
setChannelDisplayName: PropTypes.func.isRequired,
popTopScreen: PropTypes.func.isRequired,
goToScreen: PropTypes.func.isRequired,
dismissModal: PropTypes.func.isRequired,
showModalOverCurrentContext: PropTypes.func.isRequired,
}),
componentId: PropTypes.string,
viewArchivedChannels: PropTypes.bool.isRequired,
@ -50,7 +54,6 @@ export default class ChannelInfo extends PureComponent {
currentChannelCreatorName: PropTypes.string,
currentChannelMemberCount: PropTypes.number,
currentUserId: PropTypes.string,
navigator: PropTypes.object,
status: PropTypes.string,
theme: PropTypes.object.isRequired,
isChannelMuted: PropTypes.bool.isRequired,
@ -105,95 +108,61 @@ export default class ChannelInfo extends PureComponent {
}
close = (redirect = true) => {
const {actions} = this.props;
if (redirect) {
this.props.actions.setChannelDisplayName('');
actions.setChannelDisplayName('');
}
if (Platform.OS === 'android') {
this.props.navigator.dismissModal({animated: true});
actions.dismissModal();
} else {
this.props.navigator.pop({animated: true});
actions.popTopScreen();
}
};
goToChannelAddMembers = preventDoubleTap(() => {
const {actions} = this.props;
const {intl} = this.context;
const {navigator, theme} = this.props;
navigator.push({
backButtonTitle: '',
screen: 'ChannelAddMembers',
title: intl.formatMessage({id: 'channel_header.addMembers', defaultMessage: 'Add Members'}),
animated: true,
navigatorStyle: {
navBarTextColor: theme.sidebarHeaderTextColor,
navBarBackgroundColor: theme.sidebarHeaderBg,
navBarButtonColor: theme.sidebarHeaderTextColor,
screenBackgroundColor: theme.centerChannelBg,
},
});
const screen = 'ChannelAddMembers';
const title = intl.formatMessage({id: 'channel_header.addMembers', defaultMessage: 'Add Members'});
actions.goToScreen(screen, title);
});
goToChannelMembers = preventDoubleTap(() => {
const {actions, canManageUsers} = this.props;
const {intl} = this.context;
const {canManageUsers, navigator, theme} = this.props;
const id = canManageUsers ? t('channel_header.manageMembers') : t('channel_header.viewMembers');
const defaultMessage = canManageUsers ? 'Manage Members' : 'View Members';
const screen = 'ChannelMembers';
const title = intl.formatMessage({id, defaultMessage});
navigator.push({
backButtonTitle: '',
screen: 'ChannelMembers',
title: intl.formatMessage({id, defaultMessage}),
animated: true,
navigatorStyle: {
navBarTextColor: theme.sidebarHeaderTextColor,
navBarBackgroundColor: theme.sidebarHeaderBg,
navBarButtonColor: theme.sidebarHeaderTextColor,
screenBackgroundColor: theme.centerChannelBg,
},
});
actions.goToScreen(screen, title);
});
goToPinnedPosts = preventDoubleTap(() => {
const {actions, currentChannel} = this.props;
const {formatMessage} = this.context.intl;
const {actions, currentChannel, navigator, theme} = this.props;
const id = t('channel_header.pinnedPosts');
const defaultMessage = 'Pinned Posts';
const screen = 'PinnedPosts';
const title = formatMessage({id, defaultMessage});
const passProps = {
currentChannelId: currentChannel.id,
};
actions.clearPinnedPosts(currentChannel.id);
navigator.push({
backButtonTitle: '',
screen: 'PinnedPosts',
title: formatMessage({id, defaultMessage}),
animated: true,
navigatorStyle: {
navBarTextColor: theme.sidebarHeaderTextColor,
navBarBackgroundColor: theme.sidebarHeaderBg,
navBarButtonColor: theme.sidebarHeaderTextColor,
screenBackgroundColor: theme.centerChannelBg,
},
passProps: {
currentChannelId: currentChannel.id,
},
});
actions.goToScreen(screen, title, passProps);
});
handleChannelEdit = preventDoubleTap(() => {
const {actions} = this.props;
const {intl} = this.context;
const {navigator, theme} = this.props;
const id = t('mobile.channel_info.edit');
const defaultMessage = 'Edit Channel';
const screen = 'EditChannel';
const title = intl.formatMessage({id, defaultMessage});
navigator.push({
backButtonTitle: '',
screen: 'EditChannel',
title: intl.formatMessage({id, defaultMessage}),
animated: true,
navigatorStyle: {
navBarTextColor: theme.sidebarHeaderTextColor,
navBarBackgroundColor: theme.sidebarHeaderBg,
navBarButtonColor: theme.sidebarHeaderTextColor,
screenBackgroundColor: theme.centerChannelBg,
},
});
actions.goToScreen(screen, title);
});
handleLeave = () => {
@ -337,30 +306,22 @@ export default class ChannelInfo extends PureComponent {
});
showPermalinkView = (postId) => {
const {actions, navigator} = this.props;
const {actions} = this.props;
const screen = 'Permalink';
const passProps = {
isPermalink: true,
onClose: this.handleClosePermalink,
};
const options = {
layout: {
backgroundColor: changeOpacity('#000', 0.2),
},
};
actions.selectFocusedPostId(postId);
if (!this.showingPermalink) {
const options = {
screen: 'Permalink',
animationType: 'none',
backButtonTitle: '',
overrideBackPress: true,
navigatorStyle: {
navBarHidden: true,
screenBackgroundColor: changeOpacity('#000', 0.2),
modalPresentationStyle: 'overCurrentContext',
},
passProps: {
isPermalink: true,
onClose: this.handleClosePermalink,
},
};
this.showingPermalink = true;
navigator.showModal(options);
}
this.showingPermalink = true;
actions.showModalOverCurrentContext(screen, passProps, options);
};
renderViewOrManageMembersRow = () => {
@ -510,7 +471,6 @@ export default class ChannelInfo extends PureComponent {
currentChannel,
currentChannelCreatorName,
currentChannelMemberCount,
navigator,
status,
theme,
isBot,
@ -545,7 +505,6 @@ export default class ChannelInfo extends PureComponent {
displayName={currentChannel.display_name}
header={currentChannel.header}
memberCount={currentChannelMemberCount}
navigator={navigator}
onPermalinkPress={this.handlePermalinkPress}
purpose={currentChannel.purpose}
status={status}

View file

@ -23,7 +23,6 @@ export default class ChannelInfoHeader extends React.PureComponent {
memberCount: PropTypes.number,
displayName: PropTypes.string.isRequired,
header: PropTypes.string,
navigator: PropTypes.object.isRequired,
onPermalinkPress: PropTypes.func,
purpose: PropTypes.string,
status: PropTypes.string,
@ -41,7 +40,6 @@ export default class ChannelInfoHeader extends React.PureComponent {
displayName,
header,
memberCount,
navigator,
onPermalinkPress,
purpose,
status,
@ -88,7 +86,6 @@ export default class ChannelInfoHeader extends React.PureComponent {
defaultMessage='Purpose'
/>
<Markdown
navigator={navigator}
onPermalinkPress={onPermalinkPress}
baseTextStyle={baseTextStyle}
textStyles={textStyles}
@ -105,7 +102,6 @@ export default class ChannelInfoHeader extends React.PureComponent {
defaultMessage='Header'
/>
<Markdown
navigator={navigator}
onPermalinkPress={onPermalinkPress}
baseTextStyle={baseTextStyle}
textStyles={textStyles}

View file

@ -30,6 +30,12 @@ import {areChannelMentionsIgnored, getUserIdFromChannelName, isChannelMuted, sho
import {isAdmin as checkIsAdmin, isChannelAdmin as checkIsChannelAdmin, isSystemAdmin as checkIsSystemAdmin} from 'mattermost-redux/utils/user_utils';
import {getConfig, getLicense} from 'mattermost-redux/selectors/entities/general';
import {
popTopScreen,
goToScreen,
dismissModal,
showModalOverCurrentContext,
} from 'app/actions/navigation';
import {
closeDMChannel,
closeGMChannel,
@ -119,6 +125,10 @@ function mapDispatchToProps(dispatch) {
selectPenultimateChannel,
setChannelDisplayName,
handleSelectChannel,
popTopScreen,
goToScreen,
dismissModal,
showModalOverCurrentContext,
}, dispatch),
};
}

View file

@ -32,13 +32,14 @@ export default class ChannelMembers extends PureComponent {
getProfilesInChannel: PropTypes.func.isRequired,
handleRemoveChannelMembers: PropTypes.func.isRequired,
searchProfiles: PropTypes.func.isRequired,
setButtons: PropTypes.func.isRequired,
popTopScreen: PropTypes.func.isRequired,
}).isRequired,
componentId: PropTypes.string,
canManageUsers: PropTypes.bool.isRequired,
currentChannelId: PropTypes.string.isRequired,
currentChannelMembers: PropTypes.array,
currentUserId: PropTypes.string.isRequired,
navigator: PropTypes.object,
theme: PropTypes.object.isRequired,
};
@ -63,14 +64,14 @@ export default class ChannelMembers extends PureComponent {
};
this.removeButton = {
disabled: true,
enabled: false,
id: 'remove-members',
showAsAction: 'always',
title: context.intl.formatMessage({id: 'channel_members_modal.remove', defaultMessage: 'Remove'}),
text: context.intl.formatMessage({id: 'channel_members_modal.remove', defaultMessage: 'Remove'}),
};
if (props.canManageUsers) {
props.navigator.setButtons({
props.actions.setButtons(props.componentId, {
rightButtons: [this.removeButton],
});
}
@ -105,13 +106,14 @@ export default class ChannelMembers extends PureComponent {
};
close = () => {
this.props.navigator.pop({animated: true});
this.props.actions.popTopScreen();
};
enableRemoveOption = (enabled) => {
if (this.props.canManageUsers) {
this.props.navigator.setButtons({
rightButtons: [{...this.removeButton, disabled: !enabled}],
const {actions, canManageUsers, componentId} = this.props;
if (canManageUsers) {
actions.setButtons(componentId, {
rightButtons: [{...this.removeButton, enabled}],
});
}
};

View file

@ -10,11 +10,6 @@ import CustomList from 'app/components/custom_list';
import ChannelMembers from './channel_members';
describe('ChannelMembers', () => {
const navigator = {
setOnNavigatorEvent: jest.fn(),
setButtons: jest.fn(),
};
const baseProps = {
theme: Preferences.THEMES.default,
currentUserId: 'current-user-id',
@ -24,8 +19,9 @@ describe('ChannelMembers', () => {
getProfilesInChannel: jest.fn().mockImplementation(() => Promise.resolve()),
handleRemoveChannelMembers: jest.fn(),
searchProfiles: jest.fn(),
setButtons: jest.fn(),
popTopScreen: jest.fn(),
},
navigator,
componentId: 'component-id',
};

View file

@ -4,12 +4,14 @@
import {bindActionCreators} from 'redux';
import {connect} from 'react-redux';
import {handleRemoveChannelMembers} from 'app/actions/views/channel_members';
import {getTheme} from 'mattermost-redux/selectors/entities/preferences';
import {getCurrentChannel, canManageChannelMembers} from 'mattermost-redux/selectors/entities/channels';
import {makeGetProfilesInChannel} from 'mattermost-redux/selectors/entities/users';
import {getProfilesInChannel, searchProfiles} from 'mattermost-redux/actions/users';
import {setButtons, popTopScreen} from 'app/actions/navigation';
import {handleRemoveChannelMembers} from 'app/actions/views/channel_members';
import ChannelMembers from './channel_members';
function makeMapStateToProps() {
@ -40,6 +42,8 @@ function mapDispatchToProps(dispatch) {
getProfilesInChannel,
handleRemoveChannelMembers,
searchProfiles,
setButtons,
popTopScreen,
}, dispatch),
};
}

View file

@ -20,7 +20,6 @@ export default class ChannelPeek extends PureComponent {
channelId: PropTypes.string.isRequired,
currentUserId: PropTypes.string,
lastViewedAt: PropTypes.number,
navigator: PropTypes.object,
postIds: PropTypes.array,
theme: PropTypes.object.isRequired,
};
@ -72,7 +71,6 @@ export default class ChannelPeek extends PureComponent {
channelId,
currentUserId,
lastViewedAt,
navigator,
theme,
} = this.props;
@ -89,7 +87,6 @@ export default class ChannelPeek extends PureComponent {
currentUserId={currentUserId}
lastViewedAt={lastViewedAt}
channelId={channelId}
navigator={navigator}
/>
</View>
);

View file

@ -19,7 +19,6 @@ import {setNavigatorStyles} from 'app/utils/theme';
export default class CreateChannel extends PureComponent {
static propTypes = {
componentId: PropTypes.string,
navigator: PropTypes.object.isRequired,
theme: PropTypes.object.isRequired,
deviceWidth: PropTypes.number.isRequired,
deviceHeight: PropTypes.number.isRequired,
@ -28,6 +27,9 @@ export default class CreateChannel extends PureComponent {
closeButton: PropTypes.object,
actions: PropTypes.shape({
handleCreateChannel: PropTypes.func.isRequired,
setButtons: PropTypes.func.isRequired,
dismissModal: PropTypes.func.isRequired,
popTopScreen: PropTypes.func.isRequired,
}),
};
@ -45,7 +47,7 @@ export default class CreateChannel extends PureComponent {
rightButton = {
id: 'create-channel',
disabled: true,
enabled: false,
showAsAction: 'always',
};
@ -60,7 +62,7 @@ export default class CreateChannel extends PureComponent {
header: '',
};
this.rightButton.title = context.intl.formatMessage({id: 'mobile.create_channel', defaultMessage: 'Create'});
this.rightButton.text = context.intl.formatMessage({id: 'mobile.create_channel', defaultMessage: 'Create'});
if (props.closeButton) {
this.left = {...this.leftButton, icon: props.closeButton};
@ -74,7 +76,7 @@ export default class CreateChannel extends PureComponent {
buttons.leftButtons = [this.left];
}
props.navigator.setButtons(buttons);
props.actions.setButtons(props.componentId, buttons);
}
componentDidMount() {
@ -123,37 +125,38 @@ export default class CreateChannel extends PureComponent {
}
close = (goBack = false) => {
const {actions} = this.props;
if (goBack) {
this.props.navigator.pop({animated: true});
actions.popTopScreen();
} else {
this.props.navigator.dismissModal({
animationType: 'slide-down',
});
actions.dismissModal();
}
};
emitCanCreateChannel = (enabled) => {
const {actions, componentId} = this.props;
const buttons = {
rightButtons: [{...this.rightButton, disabled: !enabled}],
rightButtons: [{...this.rightButton, enabled}],
};
if (this.left) {
buttons.leftButtons = [this.left];
}
this.props.navigator.setButtons(buttons);
actions.setButtons(componentId, buttons);
};
emitCreating = (loading) => {
const {actions, componentId} = this.props;
const buttons = {
rightButtons: [{...this.rightButton, disabled: loading}],
rightButtons: [{...this.rightButton, enabled: !loading}],
};
if (this.left) {
buttons.leftButtons = [this.left];
}
this.props.navigator.setButtons(buttons);
actions.setButtons(componentId, buttons);
};
onCreateChannel = () => {
@ -176,7 +179,6 @@ export default class CreateChannel extends PureComponent {
render() {
const {
navigator,
theme,
deviceWidth,
deviceHeight,
@ -191,7 +193,6 @@ export default class CreateChannel extends PureComponent {
return (
<EditChannelInfo
navigator={navigator}
theme={theme}
enableRightButton={this.emitCanCreateChannel}
error={error}

View file

@ -4,9 +4,14 @@
import {bindActionCreators} from 'redux';
import {connect} from 'react-redux';
import {handleCreateChannel} from 'app/actions/views/create_channel';
import {getTheme} from 'mattermost-redux/selectors/entities/preferences';
import {
setButtons,
dismissModal,
popTopScreen,
} from 'app/actions/navigation';
import {handleCreateChannel} from 'app/actions/views/create_channel';
import {getDimensions} from 'app/selectors/device';
import CreateChannel from './create_channel';
@ -27,6 +32,9 @@ function mapDispatchToProps(dispatch) {
return {
actions: bindActionCreators({
handleCreateChannel,
setButtons,
dismissModal,
popTopScreen,
}, dispatch),
};
}

View file

@ -56,9 +56,10 @@ export default class EditChannel extends PureComponent {
patchChannel: PropTypes.func.isRequired,
getChannel: PropTypes.func.isRequired,
setChannelDisplayName: PropTypes.func.isRequired,
setButtons: PropTypes.func.isRequired,
popTopScreen: PropTypes.func.isRequired,
}),
componentId: PropTypes.string,
navigator: PropTypes.object.isRequired,
theme: PropTypes.object.isRequired,
deviceWidth: PropTypes.number.isRequired,
deviceHeight: PropTypes.number.isRequired,
@ -74,7 +75,7 @@ export default class EditChannel extends PureComponent {
rightButton = {
id: 'edit-channel',
disabled: true,
enabled: false,
showAsAction: 'always',
};
@ -98,13 +99,13 @@ export default class EditChannel extends PureComponent {
header,
};
this.rightButton.title = context.intl.formatMessage({id: 'mobile.edit_channel', defaultMessage: 'Save'});
this.rightButton.text = context.intl.formatMessage({id: 'mobile.edit_channel', defaultMessage: 'Save'});
const buttons = {
rightButtons: [this.rightButton],
};
props.navigator.setButtons(buttons);
props.actions.setButtons(props.componentId, buttons);
}
componentDidMount() {
@ -161,23 +162,25 @@ export default class EditChannel extends PureComponent {
this.props.actions.setChannelDisplayName(this.state.displayName);
}
this.props.navigator.pop({animated: true});
this.props.actions.popTopScreen();
};
emitCanUpdateChannel = (enabled) => {
const {actions, componentId} = this.props;
const buttons = {
rightButtons: [{...this.rightButton, disabled: !enabled}],
rightButtons: [{...this.rightButton, enabled}],
};
this.props.navigator.setButtons(buttons);
actions.setButtons(componentId, buttons);
};
emitUpdating = (loading) => {
const {actions, componentId} = this.props;
const buttons = {
rightButtons: [{...this.rightButton, disabled: loading}],
rightButtons: [{...this.rightButton, enabled: !loading}],
};
this.props.navigator.setButtons(buttons);
actions.setButtons(componentId, buttons);
};
validateDisplayName = (displayName) => {
@ -277,7 +280,6 @@ export default class EditChannel extends PureComponent {
purpose: oldPurpose,
type,
},
navigator,
theme,
currentTeamUrl,
deviceWidth,
@ -294,7 +296,6 @@ export default class EditChannel extends PureComponent {
return (
<EditChannelInfo
navigator={navigator}
theme={theme}
enableRightButton={this.emitCanUpdateChannel}
error={error}

View file

@ -9,6 +9,7 @@ import {getCurrentTeamUrl} from 'mattermost-redux/selectors/entities/teams';
import {patchChannel, getChannel} from 'mattermost-redux/actions/channels';
import {getTheme} from 'mattermost-redux/selectors/entities/preferences';
import {setButtons, popTopScreen} from 'app/actions/navigation';
import {setChannelDisplayName} from 'app/actions/views/channel';
import {getDimensions} from 'app/selectors/device';
@ -35,6 +36,8 @@ function mapDispatchToProps(dispatch) {
patchChannel,
getChannel,
setChannelDisplayName,
setButtons,
popTopScreen,
}, dispatch),
};
}

View file

@ -92,6 +92,7 @@ export default class EditProfile extends PureComponent {
dismissModal: PropTypes.func.isRequired,
setButtons: PropTypes.func.isRequired,
}).isRequired,
componentId: PropTypes.string,
currentUser: PropTypes.object.isRequired,
firstNameDisabled: PropTypes.bool.isRequired,
lastNameDisabled: PropTypes.bool.isRequired,
@ -107,7 +108,7 @@ export default class EditProfile extends PureComponent {
rightButton = {
id: 'update-profile',
disabled: true,
enabled: false,
showAsAction: 'always',
};
@ -118,9 +119,9 @@ export default class EditProfile extends PureComponent {
const buttons = {
rightButtons: [this.rightButton],
};
this.rightButton.title = context.intl.formatMessage({id: t('mobile.account.settings.save'), defaultMessage: 'Save'});
this.rightButton.text = context.intl.formatMessage({id: t('mobile.account.settings.save'), defaultMessage: 'Save'});
props.actions.setButtons(buttons);
props.actions.setButtons(props.componentId, buttons);
this.state = {
email,
@ -187,11 +188,12 @@ export default class EditProfile extends PureComponent {
};
emitCanUpdateAccount = (enabled) => {
const {actions, componentId} = this.props;
const buttons = {
rightButtons: [{...this.rightButton, disabled: !enabled}],
rightButtons: [{...this.rightButton, enabled}],
};
this.props.actions.setButtons(buttons);
actions.setButtons(componentId, buttons);
};
handleRequestError = (error) => {

View file

@ -12,11 +12,12 @@ import {getCurrentUserId, getCurrentUserRoles} from 'mattermost-redux/selectors/
import {getCurrentTeamId} from 'mattermost-redux/selectors/entities/teams';
import {showCreateOption} from 'mattermost-redux/utils/channel_utils';
import {isAdmin, isSystemAdmin} from 'mattermost-redux/utils/user_utils';
import {handleSelectChannel, setChannelDisplayName} from 'app/actions/views/channel';
import {getTheme} from 'mattermost-redux/selectors/entities/preferences';
import {getConfig, getLicense} from 'mattermost-redux/selectors/entities/general';
import {setButtons, dismissModal, goToScreen} from 'app/actions/navigation';
import {handleSelectChannel, setChannelDisplayName} from 'app/actions/views/channel';
import MoreChannels from './more_channels';
const joinableChannels = createSelector(
@ -53,6 +54,9 @@ function mapDispatchToProps(dispatch) {
getChannels,
searchChannels,
setChannelDisplayName,
setButtons,
dismissModal,
goToScreen,
}, dispatch),
};
}

View file

@ -29,6 +29,9 @@ export default class MoreChannels extends PureComponent {
getChannels: PropTypes.func.isRequired,
searchChannels: PropTypes.func.isRequired,
setChannelDisplayName: PropTypes.func.isRequired,
setButtons: PropTypes.func.isRequired,
dismissModal: PropTypes.func.isRequired,
goToScreen: PropTypes.func.isRequired,
}).isRequired,
componentId: PropTypes.string,
canCreateChannels: PropTypes.bool.isRequired,
@ -36,7 +39,6 @@ export default class MoreChannels extends PureComponent {
closeButton: PropTypes.object,
currentUserId: PropTypes.string.isRequired,
currentTeamId: PropTypes.string.isRequired,
navigator: PropTypes.object,
theme: PropTypes.object.isRequired,
};
@ -65,7 +67,7 @@ export default class MoreChannels extends PureComponent {
this.rightButton = {
id: 'create-pub-channel',
title: context.intl.formatMessage({id: 'mobile.create_channel', defaultMessage: 'Create'}),
text: context.intl.formatMessage({id: 'mobile.create_channel', defaultMessage: 'Create'}),
showAsAction: 'always',
};
@ -82,7 +84,7 @@ export default class MoreChannels extends PureComponent {
buttons.rightButtons = [this.rightButton];
}
props.navigator.setButtons(buttons);
props.actions.setButtons(props.componentId, buttons);
}
componentDidMount() {
@ -136,7 +138,7 @@ export default class MoreChannels extends PureComponent {
};
close = () => {
this.props.navigator.dismissModal({animationType: 'slide-down'});
this.props.actions.dismissModal();
};
doGetChannels = () => {
@ -164,16 +166,16 @@ export default class MoreChannels extends PureComponent {
getChannels = debounce(this.doGetChannels, 100);
headerButtons = (createEnabled) => {
const {canCreateChannels} = this.props;
const {actions, canCreateChannels, componentId} = this.props;
const buttons = {
leftButtons: [this.leftButton],
};
if (canCreateChannels) {
buttons.rightButtons = [{...this.rightButton, disabled: !createEnabled}];
buttons.rightButtons = [{...this.rightButton, enabled: createEnabled}];
}
this.props.navigator.setButtons(buttons);
actions.setButtons(componentId, buttons);
};
loadedChannels = ({data}) => {
@ -228,25 +230,16 @@ export default class MoreChannels extends PureComponent {
};
onCreateChannel = () => {
const {actions} = this.props;
const {formatMessage} = this.context.intl;
const {navigator, theme} = this.props;
navigator.push({
screen: 'CreateChannel',
animationType: 'slide-up',
title: formatMessage({id: 'mobile.create_channel.public', defaultMessage: 'New Public Channel'}),
backButtonTitle: '',
animated: true,
navigatorStyle: {
navBarTextColor: theme.sidebarHeaderTextColor,
navBarBackgroundColor: theme.sidebarHeaderBg,
navBarButtonColor: theme.sidebarHeaderTextColor,
screenBackgroundColor: theme.centerChannelBg,
},
passProps: {
channelType: General.OPEN_CHANNEL,
},
});
const screen = 'CreateChannel';
const title = formatMessage({id: 'mobile.create_channel.public', defaultMessage: 'New Public Channel'});
const passProps = {
channelType: General.OPEN_CHANNEL,
};
actions.goToScreen(screen, title, passProps);
};
renderLoading = () => {

View file

@ -11,18 +11,15 @@ import MoreChannels from './more_channels.js';
jest.mock('react-intl');
describe('MoreChannels', () => {
const navigator = {
setButtons: jest.fn(),
dismissModal: jest.fn(),
push: jest.fn(),
};
const actions = {
handleSelectChannel: jest.fn(),
joinChannel: jest.fn(),
getChannels: jest.fn().mockResolvedValue({data: [{id: 'id2', name: 'name2', display_name: 'display_name2'}]}),
searchChannels: jest.fn(),
setChannelDisplayName: jest.fn(),
setButtons: jest.fn(),
dismissModal: jest.fn(),
goToScreen: jest.fn(),
};
const baseProps = {
@ -32,7 +29,6 @@ describe('MoreChannels', () => {
closeButton: {},
currentUserId: 'current_user_id',
currentTeamId: 'current_team_id',
navigator,
theme: Preferences.THEMES.default,
componentId: 'component-id',
};
@ -46,27 +42,25 @@ describe('MoreChannels', () => {
expect(wrapper.getElement()).toMatchSnapshot();
});
test('should call props.navigator.dismissModal on close', () => {
test('should call props.actions.dismissModal on close', () => {
const wrapper = shallow(
<MoreChannels {...baseProps}/>,
{context: {intl: {formatMessage: jest.fn()}}},
);
wrapper.instance().close();
expect(baseProps.navigator.dismissModal).toHaveBeenCalledTimes(1);
expect(baseProps.navigator.dismissModal).toHaveBeenCalledWith({animationType: 'slide-down'});
expect(baseProps.actions.dismissModal).toHaveBeenCalledTimes(1);
});
test('should call props.navigator.setButtons on headerButtons', () => {
const props = {...baseProps, navigator: {...navigator, setButtons: jest.fn()}};
test('should call props.actions.setButtons on headerButtons', () => {
const wrapper = shallow(
<MoreChannels {...props}/>,
<MoreChannels {...baseProps}/>,
{context: {intl: {formatMessage: jest.fn()}}},
);
expect(props.navigator.setButtons).toHaveBeenCalledTimes(1);
expect(baseProps.actions.setButtons).toHaveBeenCalledTimes(1);
wrapper.instance().headerButtons(true);
expect(props.navigator.setButtons).toHaveBeenCalledTimes(2);
expect(baseProps.actions.setButtons).toHaveBeenCalledTimes(2);
});
test('should match return value of filterChannels', () => {