[ICU-579] Display Announcement banner (#1428)
* Announcement banner * Feedback review
This commit is contained in:
parent
6bc1f4a2db
commit
27c764d288
10 changed files with 219 additions and 2 deletions
11
app/actions/views/announcement.js
Normal file
11
app/actions/views/announcement.js
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
// Copyright (c) 2018-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
import {ViewTypes} from 'app/constants';
|
||||
|
||||
export function dismissBanner(text) {
|
||||
return {
|
||||
type: ViewTypes.ANNOUNCEMENT_BANNER,
|
||||
data: text
|
||||
};
|
||||
}
|
||||
144
app/components/announcement_banner/announcement_banner.js
Normal file
144
app/components/announcement_banner/announcement_banner.js
Normal file
|
|
@ -0,0 +1,144 @@
|
|||
// Copyright (c) 2018-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
import React, {PureComponent} from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import {
|
||||
Alert,
|
||||
Animated,
|
||||
StyleSheet,
|
||||
Text,
|
||||
TouchableOpacity
|
||||
} from 'react-native';
|
||||
import {intlShape} from 'react-intl';
|
||||
import MaterialIcons from 'react-native-vector-icons/MaterialIcons';
|
||||
|
||||
const {View: AnimatedView} = Animated;
|
||||
|
||||
export default class AnnouncementBanner extends PureComponent {
|
||||
static propTypes = {
|
||||
actions: PropTypes.shape({
|
||||
dismissBanner: PropTypes.func.isRequired
|
||||
}).isRequired,
|
||||
allowDismissal: PropTypes.bool,
|
||||
bannerColor: PropTypes.string,
|
||||
bannerDismissed: PropTypes.bool,
|
||||
bannerEnabled: PropTypes.bool,
|
||||
bannerText: PropTypes.string,
|
||||
bannerTextColor: PropTypes.string
|
||||
};
|
||||
|
||||
static contextTypes = {
|
||||
intl: intlShape
|
||||
};
|
||||
|
||||
state = {
|
||||
bannerHeight: new Animated.Value(0)
|
||||
};
|
||||
|
||||
componentWillMount() {
|
||||
const {bannerDismissed, bannerEnabled, bannerText} = this.props;
|
||||
const showBanner = bannerEnabled && !bannerDismissed && Boolean(bannerText);
|
||||
this.toggleBanner(showBanner);
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
if (this.props.bannerText !== nextProps.bannerText ||
|
||||
this.props.bannerEnabled !== nextProps.bannerEnabled ||
|
||||
this.props.bannerDismissed !== nextProps.bannerDismissed) {
|
||||
const showBanner = nextProps.bannerEnabled && !nextProps.bannerDismissed && Boolean(nextProps.bannerText);
|
||||
this.toggleBanner(showBanner);
|
||||
}
|
||||
}
|
||||
|
||||
handleDismiss = () => {
|
||||
const {actions, bannerText} = this.props;
|
||||
actions.dismissBanner(bannerText);
|
||||
};
|
||||
|
||||
handlePress = () => {
|
||||
const {formatMessage} = this.context.intl;
|
||||
const options = [{
|
||||
text: formatMessage({id: 'mobile.announcement_banner.ok', defaultMessage: 'OK'})
|
||||
}];
|
||||
|
||||
if (this.props.allowDismissal) {
|
||||
options.push({
|
||||
text: formatMessage({id: 'mobile.announcement_banner.dismiss', defaultMessage: 'Dismiss'}),
|
||||
onPress: this.handleDismiss
|
||||
});
|
||||
}
|
||||
|
||||
Alert.alert(
|
||||
formatMessage({id: 'mobile.announcement_banner.title', defaultMessage: 'Announcement'}),
|
||||
this.props.bannerText,
|
||||
options,
|
||||
{cancelable: false}
|
||||
);
|
||||
};
|
||||
|
||||
toggleBanner = (show = true) => {
|
||||
const value = show ? 38 : 0;
|
||||
Animated.timing(this.state.bannerHeight, {
|
||||
toValue: value,
|
||||
duration: 350
|
||||
}).start();
|
||||
};
|
||||
|
||||
render() {
|
||||
const {bannerHeight} = this.state;
|
||||
|
||||
const bannerStyle = {
|
||||
backgroundColor: this.props.bannerColor,
|
||||
height: bannerHeight
|
||||
};
|
||||
|
||||
const bannerTextStyle = {
|
||||
color: this.props.bannerTextColor
|
||||
};
|
||||
|
||||
return (
|
||||
<AnimatedView
|
||||
style={[style.bannerContainer, bannerStyle]}
|
||||
>
|
||||
<TouchableOpacity
|
||||
onPress={this.handlePress}
|
||||
style={style.wrapper}
|
||||
>
|
||||
<Text
|
||||
ellipsizeMode='tail'
|
||||
numberOfLines={1}
|
||||
style={[style.bannerText, bannerTextStyle]}
|
||||
>
|
||||
{this.props.bannerText}
|
||||
</Text>
|
||||
<MaterialIcons
|
||||
color={this.props.bannerTextColor}
|
||||
name='info'
|
||||
size={16}
|
||||
/>
|
||||
</TouchableOpacity>
|
||||
</AnimatedView>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const style = StyleSheet.create({
|
||||
bannerContainer: {
|
||||
paddingHorizontal: 10,
|
||||
position: 'absolute',
|
||||
top: 0,
|
||||
overflow: 'hidden',
|
||||
width: '100%'
|
||||
},
|
||||
wrapper: {
|
||||
alignItems: 'center',
|
||||
flex: 1,
|
||||
flexDirection: 'row'
|
||||
},
|
||||
bannerText: {
|
||||
flex: 1,
|
||||
fontSize: 14,
|
||||
marginRight: 5
|
||||
}
|
||||
});
|
||||
36
app/components/announcement_banner/index.js
Normal file
36
app/components/announcement_banner/index.js
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
// Copyright (c) 2018-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 {dismissBanner} from 'app/actions/views/announcement';
|
||||
|
||||
import AnnouncementBanner from './announcement_banner';
|
||||
|
||||
function mapStateToProps(state) {
|
||||
const config = getConfig(state);
|
||||
const license = getLicense(state);
|
||||
const {announcement} = state.views;
|
||||
|
||||
return {
|
||||
allowDismissal: config.AllowBannerDismissal === 'true',
|
||||
bannerColor: config.BannerColor,
|
||||
bannerDismissed: config.BannerText === announcement,
|
||||
bannerEnabled: config.EnableBanner === 'true' && license.IsLicensed === 'true',
|
||||
bannerText: config.BannerText,
|
||||
bannerTextColor: config.BannerTextColor
|
||||
};
|
||||
}
|
||||
|
||||
function mapDispatchToProps(dispatch) {
|
||||
return {
|
||||
actions: bindActionCreators({
|
||||
dismissBanner
|
||||
}, dispatch)
|
||||
};
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(AnnouncementBanner);
|
||||
|
|
@ -61,7 +61,8 @@ const ViewTypes = keyMirror({
|
|||
SET_LAST_UPGRADE_CHECK: null,
|
||||
|
||||
ADD_RECENT_EMOJI: null,
|
||||
EXTENSION_SELECTED_TEAM_ID: null
|
||||
EXTENSION_SELECTED_TEAM_ID: null,
|
||||
ANNOUNCEMENT_BANNER: null
|
||||
});
|
||||
|
||||
export default {
|
||||
|
|
|
|||
17
app/reducers/views/announcement.js
Normal file
17
app/reducers/views/announcement.js
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
// Copyright (c) 2018-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
import {UserTypes} from 'mattermost-redux/action_types';
|
||||
|
||||
import {ViewTypes} from 'app/constants';
|
||||
|
||||
export default function banner(state = '', action) {
|
||||
switch (action.type) {
|
||||
case ViewTypes.ANNOUNCEMENT_BANNER:
|
||||
return action.data;
|
||||
case UserTypes.LOGOUT_SUCCESS:
|
||||
return '';
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
}
|
||||
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
import {combineReducers} from 'redux';
|
||||
|
||||
import announcement from './announcement';
|
||||
import channel from './channel';
|
||||
import clientUpgrade from './client_upgrade';
|
||||
import extension from './extension';
|
||||
|
|
@ -17,6 +18,7 @@ import team from './team';
|
|||
import thread from './thread';
|
||||
|
||||
export default combineReducers({
|
||||
announcement,
|
||||
channel,
|
||||
clientUpgrade,
|
||||
extension,
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import {
|
|||
View
|
||||
} from 'react-native';
|
||||
|
||||
import AnnouncementBanner from 'app/components/announcement_banner';
|
||||
import PostList from 'app/components/post_list';
|
||||
import PostListRetry from 'app/components/post_list_retry';
|
||||
import RetryBarIndicator from 'app/components/retry_bar_indicator';
|
||||
|
|
@ -159,6 +160,7 @@ export default class ChannelPostList extends PureComponent {
|
|||
return (
|
||||
<View style={style.container}>
|
||||
{component}
|
||||
<AnnouncementBanner/>
|
||||
<RetryBarIndicator/>
|
||||
</View>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -46,7 +46,7 @@ const setTransforms = [
|
|||
export default function configureAppStore(initialState) {
|
||||
const viewsBlackListFilter = createBlacklistFilter(
|
||||
'views',
|
||||
['extension', 'login', 'root']
|
||||
['announcement', 'extension', 'login', 'root']
|
||||
);
|
||||
|
||||
const typingBlackListFilter = createBlacklistFilter(
|
||||
|
|
|
|||
|
|
@ -319,6 +319,7 @@ function cleanupState(action, keepCurrent = false) {
|
|||
users: payload.entities.users
|
||||
},
|
||||
views: {
|
||||
announcement: payload.views.announcement,
|
||||
...resetPayload.views,
|
||||
channel: {
|
||||
...resetPayload.views.channel,
|
||||
|
|
|
|||
|
|
@ -1940,6 +1940,9 @@
|
|||
"mobile.android.photos_permission_denied_title": "Photo library access is required",
|
||||
"mobile.android.videos_permission_denied_description": "To upload videos from your library, please change your permission settings.",
|
||||
"mobile.android.videos_permission_denied_title": "Video library access is required",
|
||||
"mobile.announcement_banner.title": "Announcement",
|
||||
"mobile.announcement_banner.ok": "OK",
|
||||
"mobile.announcement_banner.dismiss": "Dismiss",
|
||||
"mobile.channel.markAsRead": "Mark As Read",
|
||||
"mobile.channel_drawer.search": "Jump to...",
|
||||
"mobile.channel_info.alertMessageDeleteChannel": "Are you sure you want to delete the {term} {name}?",
|
||||
|
|
|
|||
Loading…
Reference in a new issue