diff --git a/app/screens/clock_display/clock_display.android.js b/app/screens/clock_display/clock_display.android.js
new file mode 100644
index 000000000..af72ae7a1
--- /dev/null
+++ b/app/screens/clock_display/clock_display.android.js
@@ -0,0 +1,211 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import {
+ TouchableOpacity,
+ Modal,
+ View
+} from 'react-native';
+
+import StatusBar from 'app/components/status_bar';
+import FormattedText from 'app/components/formatted_text';
+import RadioButtonGroup from 'app/components/radio_button';
+
+import {changeOpacity, makeStyleSheetFromTheme} from 'app/utils/theme';
+
+import ClockDisplayBase from './clock_display_base';
+
+export default class ClockDisplay extends ClockDisplayBase {
+ static propTypes = {
+ showModal: PropTypes.bool.isRequired,
+ militaryTime: PropTypes.bool.isRequired,
+ onClose: PropTypes.func.isRequired
+ };
+
+ setMilitaryTime = (value) => {
+ this.setState({
+ newMilitaryTime: value
+ });
+ };
+
+ closeModal = () => {
+ const {militaryTime, onClose} = this.props;
+ this.setState({
+ newMilitaryTime: militaryTime
+ });
+
+ onClose();
+ };
+
+ saveSelection = () => {
+ const {newMilitaryTime} = this.state;
+ const {onClose} = this.props;
+ this.saveClockDisplayPreference(newMilitaryTime);
+ onClose();
+ };
+
+ renderClockDisplayModal = (style) => {
+ const {showModal} = this.props;
+ const {intl} = this.context;
+ const {newMilitaryTime} = this.state;
+
+ const options = [{
+ label: intl.formatMessage({
+ id: 'user.settings.display.normalClock',
+ defaultMessage: '12-hour clock (example: 4:00 PM)'
+ }),
+ value: 'false',
+ checked: newMilitaryTime === 'false'
+ }, {
+ label: intl.formatMessage({
+ id: 'user.settings.display.militaryClock',
+ defaultMessage: '24-hour clock (example: 16:00)'
+ }),
+ value: 'true',
+ checked: newMilitaryTime === 'true'
+ }];
+
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+ };
+
+ render() {
+ const {theme} = this.props;
+ const style = getStyleSheet(theme);
+
+ return (
+
+
+
+ {this.renderClockDisplayModal(style)}
+
+
+ );
+ }
+}
+
+const getStyleSheet = makeStyleSheetFromTheme((theme) => {
+ return {
+ container: {
+ flex: 1,
+ backgroundColor: theme.centerChannelBg
+ },
+ wrapper: {
+ backgroundColor: changeOpacity(theme.centerChannelColor, 0.06),
+ flex: 1
+ },
+ separator: {
+ 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
+ },
+ 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
+ },
+ modalFooterButtonSpacer: {
+ marginRight: 10
+ }
+ };
+});
diff --git a/app/screens/clock_display/clock_display.ios.js b/app/screens/clock_display/clock_display.ios.js
new file mode 100644
index 000000000..0142e2f80
--- /dev/null
+++ b/app/screens/clock_display/clock_display.ios.js
@@ -0,0 +1,77 @@
+import React from 'react';
+import {
+ View
+} from 'react-native';
+
+import StatusBar from 'app/components/status_bar';
+import Section from 'app/screens/settings/section';
+import SectionItem from 'app/screens/settings/section_item';
+import FormattedText from 'app/components/formatted_text';
+
+import {changeOpacity, makeStyleSheetFromTheme} from 'app/utils/theme';
+
+import ClockDisplayBase from './clock_display_base';
+
+export default class ClockDisplay extends ClockDisplayBase {
+ render() {
+ const {theme} = this.props;
+ const {newMilitaryTime} = this.state;
+ const style = getStyleSheet(theme);
+
+ return (
+
+
+
+
+
+ )}
+ action={this.setMilitaryTime}
+ actionType='select'
+ actionValue='false'
+ selected={newMilitaryTime === 'false'}
+ theme={theme}
+ />
+
+
+ )}
+ action={this.setMilitaryTime}
+ actionType='select'
+ actionValue='true'
+ selected={newMilitaryTime === 'true'}
+ theme={theme}
+ />
+
+
+
+ );
+ }
+}
+
+const getStyleSheet = makeStyleSheetFromTheme((theme) => {
+ return {
+ container: {
+ flex: 1,
+ backgroundColor: theme.centerChannelBg
+ },
+ wrapper: {
+ backgroundColor: changeOpacity(theme.centerChannelColor, 0.06),
+ flex: 1,
+ paddingTop: 35
+ }
+ };
+});
diff --git a/app/screens/clock_display/clock_display_base.js b/app/screens/clock_display/clock_display_base.js
new file mode 100644
index 000000000..cef94e7df
--- /dev/null
+++ b/app/screens/clock_display/clock_display_base.js
@@ -0,0 +1,51 @@
+// 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';
+
+import Preferences from 'mattermost-redux/constants/preferences';
+
+export default class ClockDisplayBase extends PureComponent {
+ static propTypes = {
+ theme: PropTypes.object.isRequired,
+ militaryTime: PropTypes.string.isRequired,
+ userId: PropTypes.string.isRequired,
+ actions: PropTypes.shape({
+ savePreferences: PropTypes.func.isRequired
+ }).isRequired
+ };
+ static contextTypes = {
+ intl: intlShape.isRequired
+ };
+
+ constructor(props) {
+ super(props);
+
+ this.state = {
+ newMilitaryTime: props.militaryTime
+ };
+ }
+
+ setMilitaryTime = (value) => {
+ this.setState({
+ newMilitaryTime: value
+ });
+
+ this.saveClockDisplayPreference(value);
+ };
+
+ saveClockDisplayPreference = (newMilitaryTime) => {
+ const {userId, actions: {savePreferences}} = this.props;
+
+ const timePreference = {
+ user_id: userId,
+ category: Preferences.CATEGORY_DISPLAY_SETTINGS,
+ name: 'use_military_time',
+ value: newMilitaryTime
+ };
+
+ savePreferences(userId, [timePreference]);
+ }
+}
diff --git a/app/screens/clock_display/index.js b/app/screens/clock_display/index.js
new file mode 100644
index 000000000..e327c4eda
--- /dev/null
+++ b/app/screens/clock_display/index.js
@@ -0,0 +1,33 @@
+// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+import {bindActionCreators} from 'redux';
+import {connect} from 'react-redux';
+
+import {getTheme, get as getPreference} from 'mattermost-redux/selectors/entities/preferences';
+import {getCurrentUserId} from 'mattermost-redux/selectors/entities/users';
+import {savePreferences} from 'mattermost-redux/actions/preferences';
+import Preferences from 'mattermost-redux/constants/preferences';
+
+import ClockDisplay from './clock_display';
+
+function mapStateToProps(state) {
+ const militaryTime = getPreference(state, Preferences.CATEGORY_DISPLAY_SETTINGS, 'use_military_time') || 'false';
+ const currentUserId = getCurrentUserId(state);
+
+ return {
+ userId: currentUserId,
+ theme: getTheme(state),
+ militaryTime
+ };
+}
+
+function mapDispatchToProps(dispatch) {
+ return {
+ actions: bindActionCreators({
+ savePreferences
+ }, dispatch)
+ };
+}
+
+export default connect(mapStateToProps, mapDispatchToProps)(ClockDisplay);
diff --git a/app/screens/index.js b/app/screens/index.js
index 6946f9111..6488414b6 100644
--- a/app/screens/index.js
+++ b/app/screens/index.js
@@ -14,8 +14,10 @@ import ChannelInfo from 'app/screens/channel_info';
import ChannelMembers from 'app/screens/channel_members';
import ChannelPeek from 'app/screens/channel_peek';
import ClientUpgrade from 'app/screens/client_upgrade';
+import ClockDisplay from 'app/screens/clock_display';
import Code from 'app/screens/code';
import CreateChannel from 'app/screens/create_channel';
+import DisplaySettings from 'app/screens/settings/display_settings';
import EditChannel from 'app/screens/edit_channel';
import EditPost from 'app/screens/edit_post';
import EditProfile from 'app/screens/edit_profile';
@@ -68,8 +70,10 @@ export function registerScreens(store, Provider) {
Navigation.registerComponent('ChannelMembers', () => wrapWithContextProvider(ChannelMembers), store, Provider);
Navigation.registerComponent('ChannelPeek', () => wrapWithContextProvider(ChannelPeek), store, Provider);
Navigation.registerComponent('ClientUpgrade', () => wrapWithContextProvider(ClientUpgrade), store, Provider);
+ Navigation.registerComponent('ClockDisplay', () => wrapWithContextProvider(ClockDisplay), store, Provider);
Navigation.registerComponent('Code', () => wrapWithContextProvider(Code), store, Provider);
Navigation.registerComponent('CreateChannel', () => wrapWithContextProvider(CreateChannel), store, Provider);
+ Navigation.registerComponent('DisplaySettings', () => wrapWithContextProvider(DisplaySettings), store, Provider);
Navigation.registerComponent('EditChannel', () => wrapWithContextProvider(EditChannel), store, Provider);
Navigation.registerComponent('EditPost', () => wrapWithContextProvider(EditPost), store, Provider);
Navigation.registerComponent('EditProfile', () => wrapWithContextProvider(EditProfile), store, Provider);
diff --git a/app/screens/settings/display_settings/display_settings.js b/app/screens/settings/display_settings/display_settings.js
new file mode 100644
index 000000000..6afe6c9b4
--- /dev/null
+++ b/app/screens/settings/display_settings/display_settings.js
@@ -0,0 +1,118 @@
+// 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 {intlShape} from 'react-intl';
+import {
+ Platform,
+ View
+} from 'react-native';
+
+import SettingsItem from 'app/screens/settings/settings_item';
+import StatusBar from 'app/components/status_bar';
+import {wrapWithPreventDoubleTap} from 'app/utils/tap';
+import {changeOpacity, makeStyleSheetFromTheme} from 'app/utils/theme';
+
+import ClockDisplay from 'app/screens/clock_display';
+
+export default class DisplaySettings extends PureComponent {
+ static propTypes = {
+ navigator: PropTypes.object.isRequired,
+ theme: PropTypes.object.isRequired
+ };
+
+ static contextTypes = {
+ intl: intlShape.isRequired
+ };
+
+ state = {
+ showClockDisplaySettings: false
+ };
+
+ goToClockDisplaySettings = wrapWithPreventDoubleTap(() => {
+ const {navigator, theme} = this.props;
+ const {intl} = this.context;
+
+ if (Platform.OS === 'ios') {
+ navigator.push({
+ screen: 'ClockDisplay',
+ title: intl.formatMessage({id: 'user.settings.display.clockDisplay', defaultMessage: 'Clock Display'}),
+ animated: true,
+ backButtonTitle: '',
+ navigatorStyle: {
+ navBarTextColor: theme.sidebarHeaderTextColor,
+ navBarBackgroundColor: theme.sidebarHeaderBg,
+ navBarButtonColor: theme.sidebarHeaderTextColor,
+ screenBackgroundColor: theme.centerChannelBg
+ }
+ });
+ return;
+ }
+
+ this.setState({showClockDisplaySettings: true});
+ });
+
+ closeClockDisplaySettings = () => {
+ this.setState({showClockDisplaySettings: false});
+ };
+
+ render() {
+ const {theme} = this.props;
+ const {showClockDisplaySettings} = this.state;
+ const style = getStyleSheet(theme);
+
+ let clockDisplayModal;
+ if (Platform.OS === 'android') {
+ clockDisplayModal = (
+
+ );
+ }
+
+ return (
+
+
+
+
+
+
+
+ {clockDisplayModal}
+
+ );
+ }
+}
+
+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
+ }
+ };
+});
diff --git a/app/screens/settings/display_settings/index.js b/app/screens/settings/display_settings/index.js
new file mode 100644
index 000000000..dafb1276d
--- /dev/null
+++ b/app/screens/settings/display_settings/index.js
@@ -0,0 +1,16 @@
+// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+import {connect} from 'react-redux';
+
+import {getTheme} from 'mattermost-redux/selectors/entities/preferences';
+
+import DisplaySettings from './display_settings';
+
+function mapStateToProps(state) {
+ return {
+ theme: getTheme(state)
+ };
+}
+
+export default connect(mapStateToProps)(DisplaySettings);
diff --git a/app/screens/settings/general/settings.js b/app/screens/settings/general/settings.js
index f6d063672..828d9b14e 100644
--- a/app/screens/settings/general/settings.js
+++ b/app/screens/settings/general/settings.js
@@ -105,6 +105,22 @@ class Settings extends PureComponent {
});
});
+ goToDisplaySettings = wrapWithPreventDoubleTap(() => {
+ const {intl, navigator, theme} = this.props;
+ navigator.push({
+ screen: 'DisplaySettings',
+ title: intl.formatMessage({id: 'user.settings.modal.display', defaultMessage: 'Display'}),
+ animated: true,
+ backButtonTitle: '',
+ navigatorStyle: {
+ navBarTextColor: theme.sidebarHeaderTextColor,
+ navBarBackgroundColor: theme.sidebarHeaderBg,
+ navBarButtonColor: theme.sidebarHeaderTextColor,
+ screenBackgroundColor: theme.centerChannelBg
+ }
+ });
+ });
+
goToAdvancedSettings = wrapWithPreventDoubleTap(() => {
const {intl, navigator, theme} = this.props;
navigator.push({
@@ -221,6 +237,15 @@ class Settings extends PureComponent {
showArrow={showArrow}
theme={theme}
/>
+
{showTeams &&
-
+ {(headerId && !disableHeader) &&
+
+ }
{children}
@@ -50,12 +53,13 @@ function section(props) {
section.propTypes = {
children: PropTypes.node.isRequired,
+ disableHeader: PropTypes.bool,
disableFooter: PropTypes.bool,
footerDefaultMessage: PropTypes.string,
footerId: PropTypes.string,
footerValues: PropTypes.object,
- headerDefaultMessage: PropTypes.string.isRequired,
- headerId: PropTypes.string.isRequired,
+ headerDefaultMessage: PropTypes.string,
+ headerId: PropTypes.string,
headerValues: PropTypes.object,
theme: PropTypes.object.isRequired
};