diff --git a/lib/hooks/promote-sidebar.react.js b/lib/hooks/promote-sidebar.react.js index 031c85b6a..6a1c6c32a 100644 --- a/lib/hooks/promote-sidebar.react.js +++ b/lib/hooks/promote-sidebar.react.js @@ -1,94 +1,95 @@ // @flow import * as React from 'react'; import { changeThreadSettingsActionTypes, useChangeThreadSettings, } from '../actions/thread-actions.js'; import { createLoadingStatusSelector } from '../selectors/loading-selectors.js'; import { threadInfoSelector } from '../selectors/thread-selectors.js'; import { - threadHasPermission, threadIsSidebar, + useThreadHasPermission, } from '../shared/thread-utils.js'; import type { LoadingStatus } from '../types/loading-types.js'; import type { ThreadInfo } from '../types/minimally-encoded-thread-permissions-types.js'; import { threadPermissions } from '../types/thread-permission-types.js'; import { threadTypes } from '../types/thread-types-enum.js'; import { useDispatchActionPromise } from '../utils/redux-promise-utils.js'; import { useSelector } from '../utils/redux-utils.js'; -function canPromoteSidebar( +function useCanPromoteSidebar( sidebarThreadInfo: ThreadInfo, parentThreadInfo: ?ThreadInfo, ): boolean { - if (!threadIsSidebar(sidebarThreadInfo)) { - return false; - } - - const canChangeThreadType = threadHasPermission( + const canChangeThreadType = useThreadHasPermission( sidebarThreadInfo, threadPermissions.EDIT_PERMISSIONS, ); - const canCreateSubchannelsInParent = threadHasPermission( + const canCreateSubchannelsInParent = useThreadHasPermission( parentThreadInfo, threadPermissions.CREATE_SUBCHANNELS, ); - return canChangeThreadType && canCreateSubchannelsInParent; + + return ( + threadIsSidebar(sidebarThreadInfo) && + canChangeThreadType && + canCreateSubchannelsInParent + ); } type PromoteSidebarType = { +onPromoteSidebar: () => void, +loading: LoadingStatus, +canPromoteSidebar: boolean, }; function usePromoteSidebar( threadInfo: ThreadInfo, onError?: () => mixed, ): PromoteSidebarType { const dispatchActionPromise = useDispatchActionPromise(); const callChangeThreadSettings = useChangeThreadSettings(); const loadingStatusSelector = createLoadingStatusSelector( changeThreadSettingsActionTypes, ); const loadingStatus = useSelector(loadingStatusSelector); const { parentThreadID } = threadInfo; const parentThreadInfo: ?ThreadInfo = useSelector(state => parentThreadID ? threadInfoSelector(state)[parentThreadID] : null, ); - const canPromote = canPromoteSidebar(threadInfo, parentThreadInfo); + const canPromote = useCanPromoteSidebar(threadInfo, parentThreadInfo); const onClick = React.useCallback(() => { try { void dispatchActionPromise( changeThreadSettingsActionTypes, (async () => { return await callChangeThreadSettings({ threadID: threadInfo.id, changes: { type: threadTypes.COMMUNITY_OPEN_SUBTHREAD }, }); })(), ); } catch (e) { onError?.(); throw e; } }, [threadInfo.id, callChangeThreadSettings, dispatchActionPromise, onError]); const returnValues = React.useMemo( () => ({ onPromoteSidebar: onClick, loading: loadingStatus, canPromoteSidebar: canPromote, }), [onClick, loadingStatus, canPromote], ); return returnValues; } -export { usePromoteSidebar, canPromoteSidebar }; +export { usePromoteSidebar, useCanPromoteSidebar }; diff --git a/web/modals/threads/notifications/notifications-modal.react.js b/web/modals/threads/notifications/notifications-modal.react.js index 47a793daf..425887858 100644 --- a/web/modals/threads/notifications/notifications-modal.react.js +++ b/web/modals/threads/notifications/notifications-modal.react.js @@ -1,300 +1,300 @@ // @flow import * as React from 'react'; import { useUpdateSubscription, updateSubscriptionActionTypes, } from 'lib/actions/user-actions.js'; -import { canPromoteSidebar } from 'lib/hooks/promote-sidebar.react.js'; +import { useCanPromoteSidebar } from 'lib/hooks/promote-sidebar.react.js'; import { threadInfoSelector } from 'lib/selectors/thread-selectors.js'; import { threadIsSidebar } from 'lib/shared/thread-utils.js'; import { useDispatchActionPromise } from 'lib/utils/redux-promise-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'; type NotificationSettings = 'focused' | 'badge-only' | 'background'; const BANNER_NOTIFS = 'Banner notifs'; const BADGE_COUNT = 'Badge count'; const IN_FOCUSED_TAB = 'Lives in Focused tab'; const IN_BACKGROUND_TAB = 'Lives in Background tab'; const focusedStatements = [ { statement: BANNER_NOTIFS, isStatementValid: true, styleStatementBasedOnValidity: true, }, { statement: BADGE_COUNT, isStatementValid: true, styleStatementBasedOnValidity: true, }, { statement: IN_FOCUSED_TAB, isStatementValid: true, styleStatementBasedOnValidity: true, }, ]; const badgeOnlyStatements = [ { statement: BANNER_NOTIFS, isStatementValid: false, styleStatementBasedOnValidity: true, }, { statement: BADGE_COUNT, isStatementValid: true, styleStatementBasedOnValidity: true, }, { statement: IN_FOCUSED_TAB, isStatementValid: true, styleStatementBasedOnValidity: true, }, ]; const backgroundStatements = [ { statement: BANNER_NOTIFS, isStatementValid: false, styleStatementBasedOnValidity: true, }, { statement: BADGE_COUNT, isStatementValid: false, styleStatementBasedOnValidity: true, }, { statement: IN_BACKGROUND_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 { subscription } = threadInfo.currentUser; const { parentThreadID } = threadInfo; const parentThreadInfo = useSelector(state => parentThreadID ? threadInfoSelector(state)[parentThreadID] : null, ); const isSidebar = threadIsSidebar(threadInfo); const initialThreadSetting = React.useMemo(() => { if (!subscription.home) { return 'background'; } if (!subscription.pushNotifs) { return 'badge-only'; } return 'focused'; }, [subscription.home, subscription.pushNotifs]); const [notificationSettings, setNotificationSettings] = React.useState(initialThreadSetting); const onFocusedSelected = React.useCallback( () => setNotificationSettings('focused'), [], ); const onBadgeOnlySelected = React.useCallback( () => setNotificationSettings('badge-only'), [], ); const onBackgroundSelected = React.useCallback( () => setNotificationSettings('background'), [], ); const isFocusedSelected = notificationSettings === 'focused'; const focusedItem = React.useMemo(() => { const icon = ; return ( ); }, [isFocusedSelected, onFocusedSelected]); const isFocusedBadgeOnlySelected = notificationSettings === 'badge-only'; const focusedBadgeOnlyItem = React.useMemo(() => { const icon = ; return ( ); }, [isFocusedBadgeOnlySelected, onBadgeOnlySelected]); const isBackgroundSelected = notificationSettings === 'background'; const backgroundItem = React.useMemo(() => { const icon = ; return ( ); }, [isBackgroundSelected, onBackgroundSelected, isSidebar]); const dispatchActionPromise = useDispatchActionPromise(); const callUpdateSubscription = useUpdateSubscription(); const onClickSave = React.useCallback(() => { void dispatchActionPromise( updateSubscriptionActionTypes, callUpdateSubscription({ threadID: threadID, updatedFields: { home: notificationSettings !== 'background', pushNotifs: notificationSettings === 'focused', }, }), ); onClose(); }, [ callUpdateSubscription, dispatchActionPromise, notificationSettings, onClose, threadID, ]); const modalName = isSidebar ? 'Thread notifications' : 'Channel notifications'; + const canPromoteSidebar = useCanPromoteSidebar(threadInfo, parentThreadInfo); const noticeText = React.useMemo(() => { if (!isSidebar) { return null; } return ( <>

{'It’s not possible to move this thread to Background. ' + '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 Focused, the thread ' + 'must also be there.'}

- {canPromoteSidebar(threadInfo, parentThreadInfo) + {canPromoteSidebar ? 'If you want to move this thread to Background, ' + 'you can either move the parent to Background, ' + 'or you can promote the thread to a channel.' : 'If you want to move this thread to Background, ' + 'you’ll have to move the parent to Background.'}

); - }, [isSidebar, parentThreadInfo, threadInfo]); + }, [isSidebar, canPromoteSidebar]); const parentThreadIsInBackground = isSidebar && !parentThreadInfo?.currentUser.subscription.home; const modalContent = React.useMemo(() => { if (parentThreadIsInBackground) { return ( <>

{'It’s not possible to change the notif settings for a thread ' + 'whose parent is in Background. 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 Background, the ' + 'thread must also be there.'}

- {canPromoteSidebar(threadInfo, parentThreadInfo) + {canPromoteSidebar ? '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.' : 'If you want to change the notif settings for this thread, ' + 'you’ll have to change the notif settings for the parent.'}

); } return ( <>
{focusedItem} {focusedBadgeOnlyItem} {backgroundItem}
{noticeText} ); }, [ backgroundItem, focusedBadgeOnlyItem, focusedItem, noticeText, - parentThreadInfo, parentThreadIsInBackground, - threadInfo, + canPromoteSidebar, ]); const saveButton = React.useMemo(() => { if (parentThreadIsInBackground) { return undefined; } return ( ); }, [ initialThreadSetting, notificationSettings, onClickSave, parentThreadIsInBackground, ]); return (
{modalContent}
); } export default NotificationsModal;