RN-321 Update Settings and Notifications UX/UI Screens (#879)
* Fix Android and iOS flow * Own review * RN-321 Update Settings and Notifications UX/UI Screens * Fix style for separator in android modals * Feedback review
This commit is contained in:
parent
12cac7305f
commit
adbaead58e
35 changed files with 3212 additions and 1202 deletions
|
|
@ -11,12 +11,12 @@ export function handleUpdateUserNotifyProps(notifyProps) {
|
|||
const config = state.entities.general.config;
|
||||
const {currentUserId} = state.entities.users;
|
||||
|
||||
const {interval, ...otherProps} = notifyProps;
|
||||
const {interval, user_id, ...otherProps} = notifyProps;
|
||||
|
||||
const email = notifyProps.email;
|
||||
if (config.EnableEmailBatching === 'true' && email !== 'false') {
|
||||
const emailInterval = [{
|
||||
user_id: notifyProps.user_id,
|
||||
user_id,
|
||||
category: Preferences.CATEGORY_NOTIFICATIONS,
|
||||
name: Preferences.EMAIL_INTERVAL,
|
||||
value: interval
|
||||
|
|
|
|||
10
app/components/radio_button/index.js
Normal file
10
app/components/radio_button/index.js
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
import RadioButton from './radio_button';
|
||||
import RadioButtonGroup from './radio_button_group';
|
||||
|
||||
export {
|
||||
RadioButton,
|
||||
RadioButtonGroup
|
||||
};
|
||||
170
app/components/radio_button/radio_button.js
Normal file
170
app/components/radio_button/radio_button.js
Normal file
|
|
@ -0,0 +1,170 @@
|
|||
// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
import {connect} from 'react-redux';
|
||||
import React, {PureComponent} from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import {Animated, Text, View} from 'react-native';
|
||||
import Icon from 'react-native-vector-icons/MaterialIcons';
|
||||
|
||||
import {getTheme} from 'app/selectors/preferences';
|
||||
import {makeStyleSheetFromTheme} from 'app/utils/theme';
|
||||
|
||||
const DISABLED_OPACITY = 0.26;
|
||||
const DEFAULT_OPACITY = 1;
|
||||
|
||||
class RadioButton extends PureComponent {
|
||||
static propTypes = {
|
||||
label: PropTypes.string,
|
||||
theme: PropTypes.object,
|
||||
value: PropTypes.string,
|
||||
checked: PropTypes.bool,
|
||||
disabled: PropTypes.bool,
|
||||
onCheck: PropTypes.func
|
||||
};
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
scaleValue: new Animated.Value(0.001),
|
||||
opacityValue: new Animated.Value(0.1)
|
||||
};
|
||||
|
||||
this.responder = {
|
||||
onStartShouldSetResponder: () => true,
|
||||
onResponderGrant: this.highlight,
|
||||
onResponderRelease: this.handleResponderEnd,
|
||||
onResponderTerminate: this.unHighlight
|
||||
};
|
||||
}
|
||||
|
||||
highlight = () => {
|
||||
Animated.timing(
|
||||
this.state.scaleValue,
|
||||
{
|
||||
toValue: 1,
|
||||
duration: 150
|
||||
}
|
||||
).start();
|
||||
Animated.timing(
|
||||
this.state.opacityValue,
|
||||
{
|
||||
toValue: 0.1,
|
||||
duration: 100
|
||||
}
|
||||
).start();
|
||||
};
|
||||
|
||||
unHighlight = () => {
|
||||
Animated.timing(
|
||||
this.state.scaleValue,
|
||||
{
|
||||
toValue: 0.001,
|
||||
duration: 1500
|
||||
}
|
||||
).start();
|
||||
Animated.timing(
|
||||
this.state.opacityValue,
|
||||
{
|
||||
toValue: 0
|
||||
}
|
||||
).start();
|
||||
};
|
||||
|
||||
handleResponderEnd = () => {
|
||||
const {checked, disabled, onCheck, value} = this.props;
|
||||
|
||||
if (!checked && !disabled && onCheck) {
|
||||
onCheck(value);
|
||||
}
|
||||
|
||||
this.unHighlight();
|
||||
};
|
||||
|
||||
render() {
|
||||
const {scaleValue, opacityValue} = this.state;
|
||||
const {theme, checked, disabled} = this.props;
|
||||
const styles = getStyleSheet(theme);
|
||||
|
||||
const color = checked ? theme.buttonBg : theme.centerChannelColor;
|
||||
const opacity = disabled ? DISABLED_OPACITY : DEFAULT_OPACITY;
|
||||
|
||||
return (
|
||||
<View
|
||||
style={styles.container}
|
||||
{...this.responder}
|
||||
>
|
||||
<Animated.View
|
||||
style={[
|
||||
styles.ripple,
|
||||
{
|
||||
transform: [
|
||||
{scale: scaleValue}
|
||||
],
|
||||
opacity: opacityValue,
|
||||
backgroundColor: color
|
||||
}
|
||||
]}
|
||||
/>
|
||||
<Icon
|
||||
name={checked ? 'radio-button-checked' : 'radio-button-unchecked'}
|
||||
size={24}
|
||||
color={color}
|
||||
style={{
|
||||
opacity
|
||||
}}
|
||||
/>
|
||||
<View style={styles.labelContainer}>
|
||||
<Text
|
||||
style={[
|
||||
styles.label,
|
||||
{opacity: disabled ? DISABLED_OPACITY : DEFAULT_OPACITY}
|
||||
]}
|
||||
>
|
||||
{this.props.label}
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const getStyleSheet = makeStyleSheetFromTheme((theme) => {
|
||||
return {
|
||||
container: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
backgroundColor: 'rgba(0,0,0,0)',
|
||||
marginBottom: 15
|
||||
},
|
||||
ripple: {
|
||||
position: 'absolute',
|
||||
width: 48,
|
||||
height: 48,
|
||||
borderRadius: 56,
|
||||
top: 6
|
||||
},
|
||||
labelContainer: {
|
||||
flex: 1,
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'center'
|
||||
},
|
||||
label: {
|
||||
color: theme.centerChannelColor,
|
||||
flex: 1,
|
||||
fontSize: 17,
|
||||
marginLeft: 15,
|
||||
textAlignVertical: 'center',
|
||||
includeFontPadding: false
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
function mapStateToProps(state, ownProps) {
|
||||
return {
|
||||
...ownProps,
|
||||
theme: getTheme(state)
|
||||
};
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps)(RadioButton);
|
||||
99
app/components/radio_button/radio_button_group.js
Normal file
99
app/components/radio_button/radio_button_group.js
Normal file
|
|
@ -0,0 +1,99 @@
|
|||
// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
import React, {PureComponent} from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import {View} from 'react-native';
|
||||
import RadioButton from './radio_button';
|
||||
|
||||
export default class RadioButtonGroup extends PureComponent {
|
||||
static propTypes = {
|
||||
children: PropTypes.node.isRequired,
|
||||
name: PropTypes.string.isRequired,
|
||||
value: PropTypes.string,
|
||||
onSelect: PropTypes.func
|
||||
};
|
||||
|
||||
state = {};
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
if (props.value) {
|
||||
this.state = {
|
||||
selected: props.value
|
||||
};
|
||||
} else {
|
||||
React.Children.map(this.props.children, (option) => {
|
||||
if (option) {
|
||||
const {
|
||||
value,
|
||||
checked
|
||||
} = option.props;
|
||||
|
||||
if (!this.state.selected && checked) {
|
||||
this.state = {
|
||||
selected: value
|
||||
};
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
get value() {
|
||||
return this.state.selected;
|
||||
}
|
||||
|
||||
set value(value) {
|
||||
this.onChange(value);
|
||||
}
|
||||
|
||||
onChange = (value) => {
|
||||
const {onSelect} = this.props;
|
||||
this.setState({
|
||||
selected: value
|
||||
}, () => {
|
||||
if (onSelect) {
|
||||
onSelect(value);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
render = () => {
|
||||
const options = React.Children.map(this.props.children, (option) => {
|
||||
if (option) {
|
||||
const {
|
||||
value,
|
||||
label,
|
||||
disabled,
|
||||
...other
|
||||
} = option.props;
|
||||
|
||||
const {name} = this.props;
|
||||
|
||||
return (
|
||||
<RadioButton
|
||||
{...other}
|
||||
ref={value}
|
||||
name={name}
|
||||
key={`${name}-${value}`}
|
||||
value={value}
|
||||
label={label}
|
||||
disabled={disabled}
|
||||
onCheck={this.onChange}
|
||||
checked={this.state.selected && value === this.state.selected}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return null;
|
||||
}, this);
|
||||
|
||||
return (
|
||||
<View>
|
||||
{options}
|
||||
</View>
|
||||
);
|
||||
};
|
||||
}
|
||||
|
|
@ -288,7 +288,7 @@ const getStyleSheet = makeStyleSheetFromTheme((theme) => {
|
|||
},
|
||||
scrollView: {
|
||||
flex: 1,
|
||||
backgroundColor: changeOpacity(theme.centerChannelColor, 0.03)
|
||||
backgroundColor: changeOpacity(theme.centerChannelColor, 0.06)
|
||||
},
|
||||
scrollViewContent: {
|
||||
paddingBottom: 30
|
||||
|
|
|
|||
|
|
@ -1,601 +0,0 @@
|
|||
// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
import React, {PureComponent} from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import {injectIntl, intlShape} from 'react-intl';
|
||||
import {
|
||||
Keyboard,
|
||||
Platform,
|
||||
ScrollView,
|
||||
View
|
||||
} from 'react-native';
|
||||
|
||||
import {Preferences, RequestStatus} from 'mattermost-redux/constants';
|
||||
import {getPreferencesByCategory} from 'mattermost-redux/utils/preference_utils';
|
||||
|
||||
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 Section from './section';
|
||||
import SectionItem from './section_item';
|
||||
|
||||
class AccountNotifications extends PureComponent {
|
||||
static propTypes = {
|
||||
actions: PropTypes.shape({
|
||||
handleUpdateUserNotifyProps: PropTypes.func.isRequired
|
||||
}),
|
||||
config: PropTypes.object.isRequired,
|
||||
currentUser: PropTypes.object.isRequired,
|
||||
intl: intlShape.isRequired,
|
||||
myPreferences: PropTypes.object.isRequired,
|
||||
navigator: PropTypes.object,
|
||||
theme: PropTypes.object.isRequired,
|
||||
updateMeRequest: PropTypes.object.isRequired
|
||||
};
|
||||
|
||||
saveButton = {
|
||||
id: 'save-notifications',
|
||||
showAsAction: 'always'
|
||||
};
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
const {currentUser} = props;
|
||||
const notifyProps = currentUser.notify_props || {};
|
||||
|
||||
props.navigator.setOnNavigatorEvent(this.onNavigatorEvent);
|
||||
props.navigator.setButtons({
|
||||
rightButtons: [{
|
||||
...this.saveButton,
|
||||
title: props.intl.formatMessage({id: 'edit_post.save', defaultMessage: 'Save'})
|
||||
}]
|
||||
});
|
||||
|
||||
this.setStateFromNotifyProps(notifyProps);
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
if (Platform.OS === 'android') {
|
||||
Keyboard.addListener('keyboardDidHide', this.handleAndroidKeyboard);
|
||||
}
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
if (Platform.OS === 'android') {
|
||||
Keyboard.removeListener('keyboardDidHide', this.handleAndroidKeyboard);
|
||||
}
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
const {currentUser, updateMeRequest} = nextProps;
|
||||
if (currentUser !== this.props.currentUser) {
|
||||
const {notify_props: notifyProps} = currentUser;
|
||||
this.setStateFromNotifyProps(notifyProps);
|
||||
}
|
||||
|
||||
if (this.props.updateMeRequest !== updateMeRequest) {
|
||||
switch (updateMeRequest.status) {
|
||||
case RequestStatus.STARTED:
|
||||
this.savingNotifyProps(true);
|
||||
this.setState({error: null, saving: true});
|
||||
break;
|
||||
case RequestStatus.SUCCESS:
|
||||
this.savingNotifyProps(false);
|
||||
this.setState({error: null, saving: false});
|
||||
this.close();
|
||||
break;
|
||||
case RequestStatus.FAILURE:
|
||||
this.savingNotifyProps(false);
|
||||
this.setState({error: updateMeRequest.error, saving: false});
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
handleAndroidKeyboard = () => {
|
||||
this.refs.mention_keys.getWrappedInstance().blur();
|
||||
};
|
||||
|
||||
onNavigatorEvent = (event) => {
|
||||
if (event.type === 'NavBarButtonPress') {
|
||||
if (event.id === 'save-notifications') {
|
||||
this.saveUserNotifyProps();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
setStateFromNotifyProps = (notifyProps) => {
|
||||
const mentionKeys = (notifyProps.mention_keys || '').split(',');
|
||||
const usernameMentionIndex = mentionKeys.indexOf(this.props.currentUser.username);
|
||||
if (usernameMentionIndex > -1) {
|
||||
mentionKeys.splice(usernameMentionIndex, 1);
|
||||
}
|
||||
|
||||
const email = notifyProps.email;
|
||||
let interval;
|
||||
if (this.props.config.EnableEmailBatching === 'true') {
|
||||
const emailPreferences = getPreferencesByCategory(this.props.myPreferences, Preferences.CATEGORY_NOTIFICATIONS);
|
||||
if (emailPreferences.size) {
|
||||
interval = emailPreferences.get(Preferences.EMAIL_INTERVAL).value;
|
||||
}
|
||||
}
|
||||
|
||||
const comments = notifyProps.comments || 'never';
|
||||
|
||||
const newState = {
|
||||
...notifyProps,
|
||||
comments,
|
||||
email,
|
||||
interval,
|
||||
usernameMention: usernameMentionIndex > -1,
|
||||
mention_keys: mentionKeys.join(',')
|
||||
};
|
||||
|
||||
if (this.state) {
|
||||
this.setState(newState);
|
||||
} else {
|
||||
this.state = {...newState};
|
||||
}
|
||||
};
|
||||
|
||||
toggleFirstNameMention = () => {
|
||||
this.setState({
|
||||
first_name: (!(this.state.first_name === 'true')).toString()
|
||||
});
|
||||
};
|
||||
|
||||
toggleUsernameMention = () => {
|
||||
this.setState({
|
||||
usernameMention: !this.state.usernameMention
|
||||
});
|
||||
};
|
||||
|
||||
toggleChannelMentions = () => {
|
||||
this.setState({
|
||||
channel: (!(this.state.channel === 'true')).toString()
|
||||
});
|
||||
};
|
||||
|
||||
focusMentionKeys = () => {
|
||||
this.refs.mention_keys.getWrappedInstance().focus();
|
||||
};
|
||||
|
||||
updateMentionKeys = (text) => {
|
||||
this.setState({
|
||||
mention_keys: text
|
||||
});
|
||||
};
|
||||
|
||||
savingNotifyProps = (loading) => {
|
||||
this.props.navigator.setButtons({
|
||||
rightButtons: [{...this.saveButton, disabled: loading}]
|
||||
});
|
||||
};
|
||||
|
||||
setEmailNotifications = (value) => {
|
||||
const {config} = this.props;
|
||||
let email = value;
|
||||
let interval;
|
||||
|
||||
const emailBatchingEnabled = config.EnableEmailBatching === 'true';
|
||||
if (emailBatchingEnabled && value !== 'false') {
|
||||
interval = value;
|
||||
email = 'true';
|
||||
}
|
||||
|
||||
this.setState({
|
||||
email,
|
||||
interval
|
||||
});
|
||||
};
|
||||
|
||||
setReplyNotifications = (value) => {
|
||||
this.setState({
|
||||
comments: value
|
||||
});
|
||||
};
|
||||
|
||||
setMobilePush = (value) => {
|
||||
this.setState({
|
||||
push: value
|
||||
});
|
||||
};
|
||||
|
||||
setMobilePushStatus = (value) => {
|
||||
this.setState({
|
||||
push_status: value
|
||||
});
|
||||
};
|
||||
|
||||
saveUserNotifyProps = () => {
|
||||
let {mention_keys: mentionKeys, usernameMention, ...notifyProps} = this.state; //eslint-disable-line prefer-const
|
||||
|
||||
if (mentionKeys.length > 0) {
|
||||
mentionKeys = mentionKeys.split(',').map((m) => m.replace(/\s/g, ''));
|
||||
} else {
|
||||
mentionKeys = [];
|
||||
}
|
||||
|
||||
if (usernameMention) {
|
||||
mentionKeys.push(`${this.props.currentUser.username}`);
|
||||
}
|
||||
|
||||
mentionKeys = mentionKeys.join(',');
|
||||
|
||||
this.props.actions.handleUpdateUserNotifyProps({
|
||||
...notifyProps,
|
||||
mention_keys: mentionKeys,
|
||||
user_id: this.props.currentUser.id
|
||||
});
|
||||
};
|
||||
|
||||
buildMentionSection = () => {
|
||||
const {currentUser, theme} = this.props;
|
||||
const style = getStyleSheet(theme);
|
||||
|
||||
return (
|
||||
<Section
|
||||
headerId='user.settings.notifications.wordsTrigger'
|
||||
headerDefaultMessage='Words that trigger mentions'
|
||||
footerId='mobile.account_notifications.mentions_footer'
|
||||
footerDefaultMessage='Your username (\"{username}\") will always trigger mentions.'
|
||||
footerValues={{username: currentUser.username}}
|
||||
theme={theme}
|
||||
>
|
||||
{currentUser.first_name.length > 0 &&
|
||||
<View>
|
||||
<SectionItem
|
||||
labelId='user.settings.notifications.sensitiveName'
|
||||
labelDefaultMessage='Your case sensitive first name "{first_name}"'
|
||||
labelValues={{first_name: currentUser.first_name}}
|
||||
action={this.toggleFirstNameMention}
|
||||
actionType='toggle'
|
||||
selected={this.state.first_name === 'true'}
|
||||
theme={theme}
|
||||
/>
|
||||
<View style={style.separator}/>
|
||||
</View>
|
||||
}
|
||||
<SectionItem
|
||||
labelId='user.settings.notifications.sensitiveUsername'
|
||||
labelDefaultMessage='Your non-case sensitive username "{username}"'
|
||||
labelValues={{username: currentUser.username}}
|
||||
selected={this.state.usernameMention}
|
||||
action={this.toggleUsernameMention}
|
||||
actionType='toggle'
|
||||
theme={theme}
|
||||
/>
|
||||
<View style={style.separator}/>
|
||||
<SectionItem
|
||||
labelId='user.settings.notifications.channelWide'
|
||||
labelDefaultMessage='Channel-wide mentions "@channel", "@all"'
|
||||
action={this.toggleChannelMentions}
|
||||
actionType='toggle'
|
||||
selected={this.state.channel === 'true'}
|
||||
theme={theme}
|
||||
/>
|
||||
<View style={style.separator}/>
|
||||
<SectionItem
|
||||
labelId='user.settings.notifications.sensitiveWords'
|
||||
labelDefaultMessage='Other non-case sensitive words, separated by commas'
|
||||
theme={theme}
|
||||
action={this.focusMentionKeys}
|
||||
>
|
||||
<TextInputWithLocalizedPlaceholder
|
||||
ref='mention_keys'
|
||||
value={this.state.mention_keys}
|
||||
onChangeText={this.updateMentionKeys}
|
||||
style={style.input}
|
||||
autoCapitalize='none'
|
||||
autoCorrect={false}
|
||||
placeholder={{id: 'mobile.account_notifications.non-case_sensitive_words', defaultMessage: 'Additional words...'}}
|
||||
placeholderTextColor={changeOpacity(theme.centerChannelColor, 0.4)}
|
||||
underlineColorAndroid='transparent'
|
||||
/>
|
||||
</SectionItem>
|
||||
</Section>
|
||||
);
|
||||
};
|
||||
|
||||
buildEmailSection = () => {
|
||||
const {config, theme} = this.props;
|
||||
const style = getStyleSheet(theme);
|
||||
|
||||
const sendEmailNotifications = config.SendEmailNotifications === 'true';
|
||||
const emailBatchingEnabled = config.EnableEmailBatching === 'true';
|
||||
|
||||
let sendImmediatley = this.state.email === 'true';
|
||||
let sendImmediatleyValue = 'true';
|
||||
let fifteenMinutes;
|
||||
let hourly;
|
||||
const never = this.state.email === 'false';
|
||||
|
||||
if (emailBatchingEnabled && this.state.email !== 'false') {
|
||||
sendImmediatley = this.state.interval === Preferences.INTERVAL_IMMEDIATE.toString();
|
||||
fifteenMinutes = this.state.interval === Preferences.INTERVAL_FIFTEEN_MINUTES.toString();
|
||||
hourly = this.state.interval === Preferences.INTERVAL_HOUR.toString();
|
||||
|
||||
sendImmediatleyValue = Preferences.INTERVAL_IMMEDIATE.toString();
|
||||
}
|
||||
|
||||
return (
|
||||
<Section
|
||||
headerId='user.settings.notifications.email.send'
|
||||
headerDefaultMessage='Send email notifications'
|
||||
footerId='user.settings.notifications.emailInfo'
|
||||
footerDefaultMessage='Email notifications are sent for mentions and direct messages when you are offline or away from {siteName} for more than 5 minutes.'
|
||||
footerValues={{siteName: config.SiteName}}
|
||||
disableFooter={!sendEmailNotifications}
|
||||
theme={theme}
|
||||
>
|
||||
{sendEmailNotifications &&
|
||||
<View>
|
||||
<SectionItem
|
||||
labelId='user.settings.notifications.email.immediately'
|
||||
labelDefaultMessage='Immediately'
|
||||
action={this.setEmailNotifications}
|
||||
actionType='select'
|
||||
actionValue={sendImmediatleyValue}
|
||||
selected={sendImmediatley}
|
||||
theme={theme}
|
||||
/>
|
||||
<View style={style.separator}/>
|
||||
{emailBatchingEnabled &&
|
||||
<View>
|
||||
<SectionItem
|
||||
labelId='user.settings.notifications.email.everyXMinutes'
|
||||
labelDefaultMessage='Every {count, plural, one {minute} other {{count, number} minutes}}'
|
||||
labelValues={{count: Preferences.INTERVAL_FIFTEEN_MINUTES / 60}}
|
||||
action={this.setEmailNotifications}
|
||||
actionType='select'
|
||||
actionValue={Preferences.INTERVAL_FIFTEEN_MINUTES.toString()}
|
||||
selected={fifteenMinutes}
|
||||
theme={theme}
|
||||
/>
|
||||
<View style={style.separator}/>
|
||||
<SectionItem
|
||||
labelId='user.settings.notifications.email.everyHour'
|
||||
labelDefaultMessage='Every hour'
|
||||
action={this.setEmailNotifications}
|
||||
actionType='select'
|
||||
actionValue={Preferences.INTERVAL_HOUR.toString()}
|
||||
selected={hourly}
|
||||
theme={theme}
|
||||
/>
|
||||
<View style={style.separator}/>
|
||||
</View>
|
||||
}
|
||||
<SectionItem
|
||||
labelId='user.settings.notifications.email.never'
|
||||
labelDefaultMessage='Never'
|
||||
action={this.setEmailNotifications}
|
||||
actionType='select'
|
||||
actionValue='false'
|
||||
selected={never}
|
||||
theme={theme}
|
||||
/>
|
||||
</View>
|
||||
}
|
||||
{!sendEmailNotifications &&
|
||||
<SectionItem
|
||||
labelId='user.settings.general.emailHelp2'
|
||||
labelDefaultMessage='Email has been disabled by your System Administrator. No notification emails will be sent until it is enabled.'
|
||||
theme={theme}
|
||||
/>
|
||||
}
|
||||
</Section>
|
||||
);
|
||||
};
|
||||
|
||||
buildReplySection = () => {
|
||||
const {theme} = this.props;
|
||||
const style = getStyleSheet(theme);
|
||||
|
||||
return (
|
||||
<Section
|
||||
headerId='mobile.account_notifications.reply.header'
|
||||
headerDefaultMessage='Send reply notifications for'
|
||||
footerId='user.settings.notifications.commentsInfo'
|
||||
footerDefaultMessage="In addition to notifications for when you're mentioned, select if you would like to receive notifications on reply threads."
|
||||
theme={theme}
|
||||
>
|
||||
<SectionItem
|
||||
labelId='mobile.account_notifications.threads_start_participate'
|
||||
labelDefaultMessage='Threads that I start or participate in'
|
||||
action={this.setReplyNotifications}
|
||||
actionType='select'
|
||||
actionValue='any'
|
||||
selected={this.state.comments === 'any'}
|
||||
theme={theme}
|
||||
/>
|
||||
<View style={style.separator}/>
|
||||
<SectionItem
|
||||
labelId='mobile.account_notifications.threads_start'
|
||||
labelDefaultMessage='Threads that I start'
|
||||
action={this.setReplyNotifications}
|
||||
actionType='select'
|
||||
actionValue='root'
|
||||
selected={this.state.comments === 'root'}
|
||||
theme={theme}
|
||||
/>
|
||||
<View style={style.separator}/>
|
||||
<SectionItem
|
||||
labelId='mobile.account_notifications.threads_mentions'
|
||||
labelDefaultMessage='Mentions in threads'
|
||||
action={this.setReplyNotifications}
|
||||
actionType='select'
|
||||
actionValue='never'
|
||||
selected={this.state.comments === 'never'}
|
||||
theme={theme}
|
||||
/>
|
||||
</Section>
|
||||
);
|
||||
};
|
||||
|
||||
buildMobilePushSection = () => {
|
||||
const {config, theme} = this.props;
|
||||
const style = getStyleSheet(theme);
|
||||
|
||||
const pushNotificationsEnabled = config.SendPushNotifications === 'true';
|
||||
if (!pushNotificationsEnabled) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Section
|
||||
headerId='user.settings.push_notification.send'
|
||||
headerDefaultMessage='Send mobile push notifications'
|
||||
footerId='user.settings.push_notification.info'
|
||||
footerDefaultMessage='Notification alerts are pushed to your mobile device when there is activity in Mattermost.'
|
||||
theme={theme}
|
||||
>
|
||||
<SectionItem
|
||||
labelId='user.settings.notifications.allActivity'
|
||||
labelDefaultMessage='For all activity'
|
||||
action={this.setMobilePush}
|
||||
actionType='select'
|
||||
actionValue='all'
|
||||
selected={this.state.push === 'all'}
|
||||
theme={theme}
|
||||
/>
|
||||
<View style={style.separator}/>
|
||||
<SectionItem
|
||||
labelId='user.settings.notifications.onlyMentions'
|
||||
labelDefaultMessage='Only for mentions and direct messages'
|
||||
action={this.setMobilePush}
|
||||
actionType='select'
|
||||
actionValue='mention'
|
||||
selected={this.state.push === 'mention'}
|
||||
theme={theme}
|
||||
/>
|
||||
<View style={style.separator}/>
|
||||
<SectionItem
|
||||
labelId='user.settings.notifications.never'
|
||||
labelDefaultMessage='Never'
|
||||
action={this.setMobilePush}
|
||||
actionType='select'
|
||||
actionValue='none'
|
||||
selected={this.state.push === 'none'}
|
||||
theme={theme}
|
||||
/>
|
||||
</Section>
|
||||
);
|
||||
};
|
||||
|
||||
buildMobilePushStatusSection = () => {
|
||||
const {config, theme} = this.props;
|
||||
const style = getStyleSheet(theme);
|
||||
|
||||
const showSection = config.SendPushNotifications === 'true' && this.state.push !== 'none';
|
||||
if (!showSection) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Section
|
||||
headerId='user.settings.push_notification.status'
|
||||
headerDefaultMessage='Trigger push notifications when'
|
||||
footerId='user.settings.push_notification.status_info'
|
||||
footerDefaultMessage='Notification alerts are only pushed to your mobile device when your online status matches the selection above.'
|
||||
theme={theme}
|
||||
>
|
||||
<SectionItem
|
||||
labelId='user.settings.push_notification.online'
|
||||
labelDefaultMessage='Online, away or offline'
|
||||
action={this.setMobilePushStatus}
|
||||
actionType='select'
|
||||
actionValue='online'
|
||||
selected={this.state.push_status === 'online'}
|
||||
theme={theme}
|
||||
/>
|
||||
<View style={style.separator}/>
|
||||
<SectionItem
|
||||
labelId='user.settings.push_notification.away'
|
||||
labelDefaultMessage='Away or offline'
|
||||
action={this.setMobilePushStatus}
|
||||
actionType='select'
|
||||
actionValue='away'
|
||||
selected={this.state.push_status === 'away'}
|
||||
theme={theme}
|
||||
/>
|
||||
<View style={style.separator}/>
|
||||
<SectionItem
|
||||
labelId='user.settings.push_notification.offline'
|
||||
labelDefaultMessage='Offline'
|
||||
action={this.setMobilePushStatus}
|
||||
actionType='select'
|
||||
actionValue='offline'
|
||||
selected={this.state.push_status === 'offline'}
|
||||
theme={theme}
|
||||
/>
|
||||
</Section>
|
||||
);
|
||||
};
|
||||
|
||||
close = () => {
|
||||
this.props.navigator.pop({animated: true});
|
||||
};
|
||||
|
||||
render() {
|
||||
const {theme} = this.props;
|
||||
const style = getStyleSheet(theme);
|
||||
|
||||
if (this.state.saving) {
|
||||
return (
|
||||
<View style={style.wrapper}>
|
||||
<StatusBar/>
|
||||
<Loading/>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<View style={style.wrapper}>
|
||||
<StatusBar/>
|
||||
<ScrollView
|
||||
style={style.scrollView}
|
||||
contentContainerStyle={style.scrollViewContent}
|
||||
>
|
||||
{this.buildMentionSection()}
|
||||
{this.buildEmailSection()}
|
||||
{this.buildReplySection()}
|
||||
{this.buildMobilePushSection()}
|
||||
{this.buildMobilePushStatusSection()}
|
||||
</ScrollView>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const getStyleSheet = makeStyleSheetFromTheme((theme) => {
|
||||
return {
|
||||
wrapper: {
|
||||
flex: 1,
|
||||
backgroundColor: theme.centerChannelBg
|
||||
},
|
||||
input: {
|
||||
color: theme.centerChannelColor,
|
||||
fontSize: 12,
|
||||
height: 40
|
||||
},
|
||||
separator: {
|
||||
height: 1,
|
||||
flex: 1,
|
||||
backgroundColor: changeOpacity(theme.centerChannelColor, 0.1),
|
||||
marginHorizontal: 15
|
||||
},
|
||||
scrollView: {
|
||||
flex: 1,
|
||||
backgroundColor: changeOpacity(theme.centerChannelColor, 0.03)
|
||||
},
|
||||
scrollViewContent: {
|
||||
paddingBottom: 30
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
export default injectIntl(AccountNotifications);
|
||||
|
|
@ -1,159 +0,0 @@
|
|||
// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
import React, {PureComponent} from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import {injectIntl, intlShape} from 'react-intl';
|
||||
import {
|
||||
TouchableOpacity,
|
||||
View
|
||||
} from 'react-native';
|
||||
import Icon from 'react-native-vector-icons/FontAwesome';
|
||||
|
||||
import FormattedText from 'app/components/formatted_text';
|
||||
import StatusBar from 'app/components/status_bar';
|
||||
import {preventDoubleTap} from 'app/utils/tap';
|
||||
import {changeOpacity, makeStyleSheetFromTheme} from 'app/utils/theme';
|
||||
|
||||
class AccountSettings extends PureComponent {
|
||||
static propTypes = {
|
||||
intl: intlShape.isRequired,
|
||||
navigator: PropTypes.object,
|
||||
theme: PropTypes.object.isRequired
|
||||
};
|
||||
|
||||
// The enabled arg can be removed once all the scenes have been implemented.
|
||||
buildItemRow = (icon, id, defaultMessage, action, separator = true, enabled = false) => {
|
||||
if (!enabled) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const {theme} = this.props;
|
||||
const style = getStyleSheet(theme);
|
||||
|
||||
return (
|
||||
<View
|
||||
key={id}
|
||||
style={style.itemWrapper}
|
||||
>
|
||||
<TouchableOpacity
|
||||
style={style.item}
|
||||
onPress={() => preventDoubleTap(action)}
|
||||
>
|
||||
<View style={style.itemLeftIconContainer}>
|
||||
<Icon
|
||||
name={icon}
|
||||
size={18}
|
||||
style={style.itemLeftIcon}
|
||||
/>
|
||||
</View>
|
||||
<FormattedText
|
||||
id={id}
|
||||
defaultMessage={defaultMessage}
|
||||
style={style.itemText}
|
||||
/>
|
||||
<Icon
|
||||
name='angle-right'
|
||||
size={18}
|
||||
style={style.itemRightIcon}
|
||||
/>
|
||||
</TouchableOpacity>
|
||||
{separator && <View style={style.separator}/>}
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
goToAccountNotifications = () => {
|
||||
const {intl, navigator, theme} = this.props;
|
||||
navigator.push({
|
||||
backButtonTitle: '',
|
||||
screen: 'AccountNotifications',
|
||||
title: intl.formatMessage({id: 'user.settings.modal.notifications', defaultMessage: 'Notifications'}),
|
||||
animated: true,
|
||||
navigatorStyle: {
|
||||
navBarTextColor: theme.sidebarHeaderTextColor,
|
||||
navBarBackgroundColor: theme.sidebarHeaderBg,
|
||||
navBarButtonColor: theme.sidebarHeaderTextColor,
|
||||
screenBackgroundColor: theme.centerChannelBg
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
renderItems = () => {
|
||||
return [
|
||||
this.buildItemRow('gear', 'user.settings.modal.general', 'General', () => true, true, false),
|
||||
this.buildItemRow('lock', 'user.settings.modal.security', 'Security', () => true, true, false),
|
||||
this.buildItemRow('bell', 'user.settings.modal.notifications', 'Notifications', this.goToAccountNotifications, false, true),
|
||||
this.buildItemRow('mobile', 'user.settings.modal.display', 'Display', () => true, true, false),
|
||||
this.buildItemRow('wrench', 'user.settings.modal.advanced', 'Advanced', () => true, false, false)
|
||||
];
|
||||
};
|
||||
|
||||
render() {
|
||||
const {theme} = this.props;
|
||||
const style = getStyleSheet(theme);
|
||||
|
||||
return (
|
||||
<View style={style.wrapper}>
|
||||
<StatusBar/>
|
||||
<View style={style.container}>
|
||||
<View style={style.itemsContainer}>
|
||||
{this.renderItems()}
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const getStyleSheet = makeStyleSheetFromTheme((theme) => {
|
||||
return {
|
||||
container: {
|
||||
flex: 1,
|
||||
backgroundColor: changeOpacity(theme.centerChannelColor, 0.03)
|
||||
},
|
||||
item: {
|
||||
height: 45,
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center'
|
||||
},
|
||||
itemLeftIcon: {
|
||||
color: changeOpacity(theme.centerChannelColor, 0.5)
|
||||
},
|
||||
itemLeftIconContainer: {
|
||||
width: 18,
|
||||
marginRight: 15,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center'
|
||||
},
|
||||
itemText: {
|
||||
fontSize: 16,
|
||||
color: theme.centerChannelColor,
|
||||
flex: 1
|
||||
},
|
||||
itemRightIcon: {
|
||||
color: changeOpacity(theme.centerChannelColor, 0.5)
|
||||
},
|
||||
itemsContainer: {
|
||||
marginTop: 30,
|
||||
backgroundColor: theme.centerChannelBg,
|
||||
borderTopWidth: 1,
|
||||
borderBottomWidth: 1,
|
||||
borderTopColor: changeOpacity(theme.centerChannelColor, 0.1),
|
||||
borderBottomColor: changeOpacity(theme.centerChannelColor, 0.1)
|
||||
},
|
||||
itemWrapper: {
|
||||
marginHorizontal: 15
|
||||
},
|
||||
separator: {
|
||||
height: 1,
|
||||
backgroundColor: changeOpacity(theme.centerChannelColor, 0.1)
|
||||
},
|
||||
wrapper: {
|
||||
flex: 1,
|
||||
backgroundColor: theme.centerChannelBg
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
export default injectIntl(AccountSettings);
|
||||
|
|
@ -1,159 +0,0 @@
|
|||
// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
import React, {PureComponent} from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import {injectIntl, intlShape} from 'react-intl';
|
||||
import {
|
||||
Alert,
|
||||
TouchableOpacity,
|
||||
View
|
||||
} from 'react-native';
|
||||
import MaterialIcon from 'react-native-vector-icons/MaterialIcons';
|
||||
|
||||
import FormattedText from 'app/components/formatted_text';
|
||||
import StatusBar from 'app/components/status_bar';
|
||||
import {preventDoubleTap} from 'app/utils/tap';
|
||||
import {changeOpacity, makeStyleSheetFromTheme} from 'app/utils/theme';
|
||||
|
||||
class AdvancedSettings extends PureComponent {
|
||||
static propTypes = {
|
||||
actions: PropTypes.shape({
|
||||
purgeOfflineStore: PropTypes.func.isRequired
|
||||
}).isRequired,
|
||||
intl: intlShape.isRequired,
|
||||
theme: PropTypes.object
|
||||
};
|
||||
|
||||
buildItemRow = (icon, id, defaultMessage, action, separator = true, nextArrow = false) => {
|
||||
const {theme} = this.props;
|
||||
const style = getStyleSheet(theme);
|
||||
|
||||
return (
|
||||
<View
|
||||
key={id}
|
||||
style={style.itemWrapper}
|
||||
>
|
||||
<TouchableOpacity
|
||||
style={style.item}
|
||||
onPress={() => this.handlePress(action)}
|
||||
>
|
||||
<View style={style.itemLeftIconContainer}>
|
||||
<MaterialIcon
|
||||
name={icon}
|
||||
size={18}
|
||||
style={style.itemLeftIcon}
|
||||
/>
|
||||
</View>
|
||||
<FormattedText
|
||||
id={id}
|
||||
defaultMessage={defaultMessage}
|
||||
style={style.itemText}
|
||||
/>
|
||||
{nextArrow &&
|
||||
<MaterialIcon
|
||||
name='angle-right'
|
||||
size={18}
|
||||
style={style.itemRightIcon}
|
||||
/>
|
||||
}
|
||||
</TouchableOpacity>
|
||||
{separator && <View style={style.separator}/>}
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
clearOfflineCache = () => {
|
||||
const {actions, intl} = this.props;
|
||||
|
||||
Alert.alert(
|
||||
intl.formatMessage({id: 'mobile.advanced_settings.reset_title', defaultMessage: 'Reset Cache'}),
|
||||
intl.formatMessage({id: 'mobile.advanced_settings.reset_message', defaultMessage: '\nThis will reset all offline data and restart the app. You will be automatically logged back in once the app restarts.\n'}),
|
||||
[{
|
||||
text: intl.formatMessage({id: 'mobile.advanced_settings.reset_button', defaultMessage: 'Reset'}),
|
||||
onPress: () => actions.purgeOfflineStore()
|
||||
}, {
|
||||
text: intl.formatMessage({id: 'channel_modal.cancel', defaultMessage: 'Cancel'}),
|
||||
onPress: () => true
|
||||
}]
|
||||
);
|
||||
};
|
||||
|
||||
handlePress = (action) => {
|
||||
preventDoubleTap(action, this);
|
||||
};
|
||||
|
||||
renderItems = () => {
|
||||
return [
|
||||
this.buildItemRow('storage', 'mobile.advanced_settings.reset_title', 'Reset Cache', this.clearOfflineCache, false, false)
|
||||
];
|
||||
};
|
||||
|
||||
render() {
|
||||
const {theme} = this.props;
|
||||
const style = getStyleSheet(theme);
|
||||
|
||||
return (
|
||||
<View style={style.wrapper}>
|
||||
<StatusBar/>
|
||||
<View style={style.container}>
|
||||
<View style={style.itemsContainer}>
|
||||
{this.renderItems()}
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const getStyleSheet = makeStyleSheetFromTheme((theme) => {
|
||||
return {
|
||||
container: {
|
||||
flex: 1,
|
||||
backgroundColor: changeOpacity(theme.centerChannelColor, 0.03)
|
||||
},
|
||||
item: {
|
||||
height: 45,
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center'
|
||||
},
|
||||
itemLeftIcon: {
|
||||
color: changeOpacity(theme.centerChannelColor, 0.5)
|
||||
},
|
||||
itemLeftIconContainer: {
|
||||
width: 18,
|
||||
marginRight: 15,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center'
|
||||
},
|
||||
itemText: {
|
||||
fontSize: 16,
|
||||
color: theme.centerChannelColor,
|
||||
flex: 1
|
||||
},
|
||||
itemRightIcon: {
|
||||
color: changeOpacity(theme.centerChannelColor, 0.5)
|
||||
},
|
||||
itemsContainer: {
|
||||
marginTop: 30,
|
||||
backgroundColor: theme.centerChannelBg,
|
||||
borderTopWidth: 1,
|
||||
borderBottomWidth: 1,
|
||||
borderTopColor: changeOpacity(theme.centerChannelColor, 0.1),
|
||||
borderBottomColor: changeOpacity(theme.centerChannelColor, 0.1)
|
||||
},
|
||||
itemWrapper: {
|
||||
marginHorizontal: 15
|
||||
},
|
||||
separator: {
|
||||
height: 1,
|
||||
backgroundColor: changeOpacity(theme.centerChannelColor, 0.1)
|
||||
},
|
||||
wrapper: {
|
||||
flex: 1,
|
||||
backgroundColor: theme.centerChannelBg
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
export default injectIntl(AdvancedSettings);
|
||||
|
|
@ -5,10 +5,9 @@ import React from 'react';
|
|||
import {Navigation} from 'react-native-navigation';
|
||||
|
||||
import About from 'app/screens/about';
|
||||
import AccountSettings from 'app/screens/account_settings';
|
||||
import AccountNotifications from 'app/screens/account_notifications';
|
||||
|
||||
import AddReaction from 'app/screens/add_reaction';
|
||||
import AdvancedSettings from 'app/screens/advanced_settings';
|
||||
import AdvancedSettings from 'app/screens/settings/advanced_settings';
|
||||
import Channel from 'app/screens/channel';
|
||||
import ChannelAddMembers from 'app/screens/channel_add_members';
|
||||
import ChannelInfo from 'app/screens/channel_info';
|
||||
|
|
@ -24,13 +23,18 @@ import Mfa from 'app/screens/mfa';
|
|||
import MoreChannels from 'app/screens/more_channels';
|
||||
import MoreDirectMessages from 'app/screens/more_dms';
|
||||
import Notification from 'app/screens/notification';
|
||||
import NotificationSettings from 'app/screens/settings/notification_settings';
|
||||
import NotificationSettingsEmail from 'app/screens/settings/notification_settings_email';
|
||||
import NotificationSettingsMentions from 'app/screens/settings/notification_settings_mentions';
|
||||
import NotificationSettingsMentionsKeywords from 'app/screens/settings/notification_settings_mentions_keywords';
|
||||
import NotificationSettingsMobile from 'app/screens/settings/notification_settings_mobile';
|
||||
import OptionsModal from 'app/screens/options_modal';
|
||||
import Root from 'app/screens/root';
|
||||
import SSO from 'app/screens/sso';
|
||||
import Search from 'app/screens/search';
|
||||
import SelectServer from 'app/screens/select_server';
|
||||
import SelectTeam from 'app/screens/select_team';
|
||||
import Settings from 'app/screens/settings';
|
||||
import Settings from 'app/screens/settings/general';
|
||||
import Thread from 'app/screens/thread';
|
||||
import UserProfile from 'app/screens/user_profile';
|
||||
|
||||
|
|
@ -52,8 +56,6 @@ function wrapWithContextProvider(Comp, excludeEvents = false) {
|
|||
|
||||
export function registerScreens(store, Provider) {
|
||||
Navigation.registerComponent('About', () => wrapWithContextProvider(About), store, Provider);
|
||||
Navigation.registerComponent('AccountSettings', () => wrapWithContextProvider(AccountSettings), store, Provider);
|
||||
Navigation.registerComponent('AccountNotifications', () => wrapWithContextProvider(AccountNotifications), store, Provider);
|
||||
Navigation.registerComponent('AddReaction', () => wrapWithContextProvider(AddReaction), store, Provider);
|
||||
Navigation.registerComponent('AdvancedSettings', () => wrapWithContextProvider(AdvancedSettings), store, Provider);
|
||||
Navigation.registerComponent('Channel', () => wrapWithContextProvider(Channel), store, Provider);
|
||||
|
|
@ -72,6 +74,11 @@ export function registerScreens(store, Provider) {
|
|||
Navigation.registerComponent('MoreDirectMessages', () => wrapWithContextProvider(MoreDirectMessages), store, Provider);
|
||||
Navigation.registerComponent('OptionsModal', () => wrapWithContextProvider(OptionsModal), store, Provider);
|
||||
Navigation.registerComponent('Notification', () => wrapWithContextProvider(Notification, true), store, Provider);
|
||||
Navigation.registerComponent('NotificationSettings', () => wrapWithContextProvider(NotificationSettings), store, Provider);
|
||||
Navigation.registerComponent('NotificationSettingsEmail', () => wrapWithContextProvider(NotificationSettingsEmail), store, Provider);
|
||||
Navigation.registerComponent('NotificationSettingsMentions', () => wrapWithContextProvider(NotificationSettingsMentions), store, Provider);
|
||||
Navigation.registerComponent('NotificationSettingsMentionsKeywords', () => wrapWithContextProvider(NotificationSettingsMentionsKeywords), store, Provider);
|
||||
Navigation.registerComponent('NotificationSettingsMobile', () => wrapWithContextProvider(NotificationSettingsMobile), store, Provider);
|
||||
Navigation.registerComponent('Root', () => Root, store, Provider);
|
||||
Navigation.registerComponent('Search', () => wrapWithContextProvider(Search), store, Provider);
|
||||
Navigation.registerComponent('SelectServer', () => wrapWithContextProvider(SelectServer), store, Provider);
|
||||
|
|
|
|||
95
app/screens/settings/advanced_settings/advanced_settings.js
Normal file
95
app/screens/settings/advanced_settings/advanced_settings.js
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
import React, {PureComponent} from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import {injectIntl, intlShape} from 'react-intl';
|
||||
import {
|
||||
Alert,
|
||||
Platform,
|
||||
View
|
||||
} from 'react-native';
|
||||
|
||||
import SettingsItem from 'app/screens/settings/settings_item';
|
||||
import StatusBar from 'app/components/status_bar';
|
||||
import {preventDoubleTap} from 'app/utils/tap';
|
||||
import {changeOpacity, makeStyleSheetFromTheme} from 'app/utils/theme';
|
||||
|
||||
class AdvancedSettings extends PureComponent {
|
||||
static propTypes = {
|
||||
actions: PropTypes.shape({
|
||||
purgeOfflineStore: PropTypes.func.isRequired
|
||||
}).isRequired,
|
||||
intl: intlShape.isRequired,
|
||||
theme: PropTypes.object
|
||||
};
|
||||
|
||||
clearOfflineCache = () => {
|
||||
const {actions, intl} = this.props;
|
||||
|
||||
Alert.alert(
|
||||
intl.formatMessage({id: 'mobile.advanced_settings.reset_title', defaultMessage: 'Reset Cache'}),
|
||||
intl.formatMessage({id: 'mobile.advanced_settings.reset_message', defaultMessage: '\nThis will reset all offline data and restart the app. You will be automatically logged back in once the app restarts.\n'}),
|
||||
[{
|
||||
text: intl.formatMessage({id: 'mobile.advanced_settings.reset_button', defaultMessage: 'Reset'}),
|
||||
onPress: () => actions.purgeOfflineStore()
|
||||
}, {
|
||||
text: intl.formatMessage({id: 'channel_modal.cancel', defaultMessage: 'Cancel'}),
|
||||
onPress: () => true
|
||||
}]
|
||||
);
|
||||
};
|
||||
|
||||
handlePress = (action) => {
|
||||
preventDoubleTap(action, this);
|
||||
};
|
||||
|
||||
render() {
|
||||
const {theme} = this.props;
|
||||
const style = getStyleSheet(theme);
|
||||
|
||||
return (
|
||||
<View style={style.container}>
|
||||
<StatusBar/>
|
||||
<View style={style.wrapper}>
|
||||
<View style={style.divider}/>
|
||||
<SettingsItem
|
||||
defaultMessage='Reset Cache'
|
||||
i18nId='mobile.advanced_settings.reset_title'
|
||||
iconName='ios-refresh'
|
||||
iconType='ion'
|
||||
onPress={() => this.handlePress(this.clearOfflineCache)}
|
||||
separator={false}
|
||||
showArrow={false}
|
||||
theme={theme}
|
||||
/>
|
||||
<View style={style.divider}/>
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const getStyleSheet = makeStyleSheetFromTheme((theme) => {
|
||||
return {
|
||||
container: {
|
||||
flex: 1,
|
||||
backgroundColor: theme.centerChannelBg
|
||||
},
|
||||
wrapper: {
|
||||
backgroundColor: changeOpacity(theme.centerChannelColor, 0.06),
|
||||
flex: 1,
|
||||
...Platform.select({
|
||||
ios: {
|
||||
paddingTop: 35
|
||||
}
|
||||
})
|
||||
},
|
||||
divider: {
|
||||
backgroundColor: changeOpacity(theme.centerChannelColor, 0.1),
|
||||
height: 1
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
export default injectIntl(AdvancedSettings);
|
||||
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
import React, {PureComponent} from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import {injectIntl, intlShape} from 'react-intl';
|
||||
import {intlShape, injectIntl} from 'react-intl';
|
||||
import {
|
||||
InteractionManager,
|
||||
Linking,
|
||||
|
|
@ -12,13 +12,12 @@ import {
|
|||
} from 'react-native';
|
||||
import DeviceInfo from 'react-native-device-info';
|
||||
|
||||
import SettingsItem from 'app/screens/settings/settings_item';
|
||||
import StatusBar from 'app/components/status_bar';
|
||||
import {preventDoubleTap} from 'app/utils/tap';
|
||||
import {changeOpacity, makeStyleSheetFromTheme} from 'app/utils/theme';
|
||||
import {isValidUrl} from 'app/utils/url';
|
||||
|
||||
import SettingsItem from './settings_item';
|
||||
|
||||
class Settings extends PureComponent {
|
||||
static propTypes = {
|
||||
actions: PropTypes.shape({
|
||||
|
|
@ -82,13 +81,13 @@ class Settings extends PureComponent {
|
|||
});
|
||||
};
|
||||
|
||||
goToAccountSettings = () => {
|
||||
goToNotifications = () => {
|
||||
const {intl, navigator, theme} = this.props;
|
||||
navigator.push({
|
||||
screen: 'AccountSettings',
|
||||
title: intl.formatMessage({id: 'user.settings.modal.title', defaultMessage: 'Account Settings'}),
|
||||
animated: true,
|
||||
screen: 'NotificationSettings',
|
||||
backButtonTitle: '',
|
||||
title: intl.formatMessage({id: 'user.settings.modal.notifications', defaultMessage: 'Notifications'}),
|
||||
animated: true,
|
||||
navigatorStyle: {
|
||||
navBarTextColor: theme.sidebarHeaderTextColor,
|
||||
navBarBackgroundColor: theme.sidebarHeaderBg,
|
||||
|
|
@ -174,28 +173,30 @@ class Settings extends PureComponent {
|
|||
const style = getStyleSheet(theme);
|
||||
const showTeams = Object.keys(joinableTeams).length > 0;
|
||||
const showHelp = isValidUrl(config.HelpLink);
|
||||
const showArrow = Platform.OS === 'ios';
|
||||
|
||||
return (
|
||||
<View style={style.container}>
|
||||
<StatusBar/>
|
||||
<View style={style.wrapper}>
|
||||
<View style={style.divider}/>
|
||||
<SettingsItem
|
||||
defaultMessage='Account Settings'
|
||||
i18nId='sidebar_right_menu.accountSettings'
|
||||
iconName='cog'
|
||||
iconType='fontawesome'
|
||||
onPress={() => this.handlePress(this.goToAccountSettings)}
|
||||
separator={true}
|
||||
defaultMessage='Notifications'
|
||||
i18nId='user.settings.modal.notifications'
|
||||
iconName='ios-notifications'
|
||||
iconType='ion'
|
||||
onPress={() => this.handlePress(this.goToNotifications)}
|
||||
showArrow={showArrow}
|
||||
theme={theme}
|
||||
/>
|
||||
{showTeams &&
|
||||
<SettingsItem
|
||||
defaultMessage='Open teams you can join'
|
||||
i18nId='mobile.select_team.join_open'
|
||||
iconName='group'
|
||||
iconType='material'
|
||||
iconName='list'
|
||||
iconType='foundation'
|
||||
onPress={() => this.handlePress(this.goToSelectTeam)}
|
||||
separator={true}
|
||||
showArrow={showArrow}
|
||||
theme={theme}
|
||||
/>
|
||||
}
|
||||
|
|
@ -203,54 +204,56 @@ class Settings extends PureComponent {
|
|||
<SettingsItem
|
||||
defaultMessage='Help'
|
||||
i18nId='mobile.help.title'
|
||||
iconName='help'
|
||||
iconType='material'
|
||||
onPress={() => preventDoubleTap(this.openHelp, this)}
|
||||
separator={true}
|
||||
iconName='md-help'
|
||||
iconType='ion'
|
||||
onPress={() => this.handlePress(this.openHelp)}
|
||||
showArrow={showArrow}
|
||||
theme={theme}
|
||||
/>
|
||||
}
|
||||
<SettingsItem
|
||||
defaultMessage='Report a Problem'
|
||||
i18nId='sidebar_right_menu.report'
|
||||
iconName='warning'
|
||||
iconType='material'
|
||||
iconName='exclamation'
|
||||
iconType='fontawesome'
|
||||
onPress={() => this.handlePress(this.openErrorEmail)}
|
||||
separator={true}
|
||||
showArrow={showArrow}
|
||||
theme={theme}
|
||||
/>
|
||||
<SettingsItem
|
||||
defaultMessage='Advanced Settings'
|
||||
i18nId='mobile.advanced_settings.title'
|
||||
iconName='ios-construct'
|
||||
iconName='ios-hammer'
|
||||
iconType='ion'
|
||||
onPress={() => this.handlePress(this.goToAdvancedSettings)}
|
||||
separator={true}
|
||||
showArrow={showArrow}
|
||||
theme={theme}
|
||||
/>
|
||||
<SettingsItem
|
||||
defaultMessage='About Mattermost'
|
||||
i18nId='about.title'
|
||||
iconName='info-outline'
|
||||
iconType='material'
|
||||
iconName='ios-information-circle'
|
||||
iconType='ion'
|
||||
onPress={() => this.handlePress(this.goToAbout)}
|
||||
separator={false}
|
||||
showArrow={showArrow}
|
||||
theme={theme}
|
||||
/>
|
||||
<View style={style.divider}/>
|
||||
</View>
|
||||
<View style={style.footer}>
|
||||
<View style={style.divider}/>
|
||||
<SettingsItem
|
||||
defaultMessage='Logout'
|
||||
i18nId='sidebar_right_menu.logout'
|
||||
iconName='exit-to-app'
|
||||
iconType='material'
|
||||
isDestructor={true}
|
||||
onPress={() => this.handlePress(this.logout)}
|
||||
separator={false}
|
||||
theme={theme}
|
||||
/>
|
||||
<View style={style.footer}>
|
||||
<View style={style.divider}/>
|
||||
<SettingsItem
|
||||
centered={true}
|
||||
defaultMessage='Logout'
|
||||
i18nId='sidebar_right_menu.logout'
|
||||
isDestructor={true}
|
||||
onPress={() => this.handlePress(this.logout)}
|
||||
separator={false}
|
||||
showArrow={false}
|
||||
theme={theme}
|
||||
/>
|
||||
<View style={style.divider}/>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
|
|
@ -259,32 +262,25 @@ class Settings extends PureComponent {
|
|||
|
||||
const getStyleSheet = makeStyleSheetFromTheme((theme) => {
|
||||
return {
|
||||
leftComponent: {
|
||||
alignItems: 'center',
|
||||
flex: 1,
|
||||
justifyContent: 'center',
|
||||
width: 44
|
||||
},
|
||||
container: {
|
||||
flex: 1,
|
||||
backgroundColor: theme.centerChannelBg
|
||||
},
|
||||
wrapper: {
|
||||
backgroundColor: changeOpacity(theme.centerChannelColor, 0.03),
|
||||
flex: 1
|
||||
backgroundColor: changeOpacity(theme.centerChannelColor, 0.06),
|
||||
flex: 1,
|
||||
...Platform.select({
|
||||
ios: {
|
||||
paddingTop: 35
|
||||
}
|
||||
})
|
||||
},
|
||||
divider: {
|
||||
backgroundColor: changeOpacity(theme.centerChannelColor, 0.1),
|
||||
height: 1
|
||||
},
|
||||
footer: {
|
||||
height: 51,
|
||||
justifyContent: 'flex-end',
|
||||
...Platform.select({
|
||||
android: {
|
||||
marginBottom: 20
|
||||
}
|
||||
})
|
||||
marginTop: 35
|
||||
}
|
||||
};
|
||||
});
|
||||
|
|
@ -5,18 +5,19 @@ import {bindActionCreators} from 'redux';
|
|||
import {connect} from 'react-redux';
|
||||
|
||||
import {getCurrentUser} from 'mattermost-redux/selectors/entities/users';
|
||||
import {getMyPreferences} from 'mattermost-redux/selectors/entities/preferences';
|
||||
|
||||
import {handleUpdateUserNotifyProps} from 'app/actions/views/account_notifications';
|
||||
import {getTheme} from 'app/selectors/preferences';
|
||||
|
||||
import AccountNotifications from './account_notifications';
|
||||
import NotificationSettings from './notification_settings';
|
||||
|
||||
function mapStateToProps(state, ownProps) {
|
||||
return {
|
||||
...ownProps,
|
||||
config: state.entities.general.config,
|
||||
myPreferences: state.entities.preferences.myPreferences,
|
||||
currentUser: getCurrentUser(state),
|
||||
myPreferences: getMyPreferences(state),
|
||||
updateMeRequest: state.requests.users.updateMe,
|
||||
theme: getTheme(state)
|
||||
};
|
||||
|
|
@ -30,4 +31,4 @@ function mapDispatchToProps(dispatch) {
|
|||
};
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(AccountNotifications);
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(NotificationSettings);
|
||||
|
|
@ -0,0 +1,449 @@
|
|||
// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
import React, {PureComponent} from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import {injectIntl, intlShape} from 'react-intl';
|
||||
import {
|
||||
Alert,
|
||||
Modal,
|
||||
Platform,
|
||||
TouchableOpacity,
|
||||
View
|
||||
} from 'react-native';
|
||||
import deepEqual from 'deep-equal';
|
||||
|
||||
import {Preferences, RequestStatus} from 'mattermost-redux/constants';
|
||||
import {getPreferencesByCategory} from 'mattermost-redux/utils/preference_utils';
|
||||
|
||||
import FormattedText from 'app/components/formatted_text';
|
||||
import {RadioButton, RadioButtonGroup} from 'app/components/radio_button';
|
||||
import StatusBar from 'app/components/status_bar';
|
||||
import SettingsItem from 'app/screens/settings/settings_item';
|
||||
import {preventDoubleTap} from 'app/utils/tap';
|
||||
import {changeOpacity, makeStyleSheetFromTheme} from 'app/utils/theme';
|
||||
|
||||
class NotificationSettings extends PureComponent {
|
||||
static propTypes = {
|
||||
actions: PropTypes.shape({
|
||||
handleUpdateUserNotifyProps: PropTypes.func.isRequired
|
||||
}),
|
||||
config: PropTypes.object.isRequired,
|
||||
currentUser: PropTypes.object.isRequired,
|
||||
intl: intlShape.isRequired,
|
||||
myPreferences: PropTypes.object.isRequired,
|
||||
navigator: PropTypes.object,
|
||||
theme: PropTypes.object.isRequired,
|
||||
updateMeRequest: PropTypes.object.isRequired
|
||||
};
|
||||
|
||||
state = {
|
||||
showEmailNotificationsModal: false
|
||||
};
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
const {updateMeRequest, intl} = nextProps;
|
||||
if (this.props.updateMeRequest !== updateMeRequest && updateMeRequest.status === RequestStatus.FAILURE) {
|
||||
Alert.alert(
|
||||
intl.formatMessage({
|
||||
id: 'mobile.notification_settings.save_failed_title',
|
||||
defaultMessage: 'Connection issue'
|
||||
}),
|
||||
intl.formatMessage({
|
||||
id: 'mobile.notification_settings.save_failed_description',
|
||||
defaultMessage: 'The notification settings failed to save due to a connection issue, please try again.'
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
handlePress = (action) => {
|
||||
preventDoubleTap(action, this);
|
||||
};
|
||||
|
||||
goToNotificationSettingsEmail = () => {
|
||||
if (Platform.OS === 'ios') {
|
||||
const {currentUser, intl, navigator, theme} = this.props;
|
||||
navigator.push({
|
||||
backButtonTitle: '',
|
||||
screen: 'NotificationSettingsEmail',
|
||||
title: intl.formatMessage({
|
||||
id: 'mobile.notification_settings.email_title',
|
||||
defaultMessage: 'Email Notifications'
|
||||
}),
|
||||
animated: true,
|
||||
navigatorStyle: {
|
||||
navBarTextColor: theme.sidebarHeaderTextColor,
|
||||
navBarBackgroundColor: theme.sidebarHeaderBg,
|
||||
navBarButtonColor: theme.sidebarHeaderTextColor,
|
||||
screenBackgroundColor: theme.centerChannelBg
|
||||
},
|
||||
passProps: {
|
||||
currentUser,
|
||||
onBack: this.saveNotificationProps
|
||||
}
|
||||
});
|
||||
} else {
|
||||
this.setState({showEmailNotificationsModal: true});
|
||||
}
|
||||
};
|
||||
|
||||
goToNotificationSettingsMentions = () => {
|
||||
const {currentUser, intl, navigator, theme} = this.props;
|
||||
navigator.push({
|
||||
backButtonTitle: '',
|
||||
screen: 'NotificationSettingsMentions',
|
||||
title: intl.formatMessage({id: 'mobile.notification_settings.mentions_replies', defaultMessage: 'Mentions and Replies'}),
|
||||
animated: true,
|
||||
navigatorStyle: {
|
||||
navBarTextColor: theme.sidebarHeaderTextColor,
|
||||
navBarBackgroundColor: theme.sidebarHeaderBg,
|
||||
navBarButtonColor: theme.sidebarHeaderTextColor,
|
||||
screenBackgroundColor: theme.centerChannelBg
|
||||
},
|
||||
passProps: {
|
||||
currentUser,
|
||||
onBack: this.saveNotificationProps
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
goToNotificationSettingsMobile = () => {
|
||||
const {currentUser, intl, navigator, theme} = this.props;
|
||||
navigator.push({
|
||||
backButtonTitle: '',
|
||||
screen: 'NotificationSettingsMobile',
|
||||
title: intl.formatMessage({id: 'mobile.notification_settings.mobile_title', defaultMessage: 'Mobile Notifications'}),
|
||||
animated: true,
|
||||
navigatorStyle: {
|
||||
navBarTextColor: theme.sidebarHeaderTextColor,
|
||||
navBarBackgroundColor: theme.sidebarHeaderBg,
|
||||
navBarButtonColor: theme.sidebarHeaderTextColor,
|
||||
screenBackgroundColor: theme.centerChannelBg
|
||||
},
|
||||
passProps: {
|
||||
currentUser,
|
||||
onBack: this.saveNotificationProps
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
setEmailNotifications = (emailSetting) => {
|
||||
this.setState({emailSetting});
|
||||
};
|
||||
|
||||
saveEmailNotifications = () => {
|
||||
const {config, currentUser} = this.props;
|
||||
const {emailSetting} = this.state;
|
||||
|
||||
this.setState({showEmailNotificationsModal: false});
|
||||
|
||||
if (emailSetting) {
|
||||
let email = emailSetting;
|
||||
let interval;
|
||||
|
||||
const emailBatchingEnabled = config.EnableEmailBatching === 'true';
|
||||
if (emailBatchingEnabled && emailSetting !== 'false') {
|
||||
interval = emailSetting;
|
||||
email = 'true';
|
||||
}
|
||||
|
||||
this.saveNotificationProps({
|
||||
...currentUser.notify_props,
|
||||
email,
|
||||
interval
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
saveNotificationProps = (notifyProps) => {
|
||||
const {currentUser} = this.props;
|
||||
const {user_id} = notifyProps;
|
||||
const previousProps = {
|
||||
...currentUser.notify_props,
|
||||
user_id
|
||||
};
|
||||
|
||||
if (notifyProps.interval) {
|
||||
previousProps.interval = notifyProps.interval;
|
||||
}
|
||||
|
||||
if (!deepEqual(previousProps, notifyProps)) {
|
||||
this.props.actions.handleUpdateUserNotifyProps(notifyProps);
|
||||
}
|
||||
};
|
||||
|
||||
renderEmailNotificationSettings = (style) => {
|
||||
if (Platform.OS === 'ios') {
|
||||
return null;
|
||||
}
|
||||
|
||||
const {config, currentUser, intl, myPreferences} = this.props;
|
||||
const notifyProps = currentUser.notify_props || {};
|
||||
const sendEmailNotifications = config.SendEmailNotifications === 'true';
|
||||
const emailBatchingEnabled = sendEmailNotifications && config.EnableEmailBatching === 'true';
|
||||
|
||||
const never = notifyProps.email === 'false';
|
||||
let sendImmediatley = notifyProps.email === 'true';
|
||||
let sendImmediatleyValue = 'true';
|
||||
let fifteenMinutes;
|
||||
let hourly;
|
||||
let interval;
|
||||
|
||||
if (emailBatchingEnabled) {
|
||||
const emailPreferences = getPreferencesByCategory(myPreferences, Preferences.CATEGORY_NOTIFICATIONS);
|
||||
if (emailPreferences.size) {
|
||||
interval = emailPreferences.get(Preferences.EMAIL_INTERVAL).value;
|
||||
}
|
||||
}
|
||||
|
||||
if (emailBatchingEnabled && notifyProps.email !== 'false') {
|
||||
sendImmediatley = interval === Preferences.INTERVAL_IMMEDIATE.toString();
|
||||
fifteenMinutes = interval === Preferences.INTERVAL_FIFTEEN_MINUTES.toString();
|
||||
hourly = interval === Preferences.INTERVAL_HOUR.toString();
|
||||
sendImmediatleyValue = Preferences.INTERVAL_IMMEDIATE.toString();
|
||||
}
|
||||
|
||||
let helpText;
|
||||
if (sendEmailNotifications) {
|
||||
helpText = (
|
||||
<FormattedText
|
||||
id='user.settings.notifications.emailInfo'
|
||||
defaultMessage='Email notifications are sent for mentions and direct messages when you are offline or away from {siteName} for more than 5 minutes.'
|
||||
values={{siteName: config.SiteName}}
|
||||
style={style.modalHelpText}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Modal
|
||||
animationType='slide'
|
||||
transparent={true}
|
||||
visible={this.state.showEmailNotificationsModal}
|
||||
onRequestClose={() => this.setState({showEmailNotificationsModal: false})}
|
||||
>
|
||||
<View style={style.modalOverlay}>
|
||||
<View style={style.modal}>
|
||||
<View style={style.modalBody}>
|
||||
<View style={style.modalTitleContainer}>
|
||||
<FormattedText
|
||||
id='user.settings.notifications.email.send'
|
||||
defaultMessage='Send email notifications'
|
||||
style={style.modalTitle}
|
||||
/>
|
||||
</View>
|
||||
{!sendEmailNotifications &&
|
||||
<FormattedText
|
||||
id='user.settings.general.emailHelp2'
|
||||
defaultMessage='Email has been disabled by your System Administrator. No notification emails will be sent until it is enabled.'
|
||||
style={style.modalOptionDisabled}
|
||||
/>
|
||||
}
|
||||
{sendEmailNotifications &&
|
||||
<RadioButtonGroup
|
||||
name='emailSettings'
|
||||
onSelect={this.setEmailNotifications}
|
||||
>
|
||||
<RadioButton
|
||||
label={intl.formatMessage({
|
||||
id: 'user.settings.notifications.email.immediately',
|
||||
defaultMessage: 'Immediately'
|
||||
})}
|
||||
value={sendImmediatleyValue}
|
||||
checked={sendImmediatley}
|
||||
/>
|
||||
{emailBatchingEnabled &&
|
||||
<RadioButton
|
||||
label={intl.formatMessage({
|
||||
id: 'user.settings.notifications.email.everyXMinutes',
|
||||
defaultMessage: 'Every {count, plural, one {minute} other {{count, number} minutes}}'
|
||||
}, {count: Preferences.INTERVAL_FIFTEEN_MINUTES / 60})}
|
||||
value={Preferences.INTERVAL_FIFTEEN_MINUTES.toString()}
|
||||
checked={fifteenMinutes}
|
||||
/>
|
||||
}
|
||||
{emailBatchingEnabled &&
|
||||
<RadioButton
|
||||
label={intl.formatMessage({
|
||||
id: 'user.settings.notifications.email.everyHour',
|
||||
defaultMessage: 'Every hour'
|
||||
})}
|
||||
value={Preferences.INTERVAL_HOUR.toString()}
|
||||
checked={hourly}
|
||||
/>
|
||||
}
|
||||
<RadioButton
|
||||
label={intl.formatMessage({
|
||||
id: 'user.settings.notifications.email.never',
|
||||
defaultMessage: 'Never'
|
||||
})}
|
||||
value='false'
|
||||
checked={never}
|
||||
/>
|
||||
</RadioButtonGroup>
|
||||
}
|
||||
{helpText}
|
||||
</View>
|
||||
<View style={style.modalFooter}>
|
||||
<View style={style.divider}/>
|
||||
<View style={style.modalFooterContainer}>
|
||||
<TouchableOpacity
|
||||
style={style.modalFooterOptionContainer}
|
||||
onPress={this.saveEmailNotifications}
|
||||
>
|
||||
<FormattedText
|
||||
id='mobile.notification_settings.modal_cancel'
|
||||
defaultMessage='CANCEL'
|
||||
style={style.modalFooterOption}
|
||||
/>
|
||||
</TouchableOpacity>
|
||||
{sendEmailNotifications &&
|
||||
<View>
|
||||
<View style={{marginRight: 10}}/>
|
||||
<TouchableOpacity
|
||||
style={style.modalFooterOptionContainer}
|
||||
onPress={this.saveEmailNotifications}
|
||||
>
|
||||
<FormattedText
|
||||
id='mobile.notification_settings.modal_save'
|
||||
defaultMessage='SAVE'
|
||||
style={style.modalFooterOption}
|
||||
/>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
}
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
||||
render() {
|
||||
const {theme} = this.props;
|
||||
const style = getStyleSheet(theme);
|
||||
const showArrow = Platform.OS === 'ios';
|
||||
|
||||
return (
|
||||
<View style={style.container}>
|
||||
<StatusBar/>
|
||||
<View style={style.wrapper}>
|
||||
<View style={style.divider}/>
|
||||
<SettingsItem
|
||||
defaultMessage='Mentions and Replies'
|
||||
i18nId='mobile.notification_settings.mentions_replies'
|
||||
iconName='md-at'
|
||||
iconType='ion'
|
||||
onPress={() => this.handlePress(this.goToNotificationSettingsMentions)}
|
||||
separator={true}
|
||||
showArrow={showArrow}
|
||||
theme={theme}
|
||||
/>
|
||||
<SettingsItem
|
||||
defaultMessage='Mobile'
|
||||
i18nId='mobile.notification_settings.mobile'
|
||||
iconName='md-phone-portrait'
|
||||
iconType='ion'
|
||||
onPress={() => this.handlePress(this.goToNotificationSettingsMobile)}
|
||||
separator={true}
|
||||
showArrow={showArrow}
|
||||
theme={theme}
|
||||
/>
|
||||
<SettingsItem
|
||||
defaultMessage='Email'
|
||||
i18nId='mobile.notification_settings.email'
|
||||
iconName='ios-mail'
|
||||
iconType='ion'
|
||||
onPress={() => this.handlePress(this.goToNotificationSettingsEmail)}
|
||||
separator={false}
|
||||
showArrow={showArrow}
|
||||
theme={theme}
|
||||
/>
|
||||
<View style={style.divider}/>
|
||||
</View>
|
||||
{this.renderEmailNotificationSettings(style)}
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const getStyleSheet = makeStyleSheetFromTheme((theme) => {
|
||||
return {
|
||||
container: {
|
||||
flex: 1,
|
||||
backgroundColor: theme.centerChannelBg
|
||||
},
|
||||
wrapper: {
|
||||
backgroundColor: changeOpacity(theme.centerChannelColor, 0.06),
|
||||
flex: 1,
|
||||
...Platform.select({
|
||||
ios: {
|
||||
paddingTop: 35
|
||||
}
|
||||
})
|
||||
},
|
||||
divider: {
|
||||
backgroundColor: changeOpacity(theme.centerChannelColor, 0.1),
|
||||
height: 1,
|
||||
width: '100%'
|
||||
},
|
||||
modalOverlay: {
|
||||
backgroundColor: changeOpacity('#000000', 0.6),
|
||||
alignItems: 'center',
|
||||
flex: 1
|
||||
},
|
||||
modal: {
|
||||
backgroundColor: theme.centerChannelBg,
|
||||
borderRadius: 4,
|
||||
marginTop: 20,
|
||||
width: '95%'
|
||||
},
|
||||
modalBody: {
|
||||
paddingHorizontal: 24
|
||||
},
|
||||
modalTitleContainer: {
|
||||
marginBottom: 30,
|
||||
marginTop: 20
|
||||
},
|
||||
modalTitle: {
|
||||
color: theme.centerChannelColor,
|
||||
fontSize: 19
|
||||
},
|
||||
modalOptionDisabled: {
|
||||
color: changeOpacity(theme.centerChannelColor, 0.5),
|
||||
fontSize: 17
|
||||
},
|
||||
modalHelpText: {
|
||||
color: changeOpacity(theme.centerChannelColor, 0.5),
|
||||
fontSize: 13,
|
||||
marginTop: 20
|
||||
},
|
||||
modalFooter: {
|
||||
alignItems: 'flex-end',
|
||||
height: 58,
|
||||
marginTop: 40,
|
||||
width: '100%'
|
||||
},
|
||||
modalFooterContainer: {
|
||||
alignItems: 'center',
|
||||
flex: 1,
|
||||
flexDirection: 'row',
|
||||
paddingRight: 24
|
||||
},
|
||||
modalFooterOptionContainer: {
|
||||
alignItems: 'center',
|
||||
height: 40,
|
||||
justifyContent: 'center',
|
||||
paddingHorizontal: 10,
|
||||
paddingVertical: 5
|
||||
},
|
||||
modalFooterOption: {
|
||||
color: theme.linkColor,
|
||||
fontSize: 14
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
export default injectIntl(NotificationSettings);
|
||||
22
app/screens/settings/notification_settings_email/index.js
Normal file
22
app/screens/settings/notification_settings_email/index.js
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
import {connect} from 'react-redux';
|
||||
|
||||
import {getConfig} from 'mattermost-redux/selectors/entities/general';
|
||||
import {getMyPreferences} from 'mattermost-redux/selectors/entities/preferences';
|
||||
|
||||
import {getTheme} from 'app/selectors/preferences';
|
||||
|
||||
import NotificationSettingsEmail from './notification_settings_email';
|
||||
|
||||
function mapStateToProps(state, ownProps) {
|
||||
return {
|
||||
...ownProps,
|
||||
config: getConfig(state),
|
||||
myPreferences: getMyPreferences(state),
|
||||
theme: getTheme(state)
|
||||
};
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps)(NotificationSettingsEmail);
|
||||
|
|
@ -0,0 +1,249 @@
|
|||
// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
import React, {PureComponent} from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import {
|
||||
ScrollView,
|
||||
View
|
||||
} from 'react-native';
|
||||
|
||||
import {Preferences} from 'mattermost-redux/constants';
|
||||
import {getPreferencesByCategory} from 'mattermost-redux/utils/preference_utils';
|
||||
|
||||
import FormattedText from 'app/components/formatted_text';
|
||||
import StatusBar from 'app/components/status_bar';
|
||||
import {changeOpacity, makeStyleSheetFromTheme} from 'app/utils/theme';
|
||||
|
||||
import Section from 'app/screens/settings/section';
|
||||
import SectionItem from 'app/screens/settings/section_item';
|
||||
|
||||
export default class NotificationSettingsEmail extends PureComponent {
|
||||
static propTypes = {
|
||||
config: PropTypes.object.isRequired,
|
||||
currentUser: PropTypes.object.isRequired,
|
||||
myPreferences: PropTypes.object.isRequired,
|
||||
navigator: PropTypes.object,
|
||||
onBack: PropTypes.func.isRequired,
|
||||
theme: PropTypes.object.isRequired
|
||||
};
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
const {currentUser} = props;
|
||||
const notifyProps = currentUser.notify_props || {};
|
||||
|
||||
props.navigator.setOnNavigatorEvent(this.onNavigatorEvent);
|
||||
this.setStateFromNotifyProps(notifyProps);
|
||||
}
|
||||
|
||||
onNavigatorEvent = (event) => {
|
||||
if (event.type === 'ScreenChangedEvent') {
|
||||
switch (event.id) {
|
||||
case 'willDisappear':
|
||||
this.saveUserNotifyProps();
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
setEmailNotifications = (value) => {
|
||||
const {config} = this.props;
|
||||
let email = value;
|
||||
let interval;
|
||||
|
||||
const emailBatchingEnabled = config.EnableEmailBatching === 'true';
|
||||
if (emailBatchingEnabled && value !== 'false') {
|
||||
interval = value;
|
||||
email = 'true';
|
||||
}
|
||||
|
||||
this.setState({
|
||||
email,
|
||||
interval
|
||||
});
|
||||
};
|
||||
|
||||
setStateFromNotifyProps = (notifyProps) => {
|
||||
const {config, myPreferences} = this.props;
|
||||
let interval;
|
||||
if (config.SendEmailNotifications === 'true' && config.EnableEmailBatching === 'true') {
|
||||
const emailPreferences = getPreferencesByCategory(myPreferences, Preferences.CATEGORY_NOTIFICATIONS);
|
||||
if (emailPreferences.size) {
|
||||
interval = emailPreferences.get(Preferences.EMAIL_INTERVAL).value;
|
||||
}
|
||||
}
|
||||
|
||||
const newState = {
|
||||
...notifyProps,
|
||||
interval
|
||||
};
|
||||
|
||||
this.state = {...newState};
|
||||
};
|
||||
|
||||
saveUserNotifyProps = () => {
|
||||
this.props.onBack({
|
||||
...this.state,
|
||||
user_id: this.props.currentUser.id
|
||||
});
|
||||
};
|
||||
|
||||
renderEmailSection = () => {
|
||||
const {config, theme} = this.props;
|
||||
const style = getStyleSheet(theme);
|
||||
|
||||
const sendEmailNotifications = config.SendEmailNotifications === 'true';
|
||||
const emailBatchingEnabled = sendEmailNotifications && config.EnableEmailBatching === 'true';
|
||||
|
||||
let sendImmediatley = this.state.email === 'true';
|
||||
let sendImmediatleyValue = 'true';
|
||||
let fifteenMinutes;
|
||||
let hourly;
|
||||
const never = this.state.email === 'false';
|
||||
|
||||
if (emailBatchingEnabled && this.state.email !== 'false') {
|
||||
sendImmediatley = this.state.interval === Preferences.INTERVAL_IMMEDIATE.toString();
|
||||
fifteenMinutes = this.state.interval === Preferences.INTERVAL_FIFTEEN_MINUTES.toString();
|
||||
hourly = this.state.interval === Preferences.INTERVAL_HOUR.toString();
|
||||
|
||||
sendImmediatleyValue = Preferences.INTERVAL_IMMEDIATE.toString();
|
||||
}
|
||||
|
||||
return (
|
||||
<Section
|
||||
headerId='mobile.notification_settings.email.send'
|
||||
headerDefaultMessage='SEND EMAIL NOTIFICATIONS'
|
||||
footerId='user.settings.notifications.emailInfo'
|
||||
footerDefaultMessage='Email notifications are sent for mentions and direct messages when you are offline or away from {siteName} for more than 5 minutes.'
|
||||
footerValues={{siteName: config.SiteName}}
|
||||
disableFooter={!sendEmailNotifications}
|
||||
theme={theme}
|
||||
>
|
||||
{sendEmailNotifications &&
|
||||
<View>
|
||||
<SectionItem
|
||||
label={(
|
||||
<FormattedText
|
||||
id='user.settings.notifications.email.immediately'
|
||||
defaultMessage='Immediately'
|
||||
/>
|
||||
)}
|
||||
action={this.setEmailNotifications}
|
||||
actionType='select'
|
||||
actionValue={sendImmediatleyValue}
|
||||
selected={sendImmediatley}
|
||||
theme={theme}
|
||||
/>
|
||||
<View style={style.separator}/>
|
||||
{emailBatchingEnabled &&
|
||||
<View>
|
||||
<SectionItem
|
||||
label={(
|
||||
<FormattedText
|
||||
id='user.settings.notifications.email.everyXMinutes'
|
||||
defaultMessage='Every {count, plural, one {minute} other {{count, number} minutes}}'
|
||||
values={{count: Preferences.INTERVAL_FIFTEEN_MINUTES / 60}}
|
||||
/>
|
||||
)}
|
||||
action={this.setEmailNotifications}
|
||||
actionType='select'
|
||||
actionValue={Preferences.INTERVAL_FIFTEEN_MINUTES.toString()}
|
||||
selected={fifteenMinutes}
|
||||
theme={theme}
|
||||
/>
|
||||
<View style={style.separator}/>
|
||||
<SectionItem
|
||||
label={(
|
||||
<FormattedText
|
||||
id='user.settings.notifications.email.everyHour'
|
||||
defaultMessage='Every hour'
|
||||
/>
|
||||
)}
|
||||
action={this.setEmailNotifications}
|
||||
actionType='select'
|
||||
actionValue={Preferences.INTERVAL_HOUR.toString()}
|
||||
selected={hourly}
|
||||
theme={theme}
|
||||
/>
|
||||
<View style={style.separator}/>
|
||||
</View>
|
||||
}
|
||||
<SectionItem
|
||||
label={(
|
||||
<FormattedText
|
||||
id='user.settings.notifications.email.never'
|
||||
defaultMessage='Never'
|
||||
/>
|
||||
)}
|
||||
action={this.setEmailNotifications}
|
||||
actionType='select'
|
||||
actionValue='false'
|
||||
selected={never}
|
||||
theme={theme}
|
||||
/>
|
||||
</View>
|
||||
}
|
||||
{!sendEmailNotifications &&
|
||||
<FormattedText
|
||||
id='user.settings.general.emailHelp2'
|
||||
defaultMessage='Email has been disabled by your System Administrator. No notification emails will be sent until it is enabled.'
|
||||
style={style.disabled}
|
||||
/>
|
||||
}
|
||||
</Section>
|
||||
);
|
||||
};
|
||||
|
||||
render() {
|
||||
const {theme} = this.props;
|
||||
const style = getStyleSheet(theme);
|
||||
|
||||
return (
|
||||
<View style={style.container}>
|
||||
<StatusBar/>
|
||||
<ScrollView
|
||||
style={style.scrollView}
|
||||
contentContainerStyle={style.scrollViewContent}
|
||||
>
|
||||
{this.renderEmailSection()}
|
||||
</ScrollView>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const getStyleSheet = makeStyleSheetFromTheme((theme) => {
|
||||
return {
|
||||
container: {
|
||||
flex: 1,
|
||||
backgroundColor: theme.centerChannelBg
|
||||
},
|
||||
input: {
|
||||
color: theme.centerChannelColor,
|
||||
fontSize: 12,
|
||||
height: 40
|
||||
},
|
||||
separator: {
|
||||
backgroundColor: changeOpacity(theme.centerChannelColor, 0.1),
|
||||
flex: 1,
|
||||
height: 1,
|
||||
marginLeft: 15
|
||||
},
|
||||
scrollView: {
|
||||
flex: 1,
|
||||
backgroundColor: changeOpacity(theme.centerChannelColor, 0.06)
|
||||
},
|
||||
scrollViewContent: {
|
||||
paddingVertical: 35
|
||||
},
|
||||
disabled: {
|
||||
color: theme.centerChannelColor,
|
||||
fontSize: 15,
|
||||
paddingHorizontal: 15,
|
||||
paddingVertical: 10
|
||||
}
|
||||
};
|
||||
});
|
||||
|
|
@ -5,12 +5,13 @@ import {connect} from 'react-redux';
|
|||
|
||||
import {getTheme} from 'app/selectors/preferences';
|
||||
|
||||
import AccountSettings from './account_settings';
|
||||
import NotificationSettingsMentions from './notification_settings_mentions';
|
||||
|
||||
function mapStateToProps(state) {
|
||||
function mapStateToProps(state, ownProps) {
|
||||
return {
|
||||
...ownProps,
|
||||
theme: getTheme(state)
|
||||
};
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps)(AccountSettings);
|
||||
export default connect(mapStateToProps)(NotificationSettingsMentions);
|
||||
|
|
@ -0,0 +1,118 @@
|
|||
// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
import {PureComponent} from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import {intlShape} from 'react-intl';
|
||||
|
||||
export default class NotificationSettingsMentionsBase extends PureComponent {
|
||||
static propTypes = {
|
||||
currentUser: PropTypes.object.isRequired,
|
||||
intl: intlShape.isRequired,
|
||||
navigator: PropTypes.object,
|
||||
onBack: PropTypes.func.isRequired,
|
||||
theme: PropTypes.object.isRequired
|
||||
};
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
const {currentUser} = props;
|
||||
const notifyProps = currentUser.notify_props || {};
|
||||
|
||||
props.navigator.setOnNavigatorEvent(this.onNavigatorEvent);
|
||||
|
||||
this.goingBack = true; //use to identify if the navigator is popping this screen
|
||||
this.setStateFromNotifyProps(notifyProps);
|
||||
}
|
||||
|
||||
onNavigatorEvent = (event) => {
|
||||
if (event.type === 'ScreenChangedEvent' && this.goingBack) {
|
||||
switch (event.id) {
|
||||
case 'willDisappear':
|
||||
this.saveUserNotifyProps();
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
setStateFromNotifyProps = (notifyProps) => {
|
||||
const mentionKeys = (notifyProps.mention_keys || '').split(',');
|
||||
const usernameMentionIndex = mentionKeys.indexOf(this.props.currentUser.username);
|
||||
if (usernameMentionIndex > -1) {
|
||||
mentionKeys.splice(usernameMentionIndex, 1);
|
||||
}
|
||||
|
||||
const comments = notifyProps.comments || 'never';
|
||||
|
||||
const newState = {
|
||||
...notifyProps,
|
||||
comments,
|
||||
usernameMention: usernameMentionIndex > -1,
|
||||
mention_keys: mentionKeys.join(','),
|
||||
showKeywordsModal: false,
|
||||
showReplyModal: false
|
||||
};
|
||||
|
||||
this.keywords = newState.mention_keys;
|
||||
this.replyValue = comments;
|
||||
this.state = {...newState};
|
||||
};
|
||||
|
||||
toggleFirstNameMention = () => {
|
||||
this.setState({
|
||||
first_name: (!(this.state.first_name === 'true')).toString()
|
||||
});
|
||||
};
|
||||
|
||||
toggleUsernameMention = () => {
|
||||
this.setState({
|
||||
usernameMention: !this.state.usernameMention
|
||||
});
|
||||
};
|
||||
|
||||
toggleChannelMentions = () => {
|
||||
this.setState({
|
||||
channel: (!(this.state.channel === 'true')).toString()
|
||||
});
|
||||
};
|
||||
|
||||
updateMentionKeys = (text) => {
|
||||
this.goingBack = true;
|
||||
this.setState({
|
||||
mention_keys: text,
|
||||
showKeywordsModal: false
|
||||
});
|
||||
};
|
||||
|
||||
setReplyNotifications = (value) => {
|
||||
this.setState({
|
||||
comments: value,
|
||||
showReplyModal: false
|
||||
});
|
||||
};
|
||||
|
||||
saveUserNotifyProps = () => {
|
||||
let {mention_keys: mentionKeys, usernameMention, ...notifyProps} = this.state; //eslint-disable-line prefer-const
|
||||
|
||||
if (mentionKeys.length > 0) {
|
||||
mentionKeys = mentionKeys.split(',').map((m) => m.replace(/\s/g, ''));
|
||||
} else {
|
||||
mentionKeys = [];
|
||||
}
|
||||
|
||||
if (usernameMention) {
|
||||
mentionKeys.push(`${this.props.currentUser.username}`);
|
||||
}
|
||||
|
||||
mentionKeys = mentionKeys.join(',');
|
||||
Reflect.deleteProperty(notifyProps, 'showKeywordsModal');
|
||||
Reflect.deleteProperty(notifyProps, 'showReplyModal');
|
||||
|
||||
this.props.onBack({
|
||||
...notifyProps,
|
||||
mention_keys: mentionKeys,
|
||||
user_id: this.props.currentUser.id
|
||||
});
|
||||
};
|
||||
}
|
||||
|
|
@ -0,0 +1,445 @@
|
|||
// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
import React from 'react';
|
||||
import {
|
||||
Modal,
|
||||
ScrollView,
|
||||
Text,
|
||||
TouchableOpacity,
|
||||
View
|
||||
} from 'react-native';
|
||||
import {injectIntl} from 'react-intl';
|
||||
|
||||
import FormattedText from 'app/components/formatted_text';
|
||||
import {RadioButton, RadioButtonGroup} from 'app/components/radio_button';
|
||||
import StatusBar from 'app/components/status_bar';
|
||||
import TextInputWithLocalizedPlaceholder from 'app/components/text_input_with_localized_placeholder';
|
||||
import SectionItem from 'app/screens/settings/section_item';
|
||||
import {changeOpacity, makeStyleSheetFromTheme} from 'app/utils/theme';
|
||||
|
||||
import NotificationSettingsMentionsBase from './notification_settings_mention_base';
|
||||
|
||||
class NotificationSettingsMentionsIos extends NotificationSettingsMentionsBase {
|
||||
cancelMentionKeys = () => {
|
||||
this.setState({showKeywordsModal: false});
|
||||
this.keywords = this.state.mention_keys;
|
||||
};
|
||||
|
||||
cancelReplyNotification = () => {
|
||||
this.setState({showReplyModal: false});
|
||||
this.replyValue = this.state.comments;
|
||||
};
|
||||
|
||||
onKeywordsChangeText = (value) => {
|
||||
this.keywords = value;
|
||||
};
|
||||
|
||||
onReplyChanged = (value) => {
|
||||
this.replyValue = value;
|
||||
};
|
||||
|
||||
renderKeywordsModal(style) {
|
||||
const {theme} = this.props;
|
||||
|
||||
return (
|
||||
<Modal
|
||||
animationType='slide'
|
||||
transparent={true}
|
||||
visible={this.state.showKeywordsModal}
|
||||
onRequestClose={this.cancelMentionKeys}
|
||||
>
|
||||
<View style={style.modalOverlay}>
|
||||
<View style={style.modal}>
|
||||
<View style={style.modalBody}>
|
||||
<View style={style.modalTitleContainer}>
|
||||
<FormattedText
|
||||
id='user.settings.notifications.email.send'
|
||||
defaultMessage='Send email notifications'
|
||||
style={style.modalTitle}
|
||||
/>
|
||||
</View>
|
||||
<TextInputWithLocalizedPlaceholder
|
||||
autoFocus={true}
|
||||
defaultValue={this.keywords}
|
||||
blurOnSubmit={false}
|
||||
onChangeText={this.onKeywordsChangeText}
|
||||
multiline={false}
|
||||
style={style.input}
|
||||
autoCapitalize='none'
|
||||
autoCorrect={false}
|
||||
placeholder={{id: 'mobile.notification_settings_mentions.keywordsDescription', defaultMessage: 'Other words that trigger a mention'}}
|
||||
placeholderTextColor={changeOpacity(theme.centerChannelColor, 0.4)}
|
||||
returnKeyType='done'
|
||||
underlineColorAndroid={theme.linkColor}
|
||||
/>
|
||||
<FormattedText
|
||||
id='mobile.notification_settings_mentions.keywordsHelp'
|
||||
defaultMessage='Keywords are non-case sensitive and should be separated by a comma.'
|
||||
style={style.modalHelpText}
|
||||
/>
|
||||
</View>
|
||||
<View style={style.modalFooter}>
|
||||
<View style={style.separator}/>
|
||||
<View style={style.modalFooterContainer}>
|
||||
<TouchableOpacity
|
||||
style={style.modalFooterOptionContainer}
|
||||
onPress={this.cancelMentionKeys}
|
||||
>
|
||||
<FormattedText
|
||||
id='mobile.notification_settings.modal_cancel'
|
||||
defaultMessage='CANCEL'
|
||||
style={style.modalFooterOption}
|
||||
/>
|
||||
</TouchableOpacity>
|
||||
<View style={{marginRight: 10}}/>
|
||||
<TouchableOpacity
|
||||
style={style.modalFooterOptionContainer}
|
||||
onPress={this.saveMentionKeys}
|
||||
>
|
||||
<FormattedText
|
||||
id='mobile.notification_settings.modal_save'
|
||||
defaultMessage='SAVE'
|
||||
style={style.modalFooterOption}
|
||||
/>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
||||
renderReplyModal(style) {
|
||||
const {intl} = this.props;
|
||||
|
||||
return (
|
||||
<Modal
|
||||
animationType='slide'
|
||||
transparent={true}
|
||||
visible={this.state.showReplyModal}
|
||||
onRequestClose={this.cancelReplyNotification}
|
||||
>
|
||||
<View style={style.modalOverlay}>
|
||||
<View style={style.modal}>
|
||||
<View style={style.modalBody}>
|
||||
<View style={style.modalTitleContainer}>
|
||||
<FormattedText
|
||||
id='mobile.notification_settings.mentions.reply_title'
|
||||
defaultMessage='Send Reply notifications for'
|
||||
style={style.modalTitle}
|
||||
/>
|
||||
</View>
|
||||
<RadioButtonGroup
|
||||
name='replySettings'
|
||||
onSelect={this.onReplyChanged}
|
||||
>
|
||||
<RadioButton
|
||||
label={intl.formatMessage({
|
||||
id: 'mobile.account_notifications.threads_start_participate',
|
||||
defaultMessage: 'Threads that I start or participate in'
|
||||
})}
|
||||
value='any'
|
||||
checked={this.state.comments === 'any'}
|
||||
/>
|
||||
<RadioButton
|
||||
label={intl.formatMessage({
|
||||
id: 'mobile.account_notifications.threads_start',
|
||||
defaultMessage: 'Threads that I start'
|
||||
})}
|
||||
value='root'
|
||||
checked={this.state.comments === 'root'}
|
||||
/>
|
||||
<RadioButton
|
||||
label={intl.formatMessage({
|
||||
id: 'mobile.account_notifications.threads_mentions',
|
||||
defaultMessage: 'Mentions in threads'
|
||||
})}
|
||||
value='never'
|
||||
checked={this.state.comments === 'never'}
|
||||
/>
|
||||
</RadioButtonGroup>
|
||||
</View>
|
||||
<View style={style.modalFooter}>
|
||||
<View style={style.separator}/>
|
||||
<View style={style.modalFooterContainer}>
|
||||
<TouchableOpacity
|
||||
style={style.modalFooterOptionContainer}
|
||||
onPress={this.cancelReplyNotification}
|
||||
>
|
||||
<FormattedText
|
||||
id='mobile.notification_settings.modal_cancel'
|
||||
defaultMessage='CANCEL'
|
||||
style={style.modalFooterOption}
|
||||
/>
|
||||
</TouchableOpacity>
|
||||
<View>
|
||||
<View style={{marginRight: 10}}/>
|
||||
<TouchableOpacity
|
||||
style={style.modalFooterOptionContainer}
|
||||
onPress={this.saveReplyNotification}
|
||||
>
|
||||
<FormattedText
|
||||
id='mobile.notification_settings.modal_save'
|
||||
defaultMessage='SAVE'
|
||||
style={style.modalFooterOption}
|
||||
/>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
||||
renderReplySection() {
|
||||
const {theme} = this.props;
|
||||
|
||||
let i18nId;
|
||||
let i18nMessage;
|
||||
switch (this.state.comments) {
|
||||
case 'any':
|
||||
i18nId = 'mobile.account_notifications.threads_start_participate';
|
||||
i18nMessage = 'Threads that I start or participate in';
|
||||
break;
|
||||
case 'root':
|
||||
i18nId = 'mobile.account_notifications.threads_start';
|
||||
i18nMessage = 'Threads that I start';
|
||||
break;
|
||||
case 'never':
|
||||
i18nId = 'mobile.account_notifications.threads_mentions';
|
||||
i18nMessage = 'Mentions in threads';
|
||||
break;
|
||||
}
|
||||
|
||||
return (
|
||||
<SectionItem
|
||||
description={(
|
||||
<FormattedText
|
||||
id={i18nId}
|
||||
defaultMessage={i18nMessage}
|
||||
/>
|
||||
)}
|
||||
label={(
|
||||
<FormattedText
|
||||
id='user.settings.notifications.comments'
|
||||
defaultMessage='Reply notifications'
|
||||
/>
|
||||
)}
|
||||
action={this.showReplyModal}
|
||||
actionType='default'
|
||||
theme={theme}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
saveMentionKeys = () => {
|
||||
this.setState({showKeywordsModal: false});
|
||||
this.updateMentionKeys(this.keywords);
|
||||
};
|
||||
|
||||
saveReplyNotification = () => {
|
||||
this.setState({showReplyModal: false});
|
||||
this.setReplyNotifications(this.replyValue);
|
||||
};
|
||||
|
||||
showKeywordsModal = () => {
|
||||
this.setState({showKeywordsModal: true});
|
||||
};
|
||||
|
||||
showReplyModal = () => {
|
||||
this.setState({showReplyModal: true});
|
||||
};
|
||||
|
||||
render() {
|
||||
const {currentUser, theme} = this.props;
|
||||
const style = getStyleSheet(theme);
|
||||
|
||||
let mentionKeysComponent;
|
||||
if (this.state.mention_keys) {
|
||||
mentionKeysComponent = (<Text>{this.state.mention_keys}</Text>);
|
||||
} else {
|
||||
mentionKeysComponent = (
|
||||
<FormattedText
|
||||
id='mobile.notification_settings_mentions.keywordsDescription'
|
||||
defaultMessage='Other words that trigger a mention'
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<View style={style.container}>
|
||||
<StatusBar/>
|
||||
<ScrollView
|
||||
style={style.scrollView}
|
||||
contentContainerStyle={style.scrollViewContent}
|
||||
>
|
||||
{currentUser.first_name.length > 0 &&
|
||||
<View>
|
||||
<SectionItem
|
||||
label={(
|
||||
<Text>
|
||||
{currentUser.first_name}
|
||||
</Text>
|
||||
)}
|
||||
description={(
|
||||
<FormattedText
|
||||
id='mobile.notification_settings.mentions.sensitiveName'
|
||||
defaultMessage='Your case sensitive first name'
|
||||
/>
|
||||
)}
|
||||
action={this.toggleFirstNameMention}
|
||||
actionType='toggle'
|
||||
selected={this.state.first_name === 'true'}
|
||||
theme={theme}
|
||||
/>
|
||||
<View style={style.separator}/>
|
||||
</View>
|
||||
}
|
||||
<SectionItem
|
||||
label={(
|
||||
<Text>
|
||||
{currentUser.username}
|
||||
</Text>
|
||||
)}
|
||||
description={(
|
||||
<FormattedText
|
||||
id='mobile.notification_settings.mentions.sensitiveUsername'
|
||||
defaultMessage='Your non-case sensitive username'
|
||||
/>
|
||||
)}
|
||||
selected={this.state.usernameMention}
|
||||
action={this.toggleUsernameMention}
|
||||
actionType='toggle'
|
||||
theme={theme}
|
||||
/>
|
||||
<View style={style.separator}/>
|
||||
<SectionItem
|
||||
label={(
|
||||
<Text>
|
||||
{'@channel, @all, @here'}
|
||||
</Text>
|
||||
)}
|
||||
description={(
|
||||
<FormattedText
|
||||
id='mobile.notification_settings.mentions.channelWide'
|
||||
defaultMessage='Channel-wide mentions'
|
||||
/>
|
||||
)}
|
||||
action={this.toggleChannelMentions}
|
||||
actionType='toggle'
|
||||
selected={this.state.channel === 'true'}
|
||||
theme={theme}
|
||||
/>
|
||||
<View style={style.separator}/>
|
||||
<SectionItem
|
||||
label={(
|
||||
<FormattedText
|
||||
id='mobile.notification_settings_mentions.keywords'
|
||||
defaultMessage='Keywords'
|
||||
/>
|
||||
)}
|
||||
description={mentionKeysComponent}
|
||||
action={this.showKeywordsModal}
|
||||
actionType='default'
|
||||
theme={theme}
|
||||
/>
|
||||
<View style={style.separator}/>
|
||||
{this.renderReplySection()}
|
||||
<View style={style.separator}/>
|
||||
</ScrollView>
|
||||
{this.renderKeywordsModal(style)}
|
||||
{this.renderReplyModal(style)}
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const getStyleSheet = makeStyleSheetFromTheme((theme) => {
|
||||
return {
|
||||
container: {
|
||||
flex: 1,
|
||||
backgroundColor: theme.centerChannelBg
|
||||
},
|
||||
input: {
|
||||
color: theme.centerChannelColor,
|
||||
fontSize: 12,
|
||||
height: 40
|
||||
},
|
||||
separator: {
|
||||
backgroundColor: changeOpacity(theme.centerChannelColor, 0.1),
|
||||
flex: 1,
|
||||
height: 1
|
||||
},
|
||||
scrollView: {
|
||||
flex: 1,
|
||||
backgroundColor: changeOpacity(theme.centerChannelColor, 0.06)
|
||||
},
|
||||
scrollViewContent: {
|
||||
paddingVertical: 0
|
||||
},
|
||||
modalOverlay: {
|
||||
backgroundColor: changeOpacity('#000000', 0.6),
|
||||
alignItems: 'center',
|
||||
flex: 1
|
||||
},
|
||||
modal: {
|
||||
backgroundColor: theme.centerChannelBg,
|
||||
borderRadius: 4,
|
||||
marginTop: 20,
|
||||
width: '95%'
|
||||
},
|
||||
modalBody: {
|
||||
paddingHorizontal: 24
|
||||
},
|
||||
modalTitleContainer: {
|
||||
marginBottom: 30,
|
||||
marginTop: 20
|
||||
},
|
||||
modalTitle: {
|
||||
color: theme.centerChannelColor,
|
||||
fontSize: 19
|
||||
},
|
||||
modalOptionDisabled: {
|
||||
color: changeOpacity(theme.centerChannelColor, 0.5),
|
||||
fontSize: 17
|
||||
},
|
||||
modalInput: {
|
||||
color: theme.centerChannelColor,
|
||||
fontSize: 19
|
||||
},
|
||||
modalHelpText: {
|
||||
color: changeOpacity(theme.centerChannelColor, 0.5),
|
||||
fontSize: 13,
|
||||
marginTop: 20
|
||||
},
|
||||
modalFooter: {
|
||||
alignItems: 'flex-end',
|
||||
height: 58,
|
||||
marginTop: 40,
|
||||
width: '100%'
|
||||
},
|
||||
modalFooterContainer: {
|
||||
alignItems: 'center',
|
||||
flex: 1,
|
||||
flexDirection: 'row',
|
||||
paddingRight: 24
|
||||
},
|
||||
modalFooterOptionContainer: {
|
||||
alignItems: 'center',
|
||||
height: 40,
|
||||
justifyContent: 'center',
|
||||
paddingHorizontal: 10,
|
||||
paddingVertical: 5
|
||||
},
|
||||
modalFooterOption: {
|
||||
color: theme.linkColor,
|
||||
fontSize: 14
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
export default injectIntl(NotificationSettingsMentionsIos);
|
||||
|
|
@ -0,0 +1,238 @@
|
|||
// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
import React from 'react';
|
||||
import {
|
||||
ScrollView,
|
||||
Text,
|
||||
View
|
||||
} from 'react-native';
|
||||
import {injectIntl} from 'react-intl';
|
||||
|
||||
import FormattedText from 'app/components/formatted_text';
|
||||
import StatusBar from 'app/components/status_bar';
|
||||
import Section from 'app/screens/settings/section';
|
||||
import SectionItem from 'app/screens/settings/section_item';
|
||||
import {changeOpacity, makeStyleSheetFromTheme} from 'app/utils/theme';
|
||||
|
||||
import NotificationSettingsMentionsBase from './notification_settings_mention_base';
|
||||
|
||||
class NotificationSettingsMentionsIos extends NotificationSettingsMentionsBase {
|
||||
goToNotificationSettingsMentionKeywords = () => {
|
||||
const {intl, navigator, theme} = this.props;
|
||||
this.goingBack = false;
|
||||
|
||||
navigator.push({
|
||||
backButtonTitle: '',
|
||||
screen: 'NotificationSettingsMentionsKeywords',
|
||||
title: intl.formatMessage({id: 'mobile.notification_settings_mentions.keywords', defaultMessage: 'Keywords'}),
|
||||
animated: true,
|
||||
navigatorStyle: {
|
||||
navBarTextColor: theme.sidebarHeaderTextColor,
|
||||
navBarBackgroundColor: theme.sidebarHeaderBg,
|
||||
navBarButtonColor: theme.sidebarHeaderTextColor,
|
||||
screenBackgroundColor: theme.centerChannelBg
|
||||
},
|
||||
passProps: {
|
||||
keywords: this.state.mention_keys,
|
||||
onBack: this.updateMentionKeys
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
renderMentionSection(style) {
|
||||
const {currentUser, theme} = this.props;
|
||||
|
||||
let mentionKeysComponent;
|
||||
if (this.state.mention_keys) {
|
||||
mentionKeysComponent = (<Text>{this.state.mention_keys}</Text>);
|
||||
} else {
|
||||
mentionKeysComponent = (
|
||||
<FormattedText
|
||||
id='mobile.notification_settings_mentions.keywordsDescription'
|
||||
defaultMessage='Other words that trigger a mention'
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Section
|
||||
headerId='mobile.notification_settings_mentions.wordsTrigger'
|
||||
headerDefaultMessage='WORDS THAT TRIGGER MENTIONS'
|
||||
theme={theme}
|
||||
>
|
||||
{currentUser.first_name.length > 0 &&
|
||||
<View>
|
||||
<SectionItem
|
||||
label={(
|
||||
<Text>
|
||||
{currentUser.first_name}
|
||||
</Text>
|
||||
)}
|
||||
description={(
|
||||
<FormattedText
|
||||
id='mobile.notification_settings.mentions.sensitiveName'
|
||||
defaultMessage='Your case sensitive first name'
|
||||
/>
|
||||
)}
|
||||
action={this.toggleFirstNameMention}
|
||||
actionType='toggle'
|
||||
selected={this.state.first_name === 'true'}
|
||||
theme={theme}
|
||||
/>
|
||||
<View style={style.separator}/>
|
||||
</View>
|
||||
}
|
||||
<SectionItem
|
||||
label={(
|
||||
<Text>
|
||||
{currentUser.username}
|
||||
</Text>
|
||||
)}
|
||||
description={(
|
||||
<FormattedText
|
||||
id='mobile.notification_settings.mentions.sensitiveUsername'
|
||||
defaultMessage='Your non-case sensitive username'
|
||||
/>
|
||||
)}
|
||||
selected={this.state.usernameMention}
|
||||
action={this.toggleUsernameMention}
|
||||
actionType='toggle'
|
||||
theme={theme}
|
||||
/>
|
||||
<View style={style.separator}/>
|
||||
<SectionItem
|
||||
label={(
|
||||
<Text>
|
||||
{'@channel, @all, @here'}
|
||||
</Text>
|
||||
)}
|
||||
description={(
|
||||
<FormattedText
|
||||
id='mobile.notification_settings.mentions.channelWide'
|
||||
defaultMessage='Channel-wide mentions'
|
||||
/>
|
||||
)}
|
||||
action={this.toggleChannelMentions}
|
||||
actionType='toggle'
|
||||
selected={this.state.channel === 'true'}
|
||||
theme={theme}
|
||||
/>
|
||||
<View style={style.separator}/>
|
||||
<SectionItem
|
||||
label={(
|
||||
<FormattedText
|
||||
id='mobile.notification_settings_mentions.keywords'
|
||||
defaultMessage='Keywords'
|
||||
/>
|
||||
)}
|
||||
description={mentionKeysComponent}
|
||||
action={this.goToNotificationSettingsMentionKeywords}
|
||||
actionType='arrow'
|
||||
theme={theme}
|
||||
/>
|
||||
</Section>
|
||||
);
|
||||
}
|
||||
|
||||
renderReplySection(style) {
|
||||
const {theme} = this.props;
|
||||
|
||||
return (
|
||||
<Section
|
||||
headerId='mobile.account_notifications.reply.header'
|
||||
headerDefaultMessage='SEND REPLY NOTIFICATIONS FOR'
|
||||
theme={theme}
|
||||
>
|
||||
<SectionItem
|
||||
label={(
|
||||
<FormattedText
|
||||
id='mobile.account_notifications.threads_start_participate'
|
||||
defaultMessage='Threads that I start or participate in'
|
||||
/>
|
||||
)}
|
||||
action={this.setReplyNotifications}
|
||||
actionType='select'
|
||||
actionValue='any'
|
||||
selected={this.state.comments === 'any'}
|
||||
theme={theme}
|
||||
/>
|
||||
<View style={style.separator}/>
|
||||
<SectionItem
|
||||
label={(
|
||||
<FormattedText
|
||||
id='mobile.account_notifications.threads_start'
|
||||
defaultMessage='Threads that I start'
|
||||
/>
|
||||
)}
|
||||
action={this.setReplyNotifications}
|
||||
actionType='select'
|
||||
actionValue='root'
|
||||
selected={this.state.comments === 'root'}
|
||||
theme={theme}
|
||||
/>
|
||||
<View style={style.separator}/>
|
||||
<SectionItem
|
||||
label={(
|
||||
<FormattedText
|
||||
id='mobile.account_notifications.threads_mentions'
|
||||
defaultMessage='Mentions in threads'
|
||||
/>
|
||||
)}
|
||||
action={this.setReplyNotifications}
|
||||
actionType='select'
|
||||
actionValue='never'
|
||||
selected={this.state.comments === 'never'}
|
||||
theme={theme}
|
||||
/>
|
||||
</Section>
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
const {theme} = this.props;
|
||||
const style = getStyleSheet(theme);
|
||||
|
||||
return (
|
||||
<View style={style.container}>
|
||||
<StatusBar/>
|
||||
<ScrollView
|
||||
style={style.scrollView}
|
||||
contentContainerStyle={style.scrollViewContent}
|
||||
>
|
||||
{this.renderMentionSection(style)}
|
||||
{this.renderReplySection(style)}
|
||||
</ScrollView>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const getStyleSheet = makeStyleSheetFromTheme((theme) => {
|
||||
return {
|
||||
container: {
|
||||
flex: 1,
|
||||
backgroundColor: theme.centerChannelBg
|
||||
},
|
||||
input: {
|
||||
color: theme.centerChannelColor,
|
||||
fontSize: 12,
|
||||
height: 40
|
||||
},
|
||||
separator: {
|
||||
backgroundColor: changeOpacity(theme.centerChannelColor, 0.1),
|
||||
flex: 1,
|
||||
height: 1,
|
||||
marginLeft: 15
|
||||
},
|
||||
scrollView: {
|
||||
flex: 1,
|
||||
backgroundColor: changeOpacity(theme.centerChannelColor, 0.06)
|
||||
},
|
||||
scrollViewContent: {
|
||||
paddingVertical: 35
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
export default injectIntl(NotificationSettingsMentionsIos);
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
import {connect} from 'react-redux';
|
||||
|
||||
import {getTheme} from 'app/selectors/preferences';
|
||||
|
||||
import NotificationSettingsMentionsKeywords from './notification_settings_mentions_keywords';
|
||||
|
||||
function mapStateToProps(state, ownProps) {
|
||||
return {
|
||||
...ownProps,
|
||||
theme: getTheme(state)
|
||||
};
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps)(NotificationSettingsMentionsKeywords);
|
||||
|
|
@ -0,0 +1,130 @@
|
|||
// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
import React, {PureComponent} from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import {View} from 'react-native';
|
||||
|
||||
import FormattedText from 'app/components/formatted_text';
|
||||
import StatusBar from 'app/components/status_bar';
|
||||
import TextInputWithLocalizedPlaceholder from 'app/components/text_input_with_localized_placeholder';
|
||||
import {changeOpacity, makeStyleSheetFromTheme} from 'app/utils/theme';
|
||||
|
||||
export default class NotificationSettingsMentionsKeywords extends PureComponent {
|
||||
static propTypes = {
|
||||
keywords: PropTypes.string,
|
||||
navigator: PropTypes.object,
|
||||
onBack: PropTypes.func.isRequired,
|
||||
theme: PropTypes.object.isRequired
|
||||
};
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
keywords: props.keywords
|
||||
};
|
||||
|
||||
props.navigator.setOnNavigatorEvent(this.onNavigatorEvent);
|
||||
}
|
||||
|
||||
handleSubmit = () => {
|
||||
this.props.navigator.pop();
|
||||
};
|
||||
|
||||
keywordsRef = (ref) => {
|
||||
this.keywordsInput = ref;
|
||||
};
|
||||
|
||||
onKeywordsChangeText = (keywords) => {
|
||||
if (keywords.endsWith('\n')) {
|
||||
return this.handleSubmit();
|
||||
}
|
||||
|
||||
return this.setState({keywords});
|
||||
};
|
||||
|
||||
onNavigatorEvent = (event) => {
|
||||
if (event.type === 'ScreenChangedEvent') {
|
||||
switch (event.id) {
|
||||
case 'willDisappear':
|
||||
this.props.onBack(this.state.keywords);
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
const {theme} = this.props;
|
||||
const {keywords} = this.state;
|
||||
|
||||
const style = getStyleSheet(theme);
|
||||
|
||||
return (
|
||||
<View style={style.container}>
|
||||
<StatusBar/>
|
||||
<View style={style.wrapper}>
|
||||
<View style={style.inputContainer}>
|
||||
<TextInputWithLocalizedPlaceholder
|
||||
autoFocus={true}
|
||||
ref={this.keywordsRef}
|
||||
value={keywords}
|
||||
blurOnSubmit={false}
|
||||
onChangeText={this.onKeywordsChangeText}
|
||||
multiline={true}
|
||||
numberOfLines={1}
|
||||
style={style.input}
|
||||
autoCapitalize='none'
|
||||
autoCorrect={false}
|
||||
placeholder={{id: 'mobile.notification_settings_mentions.keywordsDescription', defaultMessage: 'Other words that trigger a mention'}}
|
||||
placeholderTextColor={changeOpacity(theme.centerChannelColor, 0.4)}
|
||||
returnKeyType='done'
|
||||
/>
|
||||
</View>
|
||||
<View style={style.helpContainer}>
|
||||
<FormattedText
|
||||
id='mobile.notification_settings_mentions.keywordsHelp'
|
||||
defaultMessage='Keywords are non-case sensitive and should be separated by a comma.'
|
||||
style={style.help}
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const getStyleSheet = makeStyleSheetFromTheme((theme) => {
|
||||
return {
|
||||
container: {
|
||||
flex: 1,
|
||||
backgroundColor: theme.centerChannelBg
|
||||
},
|
||||
wrapper: {
|
||||
flex: 1,
|
||||
backgroundColor: changeOpacity(theme.centerChannelColor, 0.06),
|
||||
paddingTop: 35
|
||||
},
|
||||
inputContainer: {
|
||||
borderTopWidth: 1,
|
||||
borderBottomWidth: 1,
|
||||
borderTopColor: changeOpacity(theme.centerChannelColor, 0.1),
|
||||
borderBottomColor: changeOpacity(theme.centerChannelColor, 0.1),
|
||||
backgroundColor: theme.centerChannelBg
|
||||
},
|
||||
input: {
|
||||
color: theme.centerChannelColor,
|
||||
fontSize: 15,
|
||||
height: 150,
|
||||
paddingHorizontal: 15,
|
||||
paddingVertical: 10
|
||||
},
|
||||
helpContainer: {
|
||||
marginTop: 10,
|
||||
paddingHorizontal: 15
|
||||
},
|
||||
help: {
|
||||
color: changeOpacity(theme.centerChannelColor, 0.4),
|
||||
fontSize: 13
|
||||
}
|
||||
};
|
||||
});
|
||||
20
app/screens/settings/notification_settings_mobile/index.js
Normal file
20
app/screens/settings/notification_settings_mobile/index.js
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
import {connect} from 'react-redux';
|
||||
|
||||
import {getConfig} from 'mattermost-redux/selectors/entities/general';
|
||||
|
||||
import {getTheme} from 'app/selectors/preferences';
|
||||
|
||||
import NotificationSettingsMobile from './notification_settings_mobile';
|
||||
|
||||
function mapStateToProps(state, ownProps) {
|
||||
return {
|
||||
...ownProps,
|
||||
config: getConfig(state),
|
||||
theme: getTheme(state)
|
||||
};
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps)(NotificationSettingsMobile);
|
||||
|
|
@ -0,0 +1,437 @@
|
|||
// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
import React from 'react';
|
||||
import {injectIntl} from 'react-intl';
|
||||
import {
|
||||
Modal,
|
||||
ScrollView,
|
||||
TouchableOpacity,
|
||||
View
|
||||
} from 'react-native';
|
||||
|
||||
import FormattedText from 'app/components/formatted_text';
|
||||
import {RadioButton, RadioButtonGroup} from 'app/components/radio_button';
|
||||
import StatusBar from 'app/components/status_bar';
|
||||
import {changeOpacity, makeStyleSheetFromTheme} from 'app/utils/theme';
|
||||
|
||||
import SectionItem from 'app/screens/settings/section_item';
|
||||
|
||||
import NotificationSettingsMobileBase from './notification_settings_mobile_base';
|
||||
|
||||
class NotificationSettingsMobileAndroid extends NotificationSettingsMobileBase {
|
||||
cancelMobilePushModal = () => {
|
||||
this.setState({showMobilePushModal: false});
|
||||
this.push = this.state.push;
|
||||
};
|
||||
|
||||
cancelMobilePushStatusModal = () => {
|
||||
this.setState({showMobilePushStatusModal: false});
|
||||
this.pushStatus = this.state.push_status;
|
||||
};
|
||||
|
||||
onMobilePushChanged = (value) => {
|
||||
this.push = value;
|
||||
};
|
||||
|
||||
onMobilePushStatusChanged = (value) => {
|
||||
this.pushStatus = value;
|
||||
};
|
||||
|
||||
renderMobilePushModal(style) {
|
||||
const {config, intl} = this.props;
|
||||
const pushNotificationsEnabled = config.SendPushNotifications === 'true';
|
||||
|
||||
return (
|
||||
<Modal
|
||||
animationType='slide'
|
||||
transparent={true}
|
||||
visible={this.state.showMobilePushModal}
|
||||
onRequestClose={this.cancelMobilePushModal}
|
||||
>
|
||||
<View style={style.modalOverlay}>
|
||||
<View style={style.modal}>
|
||||
<View style={style.modalBody}>
|
||||
<View style={style.modalTitleContainer}>
|
||||
<FormattedText
|
||||
id='mobile.notification_settings_mobile.push_activity_android'
|
||||
defaultMessage='Send notifications for'
|
||||
style={style.modalTitle}
|
||||
/>
|
||||
</View>
|
||||
{pushNotificationsEnabled &&
|
||||
<RadioButtonGroup
|
||||
name='pushSettings'
|
||||
onSelect={this.onMobilePushChanged}
|
||||
>
|
||||
<RadioButton
|
||||
label={intl.formatMessage({
|
||||
id: 'user.settings.notifications.allActivity',
|
||||
defaultMessage: 'For all activity'
|
||||
})}
|
||||
value='all'
|
||||
checked={this.state.push === 'all'}
|
||||
/>
|
||||
<RadioButton
|
||||
label={intl.formatMessage({
|
||||
id: 'user.settings.notifications.onlyMentions',
|
||||
defaultMessage: 'Only for mentions and direct messages'
|
||||
})}
|
||||
value='mention'
|
||||
checked={this.state.push === 'mention'}
|
||||
/>
|
||||
<RadioButton
|
||||
label={intl.formatMessage({
|
||||
id: 'user.settings.notifications.never',
|
||||
defaultMessage: 'Never'
|
||||
})}
|
||||
value='none'
|
||||
checked={this.state.push === 'none'}
|
||||
/>
|
||||
</RadioButtonGroup>
|
||||
}
|
||||
{!pushNotificationsEnabled &&
|
||||
<FormattedText
|
||||
id='user.settings.push_notification.disabled_long'
|
||||
defaultMessage='Push notifications for mobile devices have been disabled by your System Administrator.'
|
||||
style={style.modalOptionDisabled}
|
||||
/>
|
||||
}
|
||||
</View>
|
||||
<View style={style.modalFooter}>
|
||||
<View style={style.separator}/>
|
||||
<View style={style.modalFooterContainer}>
|
||||
<TouchableOpacity
|
||||
style={style.modalFooterOptionContainer}
|
||||
onPress={this.cancelMobilePushModal}
|
||||
>
|
||||
<FormattedText
|
||||
id='mobile.notification_settings.modal_cancel'
|
||||
defaultMessage='CANCEL'
|
||||
style={style.modalFooterOption}
|
||||
/>
|
||||
</TouchableOpacity>
|
||||
{pushNotificationsEnabled &&
|
||||
<View>
|
||||
<View style={{marginRight: 10}}/>
|
||||
<TouchableOpacity
|
||||
style={style.modalFooterOptionContainer}
|
||||
onPress={this.saveMobilePushModal}
|
||||
>
|
||||
<FormattedText
|
||||
id='mobile.notification_settings.modal_save'
|
||||
defaultMessage='SAVE'
|
||||
style={style.modalFooterOption}
|
||||
/>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
}
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
||||
renderMobilePushStatusModal(style) {
|
||||
const {intl} = this.props;
|
||||
|
||||
return (
|
||||
<Modal
|
||||
animationType='slide'
|
||||
transparent={true}
|
||||
visible={this.state.showMobilePushStatusModal}
|
||||
onRequestClose={this.cancelMobilePushStatusModal}
|
||||
>
|
||||
<View style={style.modalOverlay}>
|
||||
<View style={style.modal}>
|
||||
<View style={style.modalBody}>
|
||||
<View style={style.modalTitleContainer}>
|
||||
<FormattedText
|
||||
id='mobile.notification_settings_model.push_status_android'
|
||||
defaultMessage='Trigger push notifications when'
|
||||
style={style.modalTitle}
|
||||
/>
|
||||
</View>
|
||||
<RadioButtonGroup
|
||||
name='pushStatusSettings'
|
||||
onSelect={this.onMobilePushStatusChanged}
|
||||
>
|
||||
<RadioButton
|
||||
label={intl.formatMessage({
|
||||
id: 'user.settings.push_notification.online',
|
||||
defaultMessage: 'Online, away or offline'
|
||||
})}
|
||||
value='online'
|
||||
checked={this.state.push_status === 'online'}
|
||||
/>
|
||||
<RadioButton
|
||||
label={intl.formatMessage({
|
||||
id: 'user.settings.push_notification.away',
|
||||
defaultMessage: 'Away or offline'
|
||||
})}
|
||||
value='away'
|
||||
checked={this.state.push_status === 'away'}
|
||||
/>
|
||||
<RadioButton
|
||||
label={intl.formatMessage({
|
||||
id: 'user.settings.push_notification.offline',
|
||||
defaultMessage: 'Offline'
|
||||
})}
|
||||
value='offline'
|
||||
checked={this.state.push_status === 'offline'}
|
||||
/>
|
||||
</RadioButtonGroup>
|
||||
</View>
|
||||
<View style={style.modalFooter}>
|
||||
<View style={style.separator}/>
|
||||
<View style={style.modalFooterContainer}>
|
||||
<TouchableOpacity
|
||||
style={style.modalFooterOptionContainer}
|
||||
onPress={this.cancelMobilePushStatusModal}
|
||||
>
|
||||
<FormattedText
|
||||
id='mobile.notification_settings.modal_cancel'
|
||||
defaultMessage='CANCEL'
|
||||
style={style.modalFooterOption}
|
||||
/>
|
||||
</TouchableOpacity>
|
||||
<View style={{marginRight: 10}}/>
|
||||
<TouchableOpacity
|
||||
style={style.modalFooterOptionContainer}
|
||||
onPress={this.saveMobilePushStatusModal}
|
||||
>
|
||||
<FormattedText
|
||||
id='mobile.notification_settings.modal_save'
|
||||
defaultMessage='SAVE'
|
||||
style={style.modalFooterOption}
|
||||
/>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
||||
renderMobilePushSection() {
|
||||
const {config, theme} = this.props;
|
||||
|
||||
const pushNotificationsEnabled = config.SendPushNotifications === 'true';
|
||||
|
||||
let i18nId;
|
||||
let i18nMessage;
|
||||
const props = {};
|
||||
if (pushNotificationsEnabled) {
|
||||
switch (this.state.push) {
|
||||
case 'all':
|
||||
i18nId = 'user.settings.notifications.allActivity';
|
||||
i18nMessage = 'For all activity';
|
||||
break;
|
||||
case 'mention':
|
||||
i18nId = 'user.settings.notifications.onlyMentions';
|
||||
i18nMessage = 'Only for mentions and direct messages';
|
||||
break;
|
||||
case 'none':
|
||||
i18nId = 'user.settings.notifications.never';
|
||||
i18nMessage = 'Never';
|
||||
break;
|
||||
}
|
||||
props.description = (
|
||||
<FormattedText
|
||||
id={i18nId}
|
||||
defaultMessage={i18nMessage}
|
||||
/>
|
||||
);
|
||||
} else {
|
||||
props.description = (
|
||||
<FormattedText
|
||||
id='user.settings.push_notification.disabled'
|
||||
defaultMessage='Disabled by System Administrator'
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<SectionItem
|
||||
{...props}
|
||||
label={(
|
||||
<FormattedText
|
||||
id='mobile.notification_settings_mobile.push_activity_android'
|
||||
defaultMessage='Send notifications for'
|
||||
/>
|
||||
)}
|
||||
action={this.showMobilePushModal}
|
||||
actionType='default'
|
||||
theme={theme}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
renderMobilePushStatusSection(style) {
|
||||
const {config, theme} = this.props;
|
||||
|
||||
const showSection = config.SendPushNotifications === 'true' && this.state.push !== 'none';
|
||||
if (!showSection) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let i18nId;
|
||||
let i18nMessage;
|
||||
switch (this.state.push_status) {
|
||||
case 'online':
|
||||
i18nId = 'user.settings.push_notification.online';
|
||||
i18nMessage = 'Online, away or offline';
|
||||
break;
|
||||
case 'away':
|
||||
i18nId = 'user.settings.push_notification.away';
|
||||
i18nMessage = 'Away or offline';
|
||||
break;
|
||||
case 'offline':
|
||||
i18nId = 'user.settings.push_notification.offline';
|
||||
i18nMessage = 'Offline';
|
||||
break;
|
||||
}
|
||||
|
||||
return (
|
||||
<View>
|
||||
<SectionItem
|
||||
description={(
|
||||
<FormattedText
|
||||
id={i18nId}
|
||||
defaultMessage={i18nMessage}
|
||||
/>
|
||||
)}
|
||||
label={(
|
||||
<FormattedText
|
||||
id='mobile.notification_settings_model.push_status_android'
|
||||
defaultMessage='Trigger push notifications when'
|
||||
/>
|
||||
)}
|
||||
action={this.showMobilePushStatusModal}
|
||||
actionType='default'
|
||||
theme={theme}
|
||||
/>
|
||||
<View style={style.separator}/>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
saveMobilePushModal = () => {
|
||||
this.setState({showMobilePushModal: false});
|
||||
this.setMobilePush(this.push);
|
||||
};
|
||||
|
||||
saveMobilePushStatusModal = () => {
|
||||
this.setState({showMobilePushStatusModal: false});
|
||||
this.setMobilePushStatus(this.pushStatus);
|
||||
};
|
||||
|
||||
showMobilePushModal = () => {
|
||||
this.setState({showMobilePushModal: true});
|
||||
};
|
||||
|
||||
showMobilePushStatusModal = () => {
|
||||
this.setState({showMobilePushStatusModal: true});
|
||||
};
|
||||
|
||||
render() {
|
||||
const {theme} = this.props;
|
||||
const style = getStyleSheet(theme);
|
||||
|
||||
return (
|
||||
<View style={style.container}>
|
||||
<StatusBar/>
|
||||
<ScrollView
|
||||
style={style.scrollView}
|
||||
contentContainerStyle={style.scrollViewContent}
|
||||
>
|
||||
{this.renderMobilePushSection()}
|
||||
<View style={style.separator}/>
|
||||
{this.renderMobilePushStatusSection(style)}
|
||||
</ScrollView>
|
||||
{this.renderMobilePushModal(style)}
|
||||
{this.renderMobilePushStatusModal(style)}
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const getStyleSheet = makeStyleSheetFromTheme((theme) => {
|
||||
return {
|
||||
container: {
|
||||
flex: 1,
|
||||
backgroundColor: theme.centerChannelBg
|
||||
},
|
||||
input: {
|
||||
color: theme.centerChannelColor,
|
||||
fontSize: 12,
|
||||
height: 40
|
||||
},
|
||||
separator: {
|
||||
backgroundColor: changeOpacity(theme.centerChannelColor, 0.1),
|
||||
flex: 1,
|
||||
height: 1
|
||||
},
|
||||
scrollView: {
|
||||
flex: 1,
|
||||
backgroundColor: changeOpacity(theme.centerChannelColor, 0.06)
|
||||
},
|
||||
scrollViewContent: {
|
||||
paddingVertical: 0
|
||||
},
|
||||
modalOverlay: {
|
||||
backgroundColor: changeOpacity('#000000', 0.6),
|
||||
alignItems: 'center',
|
||||
flex: 1
|
||||
},
|
||||
modal: {
|
||||
backgroundColor: theme.centerChannelBg,
|
||||
borderRadius: 4,
|
||||
marginTop: 20,
|
||||
width: '95%'
|
||||
},
|
||||
modalBody: {
|
||||
paddingHorizontal: 24
|
||||
},
|
||||
modalTitleContainer: {
|
||||
marginBottom: 30,
|
||||
marginTop: 20
|
||||
},
|
||||
modalTitle: {
|
||||
color: theme.centerChannelColor,
|
||||
fontSize: 19
|
||||
},
|
||||
modalOptionDisabled: {
|
||||
color: changeOpacity(theme.centerChannelColor, 0.5),
|
||||
fontSize: 17
|
||||
},
|
||||
modalFooter: {
|
||||
alignItems: 'flex-end',
|
||||
height: 58,
|
||||
marginTop: 40,
|
||||
width: '100%'
|
||||
},
|
||||
modalFooterContainer: {
|
||||
alignItems: 'center',
|
||||
flex: 1,
|
||||
flexDirection: 'row',
|
||||
paddingRight: 24
|
||||
},
|
||||
modalFooterOptionContainer: {
|
||||
alignItems: 'center',
|
||||
height: 40,
|
||||
justifyContent: 'center',
|
||||
paddingHorizontal: 10,
|
||||
paddingVertical: 5
|
||||
},
|
||||
modalFooterOption: {
|
||||
color: theme.linkColor,
|
||||
fontSize: 14
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
export default injectIntl(NotificationSettingsMobileAndroid);
|
||||
|
|
@ -0,0 +1,198 @@
|
|||
// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
import React from 'react';
|
||||
import {injectIntl} from 'react-intl';
|
||||
import {
|
||||
ScrollView,
|
||||
View
|
||||
} from 'react-native';
|
||||
|
||||
import FormattedText from 'app/components/formatted_text';
|
||||
import StatusBar from 'app/components/status_bar';
|
||||
import {changeOpacity, makeStyleSheetFromTheme} from 'app/utils/theme';
|
||||
|
||||
import Section from 'app/screens/settings/section';
|
||||
import SectionItem from 'app/screens/settings/section_item';
|
||||
|
||||
import NotificationSettingsMobileBase from './notification_settings_mobile_base';
|
||||
|
||||
class NotificationSettingsMobileIos extends NotificationSettingsMobileBase {
|
||||
renderMobilePushSection(style) {
|
||||
const {config, theme} = this.props;
|
||||
|
||||
const pushNotificationsEnabled = config.SendPushNotifications === 'true';
|
||||
return (
|
||||
<Section
|
||||
headerId='mobile.notification_settings_mobile.push_activity'
|
||||
headerDefaultMessage='SEND NOTIFICATIONS FOR'
|
||||
theme={theme}
|
||||
>
|
||||
{pushNotificationsEnabled &&
|
||||
<View>
|
||||
<SectionItem
|
||||
label={(
|
||||
<FormattedText
|
||||
id='user.settings.notifications.allActivity'
|
||||
defaultMessage='For all activity'
|
||||
/>
|
||||
)}
|
||||
action={this.setMobilePush}
|
||||
actionType='select'
|
||||
actionValue='all'
|
||||
selected={this.state.push === 'all'}
|
||||
theme={theme}
|
||||
/>
|
||||
<View style={style.separator}/>
|
||||
<SectionItem
|
||||
label={(
|
||||
<FormattedText
|
||||
id='user.settings.notifications.onlyMentions'
|
||||
defaultMessage='Only for mentions and direct messages'
|
||||
/>
|
||||
)}
|
||||
action={this.setMobilePush}
|
||||
actionType='select'
|
||||
actionValue='mention'
|
||||
selected={this.state.push === 'mention'}
|
||||
theme={theme}
|
||||
/>
|
||||
<View style={style.separator}/>
|
||||
<SectionItem
|
||||
label={(
|
||||
<FormattedText
|
||||
id='user.settings.notifications.never'
|
||||
defaultMessage='Never'
|
||||
/>
|
||||
)}
|
||||
action={this.setMobilePush}
|
||||
actionType='select'
|
||||
actionValue='none'
|
||||
selected={this.state.push === 'none'}
|
||||
theme={theme}
|
||||
/>
|
||||
</View>
|
||||
}
|
||||
{!pushNotificationsEnabled &&
|
||||
<FormattedText
|
||||
id='user.settings.push_notification.disabled_long'
|
||||
defaultMessage='Push notifications for mobile devices have been disabled by your System Administrator.'
|
||||
style={style.disabled}
|
||||
/>
|
||||
}
|
||||
</Section>
|
||||
);
|
||||
}
|
||||
|
||||
renderMobilePushStatusSection(style) {
|
||||
const {config, theme} = this.props;
|
||||
|
||||
const showSection = config.SendPushNotifications === 'true' && this.state.push !== 'none';
|
||||
if (!showSection) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Section
|
||||
headerId='mobile.notification_settings_model.push_status'
|
||||
headerDefaultMessage='TRIGGER PUSH NOTIFICATIONS WHEN'
|
||||
theme={theme}
|
||||
>
|
||||
<SectionItem
|
||||
label={(
|
||||
<FormattedText
|
||||
id='user.settings.push_notification.online'
|
||||
defaultMessage='Online, away or offline'
|
||||
/>
|
||||
)}
|
||||
action={this.setMobilePushStatus}
|
||||
actionType='select'
|
||||
actionValue='online'
|
||||
selected={this.state.push_status === 'online'}
|
||||
theme={theme}
|
||||
/>
|
||||
<View style={style.separator}/>
|
||||
<SectionItem
|
||||
label={(
|
||||
<FormattedText
|
||||
id='user.settings.push_notification.away'
|
||||
defaultMessage='Away or offline'
|
||||
/>
|
||||
)}
|
||||
action={this.setMobilePushStatus}
|
||||
actionType='select'
|
||||
actionValue='away'
|
||||
selected={this.state.push_status === 'away'}
|
||||
theme={theme}
|
||||
/>
|
||||
<View style={style.separator}/>
|
||||
<SectionItem
|
||||
label={(
|
||||
<FormattedText
|
||||
id='user.settings.push_notification.offline'
|
||||
defaultMessage='Offline'
|
||||
/>
|
||||
)}
|
||||
action={this.setMobilePushStatus}
|
||||
actionType='select'
|
||||
actionValue='offline'
|
||||
selected={this.state.push_status === 'offline'}
|
||||
theme={theme}
|
||||
/>
|
||||
</Section>
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
const {theme} = this.props;
|
||||
const style = getStyleSheet(theme);
|
||||
|
||||
return (
|
||||
<View style={style.container}>
|
||||
<StatusBar/>
|
||||
<ScrollView
|
||||
style={style.scrollView}
|
||||
contentContainerStyle={style.scrollViewContent}
|
||||
>
|
||||
{this.renderMobilePushSection(style)}
|
||||
{this.renderMobilePushStatusSection(style)}
|
||||
</ScrollView>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const getStyleSheet = makeStyleSheetFromTheme((theme) => {
|
||||
return {
|
||||
container: {
|
||||
flex: 1,
|
||||
backgroundColor: theme.centerChannelBg
|
||||
},
|
||||
input: {
|
||||
color: theme.centerChannelColor,
|
||||
fontSize: 12,
|
||||
height: 40
|
||||
},
|
||||
separator: {
|
||||
backgroundColor: changeOpacity(theme.centerChannelColor, 0.1),
|
||||
flex: 1,
|
||||
height: 1,
|
||||
marginLeft: 15
|
||||
},
|
||||
scrollView: {
|
||||
flex: 1,
|
||||
backgroundColor: changeOpacity(theme.centerChannelColor, 0.06)
|
||||
},
|
||||
scrollViewContent: {
|
||||
paddingVertical: 35
|
||||
},
|
||||
disabled: {
|
||||
color: theme.centerChannelColor,
|
||||
fontSize: 15,
|
||||
paddingHorizontal: 15,
|
||||
paddingVertical: 10
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
export default injectIntl(NotificationSettingsMobileIos);
|
||||
|
|
@ -0,0 +1,62 @@
|
|||
// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
import {PureComponent} from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import {intlShape} from 'react-intl';
|
||||
|
||||
export default class NotificationSettingsMobileBase extends PureComponent {
|
||||
static propTypes = {
|
||||
config: PropTypes.object.isRequired,
|
||||
currentUser: PropTypes.object.isRequired,
|
||||
intl: intlShape.isRequired,
|
||||
navigator: PropTypes.object,
|
||||
onBack: PropTypes.func.isRequired,
|
||||
theme: PropTypes.object.isRequired
|
||||
};
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
const {currentUser} = props;
|
||||
const notifyProps = currentUser.notify_props || {};
|
||||
|
||||
this.state = {
|
||||
...notifyProps,
|
||||
showMobilePushModal: false,
|
||||
showMobilePushStatusModal: false
|
||||
};
|
||||
this.push = this.state.push;
|
||||
this.pushStatus = this.state.push_status;
|
||||
props.navigator.setOnNavigatorEvent(this.onNavigatorEvent);
|
||||
}
|
||||
|
||||
onNavigatorEvent = (event) => {
|
||||
if (event.type === 'ScreenChangedEvent') {
|
||||
switch (event.id) {
|
||||
case 'willDisappear':
|
||||
this.saveUserNotifyProps();
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
setMobilePush = (push) => {
|
||||
this.setState({push});
|
||||
};
|
||||
|
||||
setMobilePushStatus = (value) => {
|
||||
this.setState({push_status: value});
|
||||
};
|
||||
|
||||
saveUserNotifyProps = () => {
|
||||
const props = {...this.state};
|
||||
|
||||
Reflect.deleteProperty(props, 'showMobilePushModal');
|
||||
Reflect.deleteProperty(props, 'showMobilePushStatusModal');
|
||||
this.props.onBack({
|
||||
...props,
|
||||
user_id: this.props.currentUser.id
|
||||
});
|
||||
};
|
||||
}
|
||||
|
|
@ -10,33 +10,6 @@ import {
|
|||
import FormattedText from 'app/components/formatted_text';
|
||||
import {changeOpacity, makeStyleSheetFromTheme} from 'app/utils/theme';
|
||||
|
||||
const getStyleSheet = makeStyleSheetFromTheme((theme) => {
|
||||
return {
|
||||
container: {
|
||||
marginTop: 30
|
||||
},
|
||||
footer: {
|
||||
marginHorizontal: 15,
|
||||
marginTop: 10,
|
||||
fontSize: 12,
|
||||
color: changeOpacity(theme.centerChannelColor, 0.5)
|
||||
},
|
||||
header: {
|
||||
marginHorizontal: 15,
|
||||
marginBottom: 10,
|
||||
fontSize: 13,
|
||||
color: theme.centerChannelColor
|
||||
},
|
||||
items: {
|
||||
backgroundColor: theme.centerChannelBg,
|
||||
borderTopWidth: 1,
|
||||
borderBottomWidth: 1,
|
||||
borderTopColor: changeOpacity(theme.centerChannelColor, 0.1),
|
||||
borderBottomColor: changeOpacity(theme.centerChannelColor, 0.1)
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
function section(props) {
|
||||
const {
|
||||
children,
|
||||
|
|
@ -87,4 +60,31 @@ section.propTypes = {
|
|||
theme: PropTypes.object.isRequired
|
||||
};
|
||||
|
||||
const getStyleSheet = makeStyleSheetFromTheme((theme) => {
|
||||
return {
|
||||
container: {
|
||||
marginBottom: 30
|
||||
},
|
||||
header: {
|
||||
marginHorizontal: 15,
|
||||
marginBottom: 10,
|
||||
fontSize: 13,
|
||||
color: changeOpacity(theme.centerChannelColor, 0.5)
|
||||
},
|
||||
items: {
|
||||
backgroundColor: theme.centerChannelBg,
|
||||
borderTopWidth: 1,
|
||||
borderBottomWidth: 1,
|
||||
borderTopColor: changeOpacity(theme.centerChannelColor, 0.1),
|
||||
borderBottomColor: changeOpacity(theme.centerChannelColor, 0.1)
|
||||
},
|
||||
footer: {
|
||||
marginHorizontal: 15,
|
||||
marginTop: 10,
|
||||
fontSize: 12,
|
||||
color: changeOpacity(theme.centerChannelColor, 0.5)
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
export default section;
|
||||
|
|
@ -8,46 +8,27 @@ import {
|
|||
TouchableWithoutFeedback,
|
||||
View
|
||||
} from 'react-native';
|
||||
import FontAwesomeIcon from 'react-native-vector-icons/FontAwesome';
|
||||
|
||||
import FormattedText from 'app/components/formatted_text';
|
||||
import {makeStyleSheetFromTheme} from 'app/utils/theme';
|
||||
import CheckMark from 'app/components/checkmark';
|
||||
import {changeOpacity, makeStyleSheetFromTheme} from 'app/utils/theme';
|
||||
|
||||
const ActionTypes = {
|
||||
ARROW: 'arrow',
|
||||
DEFAULT: 'default',
|
||||
TOGGLE: 'toggle',
|
||||
SELECT: 'select'
|
||||
};
|
||||
|
||||
const getStyleSheet = makeStyleSheetFromTheme((theme) => {
|
||||
return {
|
||||
container: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center'
|
||||
},
|
||||
label: {
|
||||
flex: 1,
|
||||
fontSize: 12,
|
||||
color: theme.centerChannelColor,
|
||||
paddingVertical: 15
|
||||
},
|
||||
wrapper: {
|
||||
paddingHorizontal: 15
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
function sectionItem(props) {
|
||||
const {
|
||||
action,
|
||||
actionType,
|
||||
actionValue,
|
||||
children,
|
||||
labelDefaultMessage,
|
||||
labelId,
|
||||
labelValues,
|
||||
label,
|
||||
theme,
|
||||
selected
|
||||
selected,
|
||||
description
|
||||
} = props;
|
||||
|
||||
const style = getStyleSheet(theme);
|
||||
|
|
@ -68,24 +49,39 @@ function sectionItem(props) {
|
|||
value={selected}
|
||||
/>
|
||||
);
|
||||
} else if (actionType === ActionTypes.ARROW) {
|
||||
actionComponent = (
|
||||
<FontAwesomeIcon
|
||||
name='angle-right'
|
||||
style={style.arrow}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
const labelComponent = React.cloneElement(
|
||||
label,
|
||||
{style: style.label}
|
||||
);
|
||||
|
||||
let descriptionComponent;
|
||||
if (description) {
|
||||
descriptionComponent = React.cloneElement(
|
||||
description,
|
||||
{style: style.description}
|
||||
);
|
||||
}
|
||||
|
||||
const component = (
|
||||
<View style={style.wrapper}>
|
||||
<View style={style.container}>
|
||||
<FormattedText
|
||||
id={labelId}
|
||||
defaultMessage={labelDefaultMessage}
|
||||
values={labelValues}
|
||||
style={[style.label, (children && {paddingBottom: 5})]}
|
||||
/>
|
||||
{actionComponent}
|
||||
<View style={style.container}>
|
||||
<View style={description ? style.doubleContainer : style.singleContainer}>
|
||||
{labelComponent}
|
||||
{descriptionComponent}
|
||||
</View>
|
||||
{children}
|
||||
{actionComponent}
|
||||
</View>
|
||||
);
|
||||
|
||||
if (actionType === ActionTypes.DEFAULT || actionType === ActionTypes.SELECT) {
|
||||
if (actionType === ActionTypes.DEFAULT || actionType === ActionTypes.SELECT || actionType === ActionTypes.ARROW) {
|
||||
return (
|
||||
<TouchableWithoutFeedback onPress={() => action(actionValue)}>
|
||||
{component}
|
||||
|
|
@ -98,14 +94,12 @@ function sectionItem(props) {
|
|||
|
||||
sectionItem.propTypes = {
|
||||
action: PropTypes.func,
|
||||
actionType: PropTypes.oneOf([ActionTypes.DEFAULT, ActionTypes.TOGGLE, ActionTypes.SELECT]),
|
||||
actionType: PropTypes.oneOf([ActionTypes.ARROW, ActionTypes.DEFAULT, ActionTypes.TOGGLE, ActionTypes.SELECT]),
|
||||
actionValue: PropTypes.string,
|
||||
children: PropTypes.node,
|
||||
labelDefaultMessage: PropTypes.string.isRequired,
|
||||
labelId: PropTypes.string.isRequired,
|
||||
labelValues: PropTypes.object,
|
||||
label: PropTypes.node.isRequired,
|
||||
selected: PropTypes.bool,
|
||||
theme: PropTypes.object.isRequired
|
||||
theme: PropTypes.object.isRequired,
|
||||
description: PropTypes.node
|
||||
};
|
||||
|
||||
sectionItem.defaultProps = {
|
||||
|
|
@ -113,4 +107,39 @@ sectionItem.defaultProps = {
|
|||
actionType: ActionTypes.DEFAULT
|
||||
};
|
||||
|
||||
const getStyleSheet = makeStyleSheetFromTheme((theme) => {
|
||||
return {
|
||||
container: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
paddingHorizontal: 15
|
||||
},
|
||||
singleContainer: {
|
||||
alignItems: 'center',
|
||||
flex: 1,
|
||||
flexDirection: 'row',
|
||||
height: 45
|
||||
},
|
||||
doubleContainer: {
|
||||
flex: 1,
|
||||
flexDirection: 'column',
|
||||
height: 69,
|
||||
justifyContent: 'center'
|
||||
},
|
||||
label: {
|
||||
color: theme.centerChannelColor,
|
||||
fontSize: 15
|
||||
},
|
||||
description: {
|
||||
color: changeOpacity(theme.centerChannelColor, 0.6),
|
||||
fontSize: 14,
|
||||
marginTop: 3
|
||||
},
|
||||
arrow: {
|
||||
color: changeOpacity(theme.centerChannelColor, 0.25),
|
||||
fontSize: 24
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
export default sectionItem;
|
||||
|
|
@ -1,143 +0,0 @@
|
|||
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
import React, {PureComponent} from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import {TouchableOpacity, View} from 'react-native';
|
||||
import FontAwesomeIcon from 'react-native-vector-icons/FontAwesome';
|
||||
import IonIcon from 'react-native-vector-icons/Ionicons';
|
||||
import MaterialIcon from 'react-native-vector-icons/MaterialIcons';
|
||||
|
||||
import FormattedText from 'app/components/formatted_text';
|
||||
import {changeOpacity, makeStyleSheetFromTheme} from 'app/utils/theme';
|
||||
|
||||
export default class SettingsItem extends PureComponent {
|
||||
static propTypes = {
|
||||
defaultMessage: PropTypes.string.isRequired,
|
||||
i18nId: PropTypes.string.isRequired,
|
||||
iconName: PropTypes.string.isRequired,
|
||||
iconType: PropTypes.oneOf(['fontawesome', 'ion', 'material']).isRequired,
|
||||
isDestructor: PropTypes.bool,
|
||||
onPress: PropTypes.func,
|
||||
separator: PropTypes.bool,
|
||||
theme: PropTypes.object.isRequired
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
isDestructor: false,
|
||||
separator: true
|
||||
};
|
||||
|
||||
render() {
|
||||
const {
|
||||
defaultMessage,
|
||||
i18nId,
|
||||
iconName,
|
||||
iconType,
|
||||
isDestructor,
|
||||
onPress,
|
||||
separator,
|
||||
theme
|
||||
} = this.props;
|
||||
const style = getStyleSheet(theme);
|
||||
|
||||
const destructor = {};
|
||||
if (isDestructor) {
|
||||
destructor.color = theme.errorTextColor;
|
||||
}
|
||||
|
||||
let divider;
|
||||
if (separator) {
|
||||
divider = (<View style={style.divider}/>);
|
||||
}
|
||||
|
||||
let icon;
|
||||
switch (iconType) {
|
||||
case 'fontawesome':
|
||||
icon = (
|
||||
<FontAwesomeIcon
|
||||
name={iconName}
|
||||
style={[style.icon, destructor]}
|
||||
|
||||
/>
|
||||
);
|
||||
break;
|
||||
case 'ion':
|
||||
icon = (
|
||||
<IonIcon
|
||||
name={iconName}
|
||||
style={[style.icon, destructor]}
|
||||
/>
|
||||
);
|
||||
break;
|
||||
case 'material':
|
||||
icon = (
|
||||
<MaterialIcon
|
||||
name={iconName}
|
||||
style={[style.icon, destructor]}
|
||||
/>
|
||||
);
|
||||
break;
|
||||
}
|
||||
|
||||
return (
|
||||
<View style={style.container}>
|
||||
<TouchableOpacity
|
||||
onPress={onPress}
|
||||
style={{flex: 1}}
|
||||
>
|
||||
<View style={style.wrapper}>
|
||||
<View style={style.iconContainer}>
|
||||
{icon}
|
||||
</View>
|
||||
<FormattedText
|
||||
id={i18nId}
|
||||
defaultMessage={defaultMessage}
|
||||
style={[style.label, destructor]}
|
||||
/>
|
||||
</View>
|
||||
</TouchableOpacity>
|
||||
{divider}
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const getStyleSheet = makeStyleSheetFromTheme((theme) => {
|
||||
return {
|
||||
container: {
|
||||
backgroundColor: theme.centerChannelBg,
|
||||
height: 51,
|
||||
flexDirection: 'column',
|
||||
justifyContent: 'center'
|
||||
},
|
||||
wrapper: {
|
||||
alignItems: 'center',
|
||||
backgroundColor: theme.centerChannelBg,
|
||||
flexDirection: 'row',
|
||||
height: 50,
|
||||
paddingLeft: 16
|
||||
},
|
||||
iconContainer: {
|
||||
width: 18,
|
||||
marginRight: 15,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center'
|
||||
},
|
||||
icon: {
|
||||
color: changeOpacity(theme.centerChannelColor, 0.5),
|
||||
fontSize: 20
|
||||
},
|
||||
label: {
|
||||
color: theme.centerChannelColor,
|
||||
fontSize: 16,
|
||||
lineHeight: 19,
|
||||
marginLeft: 8
|
||||
},
|
||||
divider: {
|
||||
backgroundColor: changeOpacity(theme.centerChannelColor, 0.1),
|
||||
height: 1,
|
||||
marginHorizontal: 16
|
||||
}
|
||||
};
|
||||
});
|
||||
101
app/screens/settings/settings_item/index.js
Normal file
101
app/screens/settings/settings_item/index.js
Normal file
|
|
@ -0,0 +1,101 @@
|
|||
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
import React, {PureComponent} from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import {TouchableOpacity, View} from 'react-native';
|
||||
import FontAwesomeIcon from 'react-native-vector-icons/FontAwesome';
|
||||
|
||||
import FormattedText from 'app/components/formatted_text';
|
||||
|
||||
import SettingItemIcon from './setting_item_icon';
|
||||
import getStyleSheet from './style';
|
||||
|
||||
export default class SettingsItem extends PureComponent {
|
||||
static propTypes = {
|
||||
defaultMessage: PropTypes.string.isRequired,
|
||||
i18nId: PropTypes.string.isRequired,
|
||||
iconName: PropTypes.string,
|
||||
iconType: PropTypes.oneOf(['fontawesome', 'foundation', 'ion', 'material']),
|
||||
isDestructor: PropTypes.bool,
|
||||
centered: PropTypes.bool,
|
||||
onPress: PropTypes.func,
|
||||
separator: PropTypes.bool,
|
||||
showArrow: PropTypes.bool,
|
||||
theme: PropTypes.object.isRequired
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
isDestructor: false,
|
||||
separator: true
|
||||
};
|
||||
|
||||
render() {
|
||||
const {
|
||||
centered,
|
||||
defaultMessage,
|
||||
i18nId,
|
||||
iconName,
|
||||
iconType,
|
||||
isDestructor,
|
||||
onPress,
|
||||
separator,
|
||||
showArrow,
|
||||
theme
|
||||
} = this.props;
|
||||
const style = getStyleSheet(theme);
|
||||
|
||||
const destructor = {};
|
||||
if (isDestructor) {
|
||||
destructor.color = theme.errorTextColor;
|
||||
}
|
||||
|
||||
let divider;
|
||||
if (separator) {
|
||||
divider = (<View style={style.divider}/>);
|
||||
}
|
||||
|
||||
let icon;
|
||||
if (iconType && iconName) {
|
||||
icon = (
|
||||
<SettingItemIcon
|
||||
name={iconName}
|
||||
type={iconType}
|
||||
style={[style.icon, destructor]}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<TouchableOpacity
|
||||
onPress={onPress}
|
||||
>
|
||||
<View style={style.container}>
|
||||
{icon &&
|
||||
<View style={style.iconContainer}>
|
||||
{icon}
|
||||
</View>
|
||||
}
|
||||
<View style={style.wrapper}>
|
||||
<View style={style.labelContainer}>
|
||||
<FormattedText
|
||||
id={i18nId}
|
||||
defaultMessage={defaultMessage}
|
||||
style={[style.label, destructor, centered ? style.centerLabel : {}]}
|
||||
/>
|
||||
{showArrow &&
|
||||
<View style={style.arrowContainer}>
|
||||
<FontAwesomeIcon
|
||||
name='angle-right'
|
||||
style={style.arrow}
|
||||
/>
|
||||
</View>
|
||||
}
|
||||
</View>
|
||||
{divider}
|
||||
</View>
|
||||
</View>
|
||||
</TouchableOpacity>
|
||||
);
|
||||
}
|
||||
}
|
||||
52
app/screens/settings/settings_item/setting_item_icon.js
Normal file
52
app/screens/settings/settings_item/setting_item_icon.js
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import IonIcon from 'react-native-vector-icons/Ionicons';
|
||||
import FontAwesomeIcon from 'react-native-vector-icons/FontAwesome';
|
||||
import MaterialIcon from 'react-native-vector-icons/MaterialIcons';
|
||||
import FoundationIcon from 'react-native-vector-icons/Foundation';
|
||||
|
||||
export default function settingsItemIcon(props) {
|
||||
const {name, type, style} = props;
|
||||
|
||||
switch (type) {
|
||||
case 'fontawesome':
|
||||
return (
|
||||
<FontAwesomeIcon
|
||||
name={name}
|
||||
style={style}
|
||||
/>
|
||||
);
|
||||
case 'foundation':
|
||||
return (
|
||||
<FoundationIcon
|
||||
name={name}
|
||||
style={style}
|
||||
/>
|
||||
);
|
||||
case 'ion':
|
||||
return (
|
||||
<IonIcon
|
||||
name={name}
|
||||
style={style}
|
||||
/>
|
||||
);
|
||||
case 'material':
|
||||
return (
|
||||
<MaterialIcon
|
||||
name={name}
|
||||
style={style}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
settingsItemIcon.propTypes = {
|
||||
name: PropTypes.string,
|
||||
type: PropTypes.string,
|
||||
style: PropTypes.oneOfType([PropTypes.object, PropTypes.array])
|
||||
};
|
||||
49
app/screens/settings/settings_item/style.android.js
Normal file
49
app/screens/settings/settings_item/style.android.js
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
import {changeOpacity, makeStyleSheetFromTheme} from 'app/utils/theme';
|
||||
|
||||
export default makeStyleSheetFromTheme((theme) => {
|
||||
return {
|
||||
container: {
|
||||
alignItems: 'center',
|
||||
backgroundColor: theme.centerChannelBg,
|
||||
flexDirection: 'row',
|
||||
height: 68
|
||||
},
|
||||
iconContainer: {
|
||||
width: 42,
|
||||
height: 68,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
marginHorizontal: 15
|
||||
},
|
||||
icon: {
|
||||
color: theme.buttonBg,
|
||||
fontSize: 25
|
||||
},
|
||||
wrapper: {
|
||||
flex: 1
|
||||
},
|
||||
centerLabel: {
|
||||
textAlign: 'center',
|
||||
textAlignVertical: 'center'
|
||||
},
|
||||
labelContainer: {
|
||||
flex: 1,
|
||||
flexDirection: 'row'
|
||||
},
|
||||
label: {
|
||||
color: theme.centerChannelColor,
|
||||
flex: 1,
|
||||
fontSize: 17,
|
||||
textAlignVertical: 'center',
|
||||
includeFontPadding: false,
|
||||
paddingRight: 15
|
||||
},
|
||||
divider: {
|
||||
backgroundColor: changeOpacity(theme.centerChannelColor, 0.1),
|
||||
height: 1
|
||||
}
|
||||
};
|
||||
});
|
||||
58
app/screens/settings/settings_item/style.ios.js
Normal file
58
app/screens/settings/settings_item/style.ios.js
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
import {changeOpacity, makeStyleSheetFromTheme} from 'app/utils/theme';
|
||||
|
||||
export default makeStyleSheetFromTheme((theme) => {
|
||||
return {
|
||||
container: {
|
||||
alignItems: 'center',
|
||||
backgroundColor: theme.centerChannelBg,
|
||||
flexDirection: 'row',
|
||||
height: 45
|
||||
},
|
||||
iconContainer: {
|
||||
width: 29,
|
||||
height: 29,
|
||||
backgroundColor: theme.buttonBg,
|
||||
borderRadius: 7,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
marginHorizontal: 15
|
||||
},
|
||||
icon: {
|
||||
color: theme.buttonColor,
|
||||
fontSize: 18,
|
||||
marginTop: 2
|
||||
},
|
||||
wrapper: {
|
||||
flex: 1
|
||||
},
|
||||
centerLabel: {
|
||||
textAlign: 'center',
|
||||
textAlignVertical: 'center'
|
||||
},
|
||||
labelContainer: {
|
||||
flex: 1,
|
||||
flexDirection: 'row'
|
||||
},
|
||||
label: {
|
||||
color: theme.centerChannelColor,
|
||||
flex: 1,
|
||||
fontSize: 17,
|
||||
lineHeight: 43
|
||||
},
|
||||
arrowContainer: {
|
||||
justifyContent: 'center',
|
||||
paddingRight: 15
|
||||
},
|
||||
arrow: {
|
||||
color: changeOpacity(theme.centerChannelColor, 0.25),
|
||||
fontSize: 18
|
||||
},
|
||||
divider: {
|
||||
backgroundColor: changeOpacity(theme.centerChannelColor, 0.1),
|
||||
height: 1
|
||||
}
|
||||
};
|
||||
});
|
||||
|
|
@ -1802,7 +1802,7 @@
|
|||
"mobile.account.notifications.email.footer": "When offline or away for more than five minutes",
|
||||
"mobile.account_notifications.mentions_footer": "Your username (\"@{username}\") will always trigger mentions.",
|
||||
"mobile.account_notifications.non-case_sensitive_words": "Other non-case sensitive words...",
|
||||
"mobile.account_notifications.reply.header": "Send reply notifications for",
|
||||
"mobile.account_notifications.reply.header": "SEND REPLY NOTIFICATIONS FOR",
|
||||
"mobile.account_notifications.threads_mentions": "Mentions in threads",
|
||||
"mobile.account_notifications.threads_start": "Threads that I start",
|
||||
"mobile.account_notifications.threads_start_participate": "Threads that I start or participate in",
|
||||
|
|
@ -1884,6 +1884,25 @@
|
|||
"mobile.notice_platform_link": "platform",
|
||||
"mobile.notice_text": "Mattermost is made possible by the open source software used in our {platform} and {mobile}.",
|
||||
"mobile.notification.in": " in ",
|
||||
"mobile.notification_settings.email": "Email",
|
||||
"mobile.notification_settings.email_title": "Email Notifications",
|
||||
"mobile.notification_settings.mobile": "Mobile",
|
||||
"mobile.notification_settings.mobile_title": "Mobile Notifications",
|
||||
"mobile.notification_settings.save_failed_description": "The notification settings failed to save due to a connection issue, please try again.",
|
||||
"mobile.notification_settings.save_failed_title": "Connection issue",
|
||||
"mobile.notification_settings.mentions.reply_title": "Send Reply notifications for",
|
||||
"mobile.notification_settings.mentions_replies": "Mentions and Replies",
|
||||
"mobile.notification_settings.mentions.channelWide": "Channel-wide mentions",
|
||||
"mobile.notification_settings_mentions.keywords": "Keywords",
|
||||
"mobile.notification_settings_mentions.keywordsDescription": "Other words that trigger a mention",
|
||||
"mobile.notification_settings_mentions.keywordsHelp": "Keywords are non-case sensitive and should be separated by a comma.",
|
||||
"mobile.notification_settings.mentions.sensitiveName": "Your case sensitive first name",
|
||||
"mobile.notification_settings.mentions.sensitiveUsername": "Your non-case sensitive username",
|
||||
"mobile.notification_settings_mentions.wordsTrigger": "WORDS THAT TRIGGER MENTIONS",
|
||||
"mobile.notification_settings_mobile.push_activity": "SEND NOTIFICATIONS FOR",
|
||||
"mobile.notification_settings_mobile.push_activity_android": "Send notifications for",
|
||||
"mobile.notification_settings_model.push_status": "TRIGGER PUSH NOTIFICATIONS WHEN",
|
||||
"mobile.notification_settings_model.push_status_android": "Trigger push notifications when",
|
||||
"mobile.offlineIndicator.connected": "Connected",
|
||||
"mobile.offlineIndicator.connecting": "Connecting...",
|
||||
"mobile.offlineIndicator.offline": "No internet connection",
|
||||
|
|
|
|||
Loading…
Reference in a new issue