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 };