From 8b62ec71333873ec1f6b9aeddb8d1ec815156e7d Mon Sep 17 00:00:00 2001 From: Chris Duarte Date: Fri, 19 Jan 2018 14:37:03 -0800 Subject: [PATCH] Clock Display Settings (#1334) * Clock Display Settings Add api call for saving militaryTime WIP Android Clock Display Modal Clean up styles for iOS and Android header info no longer required in Sectin component Add Display Settings option Clean up Advance Settings * Add requested changes * Move Display options below Notifications option --- .../clock_display/clock_display.android.js | 211 ++++++++++++++++++ .../clock_display/clock_display.ios.js | 77 +++++++ .../clock_display/clock_display_base.js | 51 +++++ app/screens/clock_display/index.js | 33 +++ app/screens/index.js | 4 + .../display_settings/display_settings.js | 118 ++++++++++ .../settings/display_settings/index.js | 16 ++ app/screens/settings/general/settings.js | 25 +++ app/screens/settings/section.js | 20 +- 9 files changed, 547 insertions(+), 8 deletions(-) create mode 100644 app/screens/clock_display/clock_display.android.js create mode 100644 app/screens/clock_display/clock_display.ios.js create mode 100644 app/screens/clock_display/clock_display_base.js create mode 100644 app/screens/clock_display/index.js create mode 100644 app/screens/settings/display_settings/display_settings.js create mode 100644 app/screens/settings/display_settings/index.js 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 };