diff --git a/web/components/enum-settings-option-info.react.js b/web/components/enum-settings-option-info.react.js
index 1807e7328..d36a464b3 100644
--- a/web/components/enum-settings-option-info.react.js
+++ b/web/components/enum-settings-option-info.react.js
@@ -1,40 +1,49 @@
// @flow
import classnames from 'classnames';
import * as React from 'react';
import SWMansionIcon from '../SWMansionIcon.react';
import css from './enum-settings-option-info.css';
type Props = {
+optionSelected: boolean,
+valid: boolean,
+ +styleStatementBasedOnValidity: boolean,
+children: React.Node,
};
function EnumSettingsOptionInfo(props: Props): React.Node {
- const { valid, children, optionSelected } = props;
+ const {
+ optionSelected,
+ valid,
+ styleStatementBasedOnValidity,
+ children,
+ } = props;
const optionInfoClasses = React.useMemo(
() =>
classnames({
[css.optionInfo]: true,
- [css.optionInfoInvalid]: !valid,
- [css.optionInfoInvalidSelected]: !valid && optionSelected,
+ [css.optionInfoInvalid]: styleStatementBasedOnValidity && !valid,
+ [css.optionInfoInvalidSelected]:
+ styleStatementBasedOnValidity && !valid && optionSelected,
}),
- [valid, optionSelected],
+ [styleStatementBasedOnValidity, valid, optionSelected],
);
- const icon = React.useMemo(
- () => ,
- [valid],
- );
+ const icon = React.useMemo(() => {
+ if (!styleStatementBasedOnValidity) {
+ return null;
+ }
+ return ;
+ }, [styleStatementBasedOnValidity, valid]);
return (
{icon}
{children}
);
}
export default EnumSettingsOptionInfo;
diff --git a/web/components/enum-settings-option.react.js b/web/components/enum-settings-option.react.js
index e6d639d61..bfa270cf5 100644
--- a/web/components/enum-settings-option.react.js
+++ b/web/components/enum-settings-option.react.js
@@ -1,57 +1,61 @@
// @flow
import classnames from 'classnames';
import * as React from 'react';
import EnumSettingsOptionInfo from './enum-settings-option-info.react.js';
import css from './enum-settings-option.css';
import Radio from './radio.react';
type Props = {
+selected: boolean,
+onSelect: () => void,
+icon: React.Node,
+title: string,
+statements: $ReadOnlyArray<{
+statement: string,
+isStatementValid: boolean,
+ +styleStatementBasedOnValidity: boolean,
}>,
};
function EnumSettingsOption(props: Props): React.Node {
const { icon, title, statements, selected, onSelect } = props;
const descriptionItems = React.useMemo(
() =>
- statements.map(({ statement, isStatementValid }) => (
-
- {statement}
-
- )),
+ statements.map(
+ ({ statement, isStatementValid, styleStatementBasedOnValidity }) => (
+
+ {statement}
+
+ ),
+ ),
[selected, statements],
);
const optionContainerClasses = React.useMemo(
() =>
classnames(css.optionContainer, {
[css.optionContainerSelected]: selected,
}),
[selected],
);
return (
{icon}
{title}
{descriptionItems}
);
}
export default EnumSettingsOption;
diff --git a/web/modals/threads/notifications/notifications-modal.react.js b/web/modals/threads/notifications/notifications-modal.react.js
index b0f488b6c..ebf784ad7 100644
--- a/web/modals/threads/notifications/notifications-modal.react.js
+++ b/web/modals/threads/notifications/notifications-modal.react.js
@@ -1,193 +1,229 @@
// @flow
import * as React from 'react';
import {
updateSubscription,
updateSubscriptionActionTypes,
} from 'lib/actions/user-actions';
import { threadInfoSelector } from 'lib/selectors/thread-selectors';
import {
useServerCall,
useDispatchActionPromise,
} from 'lib/utils/action-utils';
import {
assetCacheURLPrefix,
focusedNotificationsIllustrationAsset,
badgeOnlyNotificationsIllustrationAsset,
backgroundNotificationsIllustrationAsset,
} from '../../../assets.js';
import Button from '../../../components/button.react';
import EnumSettingsOption from '../../../components/enum-settings-option.react';
import { useSelector } from '../../../redux/redux-utils';
import Modal from '../../modal.react';
import css from './notifications-modal.css';
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 },
- { statement: BADGE_COUNT, isStatementValid: true },
- { statement: IN_FOCUSED_TAB, isStatementValid: true },
+ {
+ 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 },
- { statement: BADGE_COUNT, isStatementValid: true },
- { statement: IN_FOCUSED_TAB, isStatementValid: true },
+ {
+ 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 },
- { statement: BADGE_COUNT, isStatementValid: false },
- { statement: IN_BACKGROUND_TAB, isStatementValid: true },
+ {
+ 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 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]);
const dispatchActionPromise = useDispatchActionPromise();
const callUpdateSubscription = useServerCall(updateSubscription);
const onClickSave = React.useCallback(() => {
dispatchActionPromise(
updateSubscriptionActionTypes,
callUpdateSubscription({
threadID: threadID,
updatedFields: {
home: notificationSettings !== 'background',
pushNotifs: notificationSettings === 'focused',
},
}),
);
onClose();
}, [
callUpdateSubscription,
dispatchActionPromise,
notificationSettings,
onClose,
threadID,
]);
return (
{focusedItem}
{focusedBadgeOnlyItem}
{backgroundItem}
);
}
export default NotificationsModal;
diff --git a/web/modals/threads/settings/thread-settings-privacy-tab.react.js b/web/modals/threads/settings/thread-settings-privacy-tab.react.js
index 7ce91b82d..33c9b00de 100644
--- a/web/modals/threads/settings/thread-settings-privacy-tab.react.js
+++ b/web/modals/threads/settings/thread-settings-privacy-tab.react.js
@@ -1,174 +1,176 @@
// @flow
import * as React from 'react';
import {
changeThreadSettings,
changeThreadSettingsActionTypes,
} from 'lib/actions/thread-actions';
import { threadTypeDescriptions } from 'lib/shared/thread-utils';
import { type SetState } from 'lib/types/hook-types';
import {
type ThreadInfo,
type ThreadChanges,
threadTypes,
} from 'lib/types/thread-types';
import {
useDispatchActionPromise,
useServerCall,
} from 'lib/utils/action-utils';
import Button from '../../../components/button.react';
import EnumSettingsOption from '../../../components/enum-settings-option.react';
import SWMansionIcon from '../../../SWMansionIcon.react';
import { useModalContext } from '../../modal-provider.react';
import css from './thread-settings-privacy-tab.css';
const { COMMUNITY_OPEN_SUBTHREAD, COMMUNITY_SECRET_SUBTHREAD } = threadTypes;
const openStatements = [
{
statement: threadTypeDescriptions[COMMUNITY_OPEN_SUBTHREAD],
isStatementValid: true,
+ styleStatementBasedOnValidity: false,
},
];
const secretStatements = [
{
statement: threadTypeDescriptions[COMMUNITY_SECRET_SUBTHREAD],
isStatementValid: true,
+ styleStatementBasedOnValidity: false,
},
];
type ThreadSettingsPrivacyTabProps = {
+inputDisabled: boolean,
+threadInfo: ThreadInfo,
+queuedChanges: ThreadChanges,
+setQueuedChanges: SetState,
+setErrorMessage: SetState,
};
function ThreadSettingsPrivacyTab(
props: ThreadSettingsPrivacyTabProps,
): React.Node {
const {
inputDisabled,
threadInfo,
queuedChanges,
setQueuedChanges,
setErrorMessage,
} = props;
const modalContext = useModalContext();
const dispatchActionPromise = useDispatchActionPromise();
const callChangeThreadSettings = useServerCall(changeThreadSettings);
const changeQueued: boolean = React.useMemo(
() => Object.values(queuedChanges).some(v => v !== null && v !== undefined),
[queuedChanges],
);
const changeThreadSettingsAction = React.useCallback(async () => {
try {
const response = await callChangeThreadSettings({
threadID: threadInfo.id,
changes: queuedChanges,
});
modalContext.popModal();
return response;
} catch (e) {
setErrorMessage('unknown_error');
setQueuedChanges(Object.freeze({}));
throw e;
}
}, [
callChangeThreadSettings,
modalContext,
queuedChanges,
setErrorMessage,
setQueuedChanges,
threadInfo.id,
]);
const onSubmit = React.useCallback(
(event: SyntheticEvent) => {
event.preventDefault();
dispatchActionPromise(
changeThreadSettingsActionTypes,
changeThreadSettingsAction(),
);
},
[changeThreadSettingsAction, dispatchActionPromise],
);
const onOpenSelected = React.useCallback(() => {
setQueuedChanges(prevQueuedChanges =>
Object.freeze({
...prevQueuedChanges,
type:
COMMUNITY_OPEN_SUBTHREAD !== threadInfo.type
? COMMUNITY_OPEN_SUBTHREAD
: undefined,
}),
);
}, [setQueuedChanges, threadInfo.type]);
const onSecretSelected = React.useCallback(() => {
setQueuedChanges(prevQueuedChanges =>
Object.freeze({
...prevQueuedChanges,
type:
COMMUNITY_SECRET_SUBTHREAD !== threadInfo.type
? COMMUNITY_SECRET_SUBTHREAD
: undefined,
}),
);
}, [setQueuedChanges, threadInfo.type]);
const globeIcon = React.useMemo(
() => ,
[],
);
const lockIcon = React.useMemo(
() => ,
[],
);
return (
);
}
export default ThreadSettingsPrivacyTab;