diff --git a/lib/shared/thread-settings-notifications-utils.js b/lib/shared/thread-settings-notifications-utils.js index 307067e58..b2061ab8d 100644 --- a/lib/shared/thread-settings-notifications-utils.js +++ b/lib/shared/thread-settings-notifications-utils.js @@ -1,182 +1,156 @@ // @flow import * as React from 'react'; import { threadIsSidebar } from './thread-utils.js'; import { updateSubscriptionActionTypes, useUpdateSubscription, } from '../actions/user-actions.js'; import { useCanPromoteSidebar } from '../hooks/promote-sidebar.react.js'; import { createLoadingStatusSelector } from '../selectors/loading-selectors.js'; import { threadInfoSelector } from '../selectors/thread-selectors.js'; import type { ThreadInfo } from '../types/minimally-encoded-thread-permissions-types.js'; import { threadTypeIsThick } from '../types/thread-types-enum.js'; import { useDispatchActionPromise } from '../utils/redux-promise-utils.js'; import { useSelector } from '../utils/redux-utils.js'; type NotificationSettings = 'home' | 'notif-count-only' | 'muted'; const threadSettingsNotificationsCopy = { BANNER_NOTIFS: 'Banner notifs', NOTIF_COUNT: 'Notif count', IN_HOME_TAB: 'Lives in Home tab', IN_MUTED_TAB: 'Lives in Muted tab', HOME: 'Home', NOTIF_COUNT_ONLY: 'Home (notif count only)', MUTED: 'Muted', SIDEBAR_TITLE: 'Thread notifications', CHANNEL_TITLE: 'Channel notifications', - IS_SIDEBAR: - 'It’s not possible to move this thread to Muted. ' + - 'That’s because Comm’s design always shows threads ' + - 'underneath their parent in the Inbox, which means ' + - 'that if a thread’s parent is in Home, the thread ' + - 'must also be there.', - IS_SIDEBAR_CAN_PROMOTE: - 'If you want to move this thread to Muted, ' + - 'you can either move the parent to Muted, ' + - 'or you can promote the thread to a channel.', - IS_SIDEBAR_CAN_NOT_PROMOTE: - 'If you want to move this thread to Muted, ' + - 'you’ll have to move the parent to Muted.', - PARENT_THREAD_IS_MUTED: - 'It’s not possible to change the notif settings for a thread ' + - 'whose parent is in Muted. That’s because Comm’s design ' + - 'always shows threads underneath their parent in the Inbox, ' + - 'which means that if a thread’s parent is in Muted, the ' + - 'thread must also be there.', - PARENT_THREAD_IS_MUTED_CAN_PROMOTE: - 'If you want to change the notif settings for this thread, ' + - 'you can either change the notif settings for the parent, ' + - 'or you can promote the thread to a channel.', - PARENT_THREAD_IS_MUTED_CAN_NOT_PROMOTE: - 'If you want to change the notif settings for this thread, ' + - 'you’ll have to change the notif settings for the parent.', }; const updateSubscriptionLoadingStatusSelector = createLoadingStatusSelector( updateSubscriptionActionTypes, ); function useThreadSettingsNotifications( threadInfo: ThreadInfo, onSuccessCallback: () => mixed, ): { +notificationSettings: NotificationSettings, +onHomeSelected: () => mixed, +onNotifCountOnlySelected: () => mixed, +onMutedSelected: () => mixed, +saveButtonDisabled: boolean, +onSave: () => mixed, +isSidebar: boolean, +canPromoteSidebar: boolean, +parentThreadIsMuted: boolean, } { const subscription = threadInfo.currentUser.subscription; const initialThreadSetting = React.useMemo(() => { if (!subscription.home) { return 'muted'; } if (!subscription.pushNotifs) { return 'notif-count-only'; } return 'home'; }, [subscription.home, subscription.pushNotifs]); const [notificationSettings, setNotificationSettings] = React.useState(initialThreadSetting); const onHomeSelected = React.useCallback( () => setNotificationSettings('home'), [], ); const onNotifCountOnlySelected = React.useCallback( () => setNotificationSettings('notif-count-only'), [], ); const onMutedSelected = React.useCallback( () => setNotificationSettings('muted'), [], ); const dispatchActionPromise = useDispatchActionPromise(); const callUpdateSubscription = useUpdateSubscription(); const updateSubscriptionPromise = React.useCallback(async () => { const updateSubscriptionRequest = { threadID: threadInfo.id, updatedFields: { home: notificationSettings !== 'muted', pushNotifs: notificationSettings === 'home', }, }; const updateSubscriptionInput = threadTypeIsThick(threadInfo.type) ? { thick: true, threadInfo, ...updateSubscriptionRequest, } : { thick: false, ...updateSubscriptionRequest, }; const res = await callUpdateSubscription(updateSubscriptionInput); onSuccessCallback(); return res; }, [ callUpdateSubscription, notificationSettings, onSuccessCallback, threadInfo, ]); const updateSubscriptionLoadingStatus = useSelector( updateSubscriptionLoadingStatusSelector, ); const isLoading = updateSubscriptionLoadingStatus === 'loading'; const saveButtonDisabled = isLoading || notificationSettings === initialThreadSetting; const onSave = React.useCallback(() => { if (saveButtonDisabled) { return; } void dispatchActionPromise( updateSubscriptionActionTypes, updateSubscriptionPromise(), ); }, [saveButtonDisabled, dispatchActionPromise, updateSubscriptionPromise]); const isSidebar = threadIsSidebar(threadInfo); const { parentThreadID } = threadInfo; const parentThreadInfo = useSelector(state => parentThreadID ? threadInfoSelector(state)[parentThreadID] : null, ); const canPromoteSidebar = useCanPromoteSidebar(threadInfo, parentThreadInfo); const parentThreadIsMuted = isSidebar && !parentThreadInfo?.currentUser.subscription.home; return { notificationSettings, onHomeSelected, onNotifCountOnlySelected, onMutedSelected, saveButtonDisabled, onSave, isSidebar, canPromoteSidebar, parentThreadIsMuted, }; } export { threadSettingsNotificationsCopy, useThreadSettingsNotifications }; diff --git a/native/chat/settings/thread-settings-notifications.react.js b/native/chat/settings/thread-settings-notifications.react.js index 588f5f2e4..ee0d1798f 100644 --- a/native/chat/settings/thread-settings-notifications.react.js +++ b/native/chat/settings/thread-settings-notifications.react.js @@ -1,384 +1,321 @@ // @flow import * as React from 'react'; import { View, Text } from 'react-native'; import { threadSettingsNotificationsCopy, useThreadSettingsNotifications, } from 'lib/shared/thread-settings-notifications-utils.js'; import type { ThreadInfo } from 'lib/types/minimally-encoded-thread-permissions-types.js'; import EnumSettingsOption from '../../components/enum-settings-option.react.js'; import SWMansionIcon from '../../components/swmansion-icon.react.js'; import HeaderRightTextButton from '../../navigation/header-right-text-button.react.js'; import type { NavigationRoute } from '../../navigation/route-names.js'; import { useStyles, useColors } from '../../themes/colors.js'; import AllNotifsIllustration from '../../vectors/all-notifs-illustration.react.js'; import BadgeNotifsIllustration from '../../vectors/badge-notifs-illustration.react.js'; import MutedNotifsIllustration from '../../vectors/muted-notifs-illustration.react.js'; import type { ChatNavigationProp } from '../chat.react.js'; export type ThreadSettingsNotificationsParams = { +threadInfo: ThreadInfo, }; type NotificationDescriptionProps = { +selected: boolean, +bannerNotifsEnabled: boolean, +notifCountEnabled: boolean, +livesInHomeTab: boolean, }; function NotificationDescription( props: NotificationDescriptionProps, ): React.Node { const { selected, bannerNotifsEnabled, notifCountEnabled, livesInHomeTab } = props; const styles = useStyles(unboundStyles); const colors = useColors(); const bannerNotifsDescriptionTextStyles = React.useMemo(() => { const style = [styles.notificationOptionDescriptionText]; if (selected && !bannerNotifsEnabled) { style.push(styles.notificationOptionDescriptionTextDisabledSelected); } else if (!bannerNotifsEnabled) { style.push(styles.notificationOptionDescriptionTextDisabled); } return style; }, [ bannerNotifsEnabled, selected, styles.notificationOptionDescriptionText, styles.notificationOptionDescriptionTextDisabled, styles.notificationOptionDescriptionTextDisabledSelected, ]); const notifCountDescriptionTextStyles = React.useMemo(() => { const style = [styles.notificationOptionDescriptionText]; if (selected && !notifCountEnabled) { style.push(styles.notificationOptionDescriptionTextDisabledSelected); } else if (!notifCountEnabled) { style.push(styles.notificationOptionDescriptionTextDisabled); } return style; }, [ notifCountEnabled, selected, styles.notificationOptionDescriptionText, styles.notificationOptionDescriptionTextDisabled, styles.notificationOptionDescriptionTextDisabledSelected, ]); let bannerNotifsIconColor = colors.panelForegroundSecondaryLabel; if (selected && !bannerNotifsEnabled) { bannerNotifsIconColor = colors.panelInputSecondaryForeground; } else if (!bannerNotifsEnabled) { bannerNotifsIconColor = colors.panelSecondaryForeground; } let notifCountIconColor = colors.panelForegroundSecondaryLabel; if (selected && !notifCountEnabled) { notifCountIconColor = colors.panelInputSecondaryForeground; } else if (!notifCountEnabled) { notifCountIconColor = colors.panelSecondaryForeground; } return ( <> {threadSettingsNotificationsCopy.BANNER_NOTIFS} {threadSettingsNotificationsCopy.NOTIF_COUNT} {livesInHomeTab ? threadSettingsNotificationsCopy.IN_HOME_TAB : threadSettingsNotificationsCopy.IN_MUTED_TAB} ); } type Props = { +navigation: ChatNavigationProp<'ThreadSettingsNotifications'>, +route: NavigationRoute<'ThreadSettingsNotifications'>, }; function ThreadSettingsNotifications(props: Props): React.Node { const { navigation: { setOptions, goBack }, route: { params: { threadInfo }, }, } = props; const { notificationSettings, onHomeSelected, onNotifCountOnlySelected, onMutedSelected, saveButtonDisabled, onSave, - isSidebar, - canPromoteSidebar, - parentThreadIsMuted, } = useThreadSettingsNotifications(threadInfo, goBack); React.useEffect(() => { setOptions({ - headerRight: () => - parentThreadIsMuted ? null : ( - - ), + headerRight: () => ( + + ), }); - }, [saveButtonDisabled, onSave, setOptions, parentThreadIsMuted]); + }, [saveButtonDisabled, onSave, setOptions]); const styles = useStyles(unboundStyles); const allNotificationsIllustration = React.useMemo( () => ( ), [styles.notificationOptionIconContainer], ); const notifCountOnlyIllustration = React.useMemo( () => ( ), [styles.notificationOptionIconContainer], ); const mutedIllustration = React.useMemo( () => ( ), [styles.notificationOptionIconContainer], ); const allNotificationsDescription = React.useMemo( () => ( ), [notificationSettings], ); const notifCountOnlyDescription = React.useMemo( () => ( ), [notificationSettings], ); const mutedDescription = React.useMemo( () => ( ), [notificationSettings], ); - const noticeText = React.useMemo(() => { - if (!isSidebar) { - return null; - } - - return ( - - - {threadSettingsNotificationsCopy.IS_SIDEBAR} - - - {canPromoteSidebar - ? threadSettingsNotificationsCopy.IS_SIDEBAR_CAN_PROMOTE - : threadSettingsNotificationsCopy.IS_SIDEBAR_CAN_NOT_PROMOTE} - - - ); - }, [ - canPromoteSidebar, - isSidebar, - styles.noticeText, - styles.noticeTextContainer, - ]); - const threadSettingsNotifications = React.useMemo(() => { - if (parentThreadIsMuted) { - return ( - - - {threadSettingsNotificationsCopy.PARENT_THREAD_IS_MUTED} - - - {canPromoteSidebar - ? threadSettingsNotificationsCopy.PARENT_THREAD_IS_MUTED_CAN_PROMOTE - : threadSettingsNotificationsCopy.PARENT_THREAD_IS_MUTED_CAN_NOT_PROMOTE} - - - ); - } - return ( - {noticeText} ); }, [ - parentThreadIsMuted, styles.container, styles.enumSettingsOptionContainer, - styles.parentThreadIsMutedNoticeContainerStyle, - styles.parentThreadIsMutedNoticeText, notificationSettings, onHomeSelected, allNotificationsDescription, allNotificationsIllustration, onNotifCountOnlySelected, notifCountOnlyDescription, notifCountOnlyIllustration, onMutedSelected, mutedDescription, mutedIllustration, - isSidebar, - noticeText, - canPromoteSidebar, ]); return threadSettingsNotifications; } const unboundStyles = { container: { backgroundColor: 'panelForeground', }, enumSettingsOptionContainer: { padding: 8, }, notificationOptionIconContainer: { justifyContent: 'center', marginLeft: 8, marginRight: 16, }, notificationOptionDescriptionListItem: { flexDirection: 'row', alignItems: 'center', marginTop: 4, }, notificationOptionDescriptionText: { color: 'panelForegroundSecondaryLabel', marginLeft: 4, fontSize: 14, }, notificationOptionDescriptionTextDisabled: { textDecorationLine: 'line-through', color: 'panelSecondaryForeground', }, notificationOptionDescriptionTextDisabledSelected: { color: 'panelInputSecondaryForeground', textDecorationLine: 'line-through', }, noticeTextContainer: { padding: 16, }, noticeText: { color: 'panelForegroundSecondaryLabel', textAlign: 'center', fontSize: 14, lineHeight: 18, marginVertical: 8, }, - parentThreadIsMutedNoticeContainerStyle: { - backgroundColor: 'panelForeground', - paddingHorizontal: 16, - paddingVertical: 8, - }, - parentThreadIsMutedNoticeText: { - color: 'panelForegroundSecondaryLabel', - fontSize: 16, - lineHeight: 20, - textAlign: 'left', - marginVertical: 8, - }, }; export default ThreadSettingsNotifications; diff --git a/web/modals/threads/notifications/notifications-modal.react.js b/web/modals/threads/notifications/notifications-modal.react.js index 44cfd20ff..c5b2c0b2b 100644 --- a/web/modals/threads/notifications/notifications-modal.react.js +++ b/web/modals/threads/notifications/notifications-modal.react.js @@ -1,219 +1,170 @@ // @flow import * as React from 'react'; import { threadInfoSelector } from 'lib/selectors/thread-selectors.js'; import { threadSettingsNotificationsCopy, useThreadSettingsNotifications, } from 'lib/shared/thread-settings-notifications-utils.js'; import css from './notifications-modal.css'; import AllNotifsIllustration from '../../../assets/all-notifs.react.js'; import BadgeNotifsIllustration from '../../../assets/badge-notifs.react.js'; import MutedNotifsIllustration from '../../../assets/muted-notifs.react.js'; import Button from '../../../components/button.react.js'; import EnumSettingsOption from '../../../components/enum-settings-option.react.js'; import { useSelector } from '../../../redux/redux-utils.js'; import Modal from '../../modal.react.js'; const homeStatements = [ { statement: threadSettingsNotificationsCopy.BANNER_NOTIFS, isStatementValid: true, styleStatementBasedOnValidity: true, }, { statement: threadSettingsNotificationsCopy.NOTIF_COUNT, isStatementValid: true, styleStatementBasedOnValidity: true, }, { statement: threadSettingsNotificationsCopy.IN_HOME_TAB, isStatementValid: true, styleStatementBasedOnValidity: true, }, ]; const notifCountOnlyStatements = [ { statement: threadSettingsNotificationsCopy.BANNER_NOTIFS, isStatementValid: false, styleStatementBasedOnValidity: true, }, { statement: threadSettingsNotificationsCopy.NOTIF_COUNT, isStatementValid: true, styleStatementBasedOnValidity: true, }, { statement: threadSettingsNotificationsCopy.IN_HOME_TAB, isStatementValid: true, styleStatementBasedOnValidity: true, }, ]; const mutedStatements = [ { statement: threadSettingsNotificationsCopy.BANNER_NOTIFS, isStatementValid: false, styleStatementBasedOnValidity: true, }, { statement: threadSettingsNotificationsCopy.NOTIF_COUNT, isStatementValid: false, styleStatementBasedOnValidity: true, }, { statement: threadSettingsNotificationsCopy.IN_MUTED_TAB, isStatementValid: true, styleStatementBasedOnValidity: true, }, ]; type Props = { +threadID: string, +onClose: () => void, }; function NotificationsModal(props: Props): React.Node { const { onClose, threadID } = props; const threadInfo = useSelector(state => threadInfoSelector(state)[threadID]); const { notificationSettings, onHomeSelected, onNotifCountOnlySelected, onMutedSelected, saveButtonDisabled, onSave, isSidebar, - canPromoteSidebar, - parentThreadIsMuted, } = useThreadSettingsNotifications(threadInfo, onClose); const isHomeSelected = notificationSettings === 'home'; const homeItem = React.useMemo(() => { const icon = ; return ( ); }, [isHomeSelected, onHomeSelected]); const isNotifyCountOnlySelected = notificationSettings === 'notif-count-only'; const notifCountOnlyItem = React.useMemo(() => { const icon = ; return ( ); }, [isNotifyCountOnlySelected, onNotifCountOnlySelected]); const isMutedSelected = notificationSettings === 'muted'; const backgroundItem = React.useMemo(() => { const icon = ; return ( ); - }, [isMutedSelected, onMutedSelected, isSidebar]); + }, [isMutedSelected, onMutedSelected]); const modalName = isSidebar ? threadSettingsNotificationsCopy.SIDEBAR_TITLE : threadSettingsNotificationsCopy.CHANNEL_TITLE; - const noticeText = React.useMemo(() => { - if (!isSidebar) { - return null; - } - - return ( - <> -

- {threadSettingsNotificationsCopy.IS_SIDEBAR} -

-

- {canPromoteSidebar - ? threadSettingsNotificationsCopy.IS_SIDEBAR_CAN_PROMOTE - : threadSettingsNotificationsCopy.IS_SIDEBAR_CAN_NOT_PROMOTE} -

- - ); - }, [isSidebar, canPromoteSidebar]); - const modalContent = React.useMemo(() => { - if (parentThreadIsMuted) { - return ( - <> -

- {threadSettingsNotificationsCopy.PARENT_THREAD_IS_MUTED} -

-

- {canPromoteSidebar - ? threadSettingsNotificationsCopy.PARENT_THREAD_IS_MUTED_CAN_PROMOTE - : threadSettingsNotificationsCopy.PARENT_THREAD_IS_MUTED_CAN_NOT_PROMOTE} -

- - ); - } - return ( <>
{homeItem} {notifCountOnlyItem} {backgroundItem}
- {noticeText} ); - }, [ - parentThreadIsMuted, - homeItem, - notifCountOnlyItem, - backgroundItem, - noticeText, - canPromoteSidebar, - ]); + }, [homeItem, notifCountOnlyItem, backgroundItem]); const saveButton = React.useMemo(() => { - if (parentThreadIsMuted) { - return undefined; - } - return ( ); - }, [parentThreadIsMuted, onSave, saveButtonDisabled]); + }, [onSave, saveButtonDisabled]); return (
{modalContent}
); } export default NotificationsModal;