From da8c09ff98a7922cfef3fc9a781546a2f8db9706 Mon Sep 17 00:00:00 2001 From: Elias Nahum Date: Thu, 8 Apr 2021 20:30:58 -0400 Subject: [PATCH] MM-31968 Save auto response props on keyboard done (#5273) --- app/mm-redux/types/users.ts | 4 + .../notification_settings.test.js.snap | 1 + .../notification_settings.js | 51 +++-- .../{index.js => index.ts} | 3 +- .../notification_settings_auto_responder.js | 200 ------------------ .../notification_settings_auto_responder.tsx | 186 ++++++++++++++++ .../notification_settings_mention_base.js | 2 - 7 files changed, 226 insertions(+), 221 deletions(-) rename app/screens/settings/notification_settings_auto_responder/{index.js => index.ts} (87%) delete mode 100644 app/screens/settings/notification_settings_auto_responder/notification_settings_auto_responder.js create mode 100644 app/screens/settings/notification_settings_auto_responder/notification_settings_auto_responder.tsx diff --git a/app/mm-redux/types/users.ts b/app/mm-redux/types/users.ts index b1e8fda43..5051548ee 100644 --- a/app/mm-redux/types/users.ts +++ b/app/mm-redux/types/users.ts @@ -5,7 +5,10 @@ import {Team} from './teams'; import {PostType} from './posts'; import {$ID, IDMappedObjects, RelationOneToMany, RelationOneToOne} from './utilities'; export type UserNotifyProps = { + auto_responder_active?: 'true' | 'false'; + auto_responder_message?: string; desktop: 'default' | 'all' | 'mention' | 'none'; + desktop_notification_sound?: string; desktop_sound: 'true' | 'false'; email: 'true' | 'false'; mark_unread: 'all' | 'mention'; @@ -15,6 +18,7 @@ export type UserNotifyProps = { first_name: 'true' | 'false'; channel: 'true' | 'false'; mention_keys: string; + user_id?: string; }; export type UserProfile = { id: string; diff --git a/app/screens/settings/notification_settings/__snapshots__/notification_settings.test.js.snap b/app/screens/settings/notification_settings/__snapshots__/notification_settings.test.js.snap index 02f5fdf22..936af58cb 100644 --- a/app/screens/settings/notification_settings/__snapshots__/notification_settings.test.js.snap +++ b/app/screens/settings/notification_settings/__snapshots__/notification_settings.test.js.snap @@ -99,6 +99,7 @@ NotificationSettings { "updateMeRequest": Object {}, }, "refs": Object {}, + "sanitizeNotificationProps": [Function], "saveAutoResponder": [Function], "saveNotificationProps": [Function], "setState": [Function], diff --git a/app/screens/settings/notification_settings/notification_settings.js b/app/screens/settings/notification_settings/notification_settings.js index e7ac2b59b..ad1b4ffe7 100644 --- a/app/screens/settings/notification_settings/notification_settings.js +++ b/app/screens/settings/notification_settings/notification_settings.js @@ -127,6 +127,22 @@ export default class NotificationSettings extends PureComponent { }); }; + saveAutoResponder = (notifyProps) => { + const {intl} = this.context; + + if (!notifyProps.auto_responder_message) { + notifyProps.auto_responder_message = intl.formatMessage({ + id: 'mobile.notification_settings.auto_responder.default_message', + defaultMessage: 'Hello, I am out of office and unable to respond to messages.', + }); + } + + if (this.shouldSaveAutoResponder(notifyProps)) { + const notify_props = this.sanitizeNotificationProps(notifyProps); + this.props.actions.updateMe({notify_props}); + } + }; + saveNotificationProps = (notifyProps) => { const {currentUser} = this.props; const prevProps = getNotificationProps(currentUser); @@ -136,10 +152,22 @@ export default class NotificationSettings extends PureComponent { }; if (!deepEqual(prevProps, notifyProps)) { - this.props.actions.updateMe({notify_props: updatedProps}); + const notify_props = this.sanitizeNotificationProps(updatedProps); + this.props.actions.updateMe({notify_props}); } }; + sanitizeNotificationProps = (notifyProps) => { + const sanitized = {...notifyProps}; + Reflect.deleteProperty(sanitized, 'showKeywordsModal'); + Reflect.deleteProperty(sanitized, 'showReplyModal'); + Reflect.deleteProperty(sanitized, 'androidKeywords'); + Reflect.deleteProperty(sanitized, 'newReplyValue'); + Reflect.deleteProperty(sanitized, 'user_id'); + + return sanitized; + } + /** * shouldSaveAutoResponder * @@ -150,28 +178,15 @@ export default class NotificationSettings extends PureComponent { * for some reason the update does not get received on mobile, it does for web */ shouldSaveAutoResponder = (notifyProps) => { - const {currentUserStatus} = this.props; + const {currentUser, currentUserStatus} = this.props; const {auto_responder_active: autoResponderActive} = notifyProps; + const prevProps = getNotificationProps(currentUser); const enabling = currentUserStatus !== General.OUT_OF_OFFICE && autoResponderActive === 'true'; const disabling = currentUserStatus === General.OUT_OF_OFFICE && autoResponderActive === 'false'; + const updatedMsg = prevProps.auto_responder_message !== notifyProps.auto_responder_message; - return enabling || disabling; - }; - - saveAutoResponder = (notifyProps) => { - const {intl} = this.context; - - if (!notifyProps.auto_responder_message || notifyProps.auto_responder_message === '') { - notifyProps.auto_responder_message = intl.formatMessage({ - id: 'mobile.notification_settings.auto_responder.default_message', - defaultMessage: 'Hello, I am out of office and unable to respond to messages.', - }); - } - - if (this.shouldSaveAutoResponder(notifyProps)) { - this.props.actions.updateMe({notify_props: notifyProps}); - } + return enabling || disabling || updatedMsg; }; render() { diff --git a/app/screens/settings/notification_settings_auto_responder/index.js b/app/screens/settings/notification_settings_auto_responder/index.ts similarity index 87% rename from app/screens/settings/notification_settings_auto_responder/index.js rename to app/screens/settings/notification_settings_auto_responder/index.ts index 48ef14232..db03f34a2 100644 --- a/app/screens/settings/notification_settings_auto_responder/index.js +++ b/app/screens/settings/notification_settings_auto_responder/index.ts @@ -5,9 +5,10 @@ import {connect} from 'react-redux'; import {getTheme} from '@mm-redux/selectors/entities/preferences'; import {getCurrentUserId, getStatusForUserId} from '@mm-redux/selectors/entities/users'; +import {GlobalState} from '@mm-redux/types/store'; import NotificationSettingsAutoResponder from './notification_settings_auto_responder'; -function mapStateToProps(state) { +function mapStateToProps(state: GlobalState) { const currentUserId = getCurrentUserId(state); const currentUserStatus = getStatusForUserId(state, currentUserId); diff --git a/app/screens/settings/notification_settings_auto_responder/notification_settings_auto_responder.js b/app/screens/settings/notification_settings_auto_responder/notification_settings_auto_responder.js deleted file mode 100644 index c8a7ee35a..000000000 --- a/app/screens/settings/notification_settings_auto_responder/notification_settings_auto_responder.js +++ /dev/null @@ -1,200 +0,0 @@ -// Copyright (c) 2015-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 {intlShape} from 'react-intl'; -import {SafeAreaView} from 'react-native-safe-area-context'; - -import {General} from '@mm-redux/constants'; -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 {getNotificationProps} from 'app/utils/notify_props'; -import { - changeOpacity, - makeStyleSheetFromTheme, - getKeyboardAppearanceFromTheme, -} from 'app/utils/theme'; -import {t} from 'app/utils/i18n'; - -import Section from 'app/screens/settings/section'; -import SectionItem from 'app/screens/settings/section_item'; - -export default class NotificationSettingsAutoResponder extends PureComponent { - static propTypes = { - currentUser: PropTypes.object.isRequired, - onBack: PropTypes.func.isRequired, - theme: PropTypes.object.isRequired, - currentUserStatus: PropTypes.string.isRequired, - }; - - static contextTypes = { - intl: intlShape, - }; - - constructor(props, context) { - super(props, context); - const {currentUser} = props; - const {intl} = this.context; - const notifyProps = getNotificationProps(currentUser); - - this.autoresponderRef = React.createRef(); - - const autoResponderDefault = intl.formatMessage({ - id: 'mobile.notification_settings.auto_responder.default_message', - defaultMessage: 'Hello, I am out of office and unable to respond to messages.', - }); - - let autoResponderActive = 'false'; - if (props.currentUserStatus === General.OUT_OF_OFFICE && notifyProps.auto_responder_active) { - autoResponderActive = 'true'; - } - - this.state = { - ...notifyProps, - auto_responder_active: autoResponderActive, - auto_responder_message: notifyProps.auto_responder_message || autoResponderDefault, - }; - } - - componentWillUnmount() { - this.saveUserNotifyProps(); - } - - componentDidMount() { - setTimeout(() => { - requestAnimationFrame(() => { - if (this.autoresponderRef.current) { - this.autoresponderRef.current.focus(); - } - }); - }, 500); - } - - saveUserNotifyProps = () => { - this.props.onBack({ - ...this.state, - user_id: this.props.currentUser.id, - }); - }; - - onAutoResponseToggle = (active) => { - if (active) { - this.setState({auto_responder_active: 'true'}); - return; - } - this.setState({auto_responder_active: 'false'}); - }; - - onAutoResponseChangeText = (message) => { - this.setState({auto_responder_message: message}); - }; - - render() { - const {theme} = this.props; - const { - auto_responder_active: autoResponderActive, - auto_responder_message: autoResponderMessage, - } = this.state; - const style = getStyleSheet(theme); - - const autoResponderActiveLabel = ( - - ); - - return ( - - - -
- -
- {autoResponderActive === 'true' && ( -
- - - -
- )} - -
-
- ); - } -} - -const getStyleSheet = makeStyleSheetFromTheme((theme) => { - return { - container: { - flex: 1, - backgroundColor: theme.centerChannelBg, - }, - wrapper: { - backgroundColor: changeOpacity(theme.centerChannelColor, 0.06), - flex: 1, - 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, - }, - footer: { - marginHorizontal: 15, - fontSize: 12, - color: changeOpacity(theme.centerChannelColor, 0.5), - }, - }; -}); diff --git a/app/screens/settings/notification_settings_auto_responder/notification_settings_auto_responder.tsx b/app/screens/settings/notification_settings_auto_responder/notification_settings_auto_responder.tsx new file mode 100644 index 000000000..e920d52ff --- /dev/null +++ b/app/screens/settings/notification_settings_auto_responder/notification_settings_auto_responder.tsx @@ -0,0 +1,186 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import React, {useEffect, useRef, useState} from 'react'; + +import {View} from 'react-native'; +import {intlShape, injectIntl} from 'react-intl'; +import {SafeAreaView} from 'react-native-safe-area-context'; + +import {popTopScreen} from '@actions/navigation'; +import FormattedText from '@components/formatted_text'; +import StatusBar from '@components/status_bar'; +import TextInputWithLocalizedPlaceholder from '@components/text_input_with_localized_placeholder'; +import {General} from '@mm-redux/constants'; +import Section from '@screens/settings/section'; +import SectionItem from '@screens/settings/section_item'; +import {t} from '@utils/i18n'; +import {getNotificationProps} from '@utils/notify_props'; +import { + changeOpacity, + makeStyleSheetFromTheme, + getKeyboardAppearanceFromTheme, +} from '@utils/theme'; +import {UserNotifyProps, UserProfile} from '@mm-redux/types/users'; +import {Theme} from '@mm-redux/types/preferences'; + +interface NotificationSettingsAutoResponderProps { + currentUser: UserProfile; + currentUserStatus: string; + intl: typeof intlShape; + onBack: (notifyProps: UserNotifyProps) => void; + theme: Theme; +} + +const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => { + return { + container: { + flex: 1, + backgroundColor: theme.centerChannelBg, + }, + wrapper: { + backgroundColor: changeOpacity(theme.centerChannelColor, 0.06), + flex: 1, + 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, + }, + footer: { + marginHorizontal: 15, + fontSize: 12, + color: changeOpacity(theme.centerChannelColor, 0.5), + }, + }; +}); + +const NotificationSettingsAutoResponder = ({currentUser, currentUserStatus, intl, onBack, theme} : NotificationSettingsAutoResponderProps) => { + const autoresponderRef = useRef(null); + const [notifyProps, setNotifyProps] = useState(getNotificationProps(currentUser)); + const style = getStyleSheet(theme); + + const onAutoResponseToggle = (active: boolean) => { + setNotifyProps({ + ...notifyProps, + auto_responder_active: active ? 'true' : 'false', + }); + }; + + const onAutoResponseChangeText = (message: string) => { + setNotifyProps({ + ...notifyProps, + auto_responder_message: message, + }); + }; + + const onSubmitEditing = () => { + popTopScreen(); + }; + + useEffect(() => { + const autoResponderDefault = intl.formatMessage({ + id: 'mobile.notification_settings.auto_responder.default_message', + defaultMessage: 'Hello, I am out of office and unable to respond to messages.', + }); + + setNotifyProps({ + ...notifyProps, + auto_responder_active: (currentUserStatus === General.OUT_OF_OFFICE && notifyProps.auto_responder_active) ? 'true' : 'false', + auto_responder_message: notifyProps.auto_responder_message || autoResponderDefault, + }); + }, []); + + useEffect(() => { + const autoresponderTimeout = setTimeout(() => { + autoresponderRef.current?.focus(); + }, 500); + + return () => { + clearTimeout(autoresponderTimeout); + + onBack({ + ...notifyProps, + user_id: currentUser.id, + }); + }; + }, [notifyProps]); + + const autoResponderActiveLabel = ( + + ); + const { + auto_responder_active: autoResponderActive, + auto_responder_message: autoResponderMessage, + } = notifyProps; + + return ( + + + +
+ +
+ {autoResponderActive === 'true' && ( +
+ + + +
+ )} + +
+
+ ); +}; + +export default injectIntl(NotificationSettingsAutoResponder); diff --git a/app/screens/settings/notification_settings_mentions/notification_settings_mention_base.js b/app/screens/settings/notification_settings_mentions/notification_settings_mention_base.js index 7d8c6e20d..dc15f8526 100644 --- a/app/screens/settings/notification_settings_mentions/notification_settings_mention_base.js +++ b/app/screens/settings/notification_settings_mentions/notification_settings_mention_base.js @@ -111,8 +111,6 @@ export default class NotificationSettingsMentionsBase extends PureComponent { } mentionKeys = mentionKeys.join(','); - Reflect.deleteProperty(notifyProps, 'showKeywordsModal'); - Reflect.deleteProperty(notifyProps, 'showReplyModal'); this.props.onBack({ ...notifyProps,