diff --git a/native/chat/settings/thread-settings-notifications.react.js b/native/chat/settings/thread-settings-notifications.react.js index 9721ed81a..0a3b21e5b 100644 --- a/native/chat/settings/thread-settings-notifications.react.js +++ b/native/chat/settings/thread-settings-notifications.react.js @@ -1,135 +1,298 @@ // @flow import * as React from 'react'; -import { View } from 'react-native'; +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 } from '../../themes/colors.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, + +badgeCountEnabled: boolean, + +livesInFocusedTab: boolean, +}; + +function NotificationDescription( + props: NotificationDescriptionProps, +): React.Node { + const { + selected, + bannerNotifsEnabled, + badgeCountEnabled, + livesInFocusedTab, + } = 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 badgeCountDescriptionTextStyles = React.useMemo(() => { + const style = [styles.notificationOptionDescriptionText]; + + if (selected && !badgeCountEnabled) { + style.push(styles.notificationOptionDescriptionTextDisabledSelected); + } else if (!badgeCountEnabled) { + style.push(styles.notificationOptionDescriptionTextDisabled); + } + + return style; + }, [ + badgeCountEnabled, + selected, + styles.notificationOptionDescriptionText, + styles.notificationOptionDescriptionTextDisabled, + styles.notificationOptionDescriptionTextDisabledSelected, + ]); + + let bannerNotifsIconColor = colors.panelForegroundSecondaryLabel; + if (selected && !bannerNotifsEnabled) { + bannerNotifsIconColor = colors.panelInputSecondaryForeground; + } else if (!bannerNotifsEnabled) { + bannerNotifsIconColor = colors.panelSecondaryForeground; + } + + let badgeCountIconColor = colors.panelForegroundSecondaryLabel; + if (selected && !badgeCountEnabled) { + badgeCountIconColor = colors.panelInputSecondaryForeground; + } else if (!badgeCountEnabled) { + badgeCountIconColor = colors.panelSecondaryForeground; + } + + return ( + <> + + + + {threadSettingsNotificationsCopy.BANNER_NOTIFS} + + + + + + {threadSettingsNotificationsCopy.BADGE_COUNT} + + + + + + {livesInFocusedTab + ? threadSettingsNotificationsCopy.IN_FOCUSED_TAB + : threadSettingsNotificationsCopy.IN_BACKGROUND_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, onFocusedSelected, onBadgeOnlySelected, onBackgroundSelected, saveButtonDisabled, onSave, } = useThreadSettingsNotifications(threadInfo, goBack); React.useEffect(() => { setOptions({ headerRight: () => ( ), }); }, [saveButtonDisabled, onSave, setOptions]); const styles = useStyles(unboundStyles); const allNotificationsIllustration = React.useMemo( () => ( ), [styles.notificationOptionIconContainer], ); const badgeOnlyIllustration = React.useMemo( () => ( ), [styles.notificationOptionIconContainer], ); const mutedIllustration = React.useMemo( () => ( ), [styles.notificationOptionIconContainer], ); + const allNotificationsDescription = React.useMemo( + () => ( + + ), + [notificationSettings], + ); + + const badgeOnlyDescription = React.useMemo( + () => ( + + ), + [notificationSettings], + ); + + const mutedDescription = React.useMemo( + () => ( + + ), + [notificationSettings], + ); + return ( ); } 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', + }, }; export default ThreadSettingsNotifications; diff --git a/native/components/enum-settings-option.react.js b/native/components/enum-settings-option.react.js index 3ec6c4b9c..4cfd1dff9 100644 --- a/native/components/enum-settings-option.react.js +++ b/native/components/enum-settings-option.react.js @@ -1,188 +1,196 @@ // @flow import * as React from 'react'; import { Text, TouchableOpacity, View } from 'react-native'; import CommIcon from '../components/comm-icon.react.js'; import { useColors, useStyles } from '../themes/colors.js'; type InputType = 'radio' | 'checkbox'; type EnumSettingsOptionProps = { +icon?: string | React.Node, +name: string, - +description: string, + +description: string | React.Node, +enumValue: boolean, +onEnumValuePress: () => mixed, +type?: InputType, +disabled?: boolean, }; function EnumSettingsOption(props: EnumSettingsOptionProps): React.Node { const styles = useStyles(unboundStyles); const colors = useColors(); const { icon, name, description, enumValue, onEnumValuePress, type = 'radio', disabled, } = props; const enumIcon = React.useMemo(() => { if (!icon) { return null; } if (typeof icon === 'string') { return ( ); } return icon; }, [icon, styles.enumIcon, colors.purpleButton]); + const descriptionElement = React.useMemo(() => { + if (typeof description === 'string') { + return {description}; + } + + return description; + }, [description, styles.enumInfoDescription]); + const enumInputStyles = React.useMemo(() => { const style = [styles.enumInput]; if (disabled) { style.push(styles.disabled); } else if (type === 'radio') { style.push(styles.radio); } else { style.push(styles.checkBox); } return style; }, [ disabled, styles.checkBox, styles.disabled, styles.enumInput, styles.radio, type, ]); const enumInputFilledStyles = React.useMemo(() => { const style = [styles.enumInputFill]; if (type === 'radio') { style.push(styles.radioFill); } else { style.push(styles.checkBoxFill); } return style; }, [styles.checkBoxFill, styles.enumInputFill, styles.radioFill, type]); const enumInputFill = React.useMemo( () => (enumValue ? : null), [enumValue, enumInputFilledStyles], ); const touchableContainerStyle = React.useMemo(() => { const style = [styles.touchableContainer]; if (enumValue) { style.push(styles.touchableContainerSelected); } return style; }, [enumValue, styles.touchableContainer, styles.touchableContainerSelected]); return ( {enumIcon} {name} - {description} + {descriptionElement} {enumInputFill} ); } const unboundStyles = { container: { backgroundColor: 'panelForeground', paddingVertical: 4, paddingHorizontal: 8, }, touchableContainer: { flexDirection: 'row', padding: 12, }, touchableContainerSelected: { backgroundColor: 'panelSecondaryForeground', borderRadius: 8, }, enumIcon: { paddingTop: 4, paddingRight: 24, }, enumInfoContainer: { flex: 1, flexDirection: 'column', justifyContent: 'space-evenly', }, enumInfoName: { color: 'panelForegroundLabel', fontSize: 18, lineHeight: 24, marginBottom: 4, }, enumInfoDescription: { color: 'panelForegroundSecondaryLabel', lineHeight: 18, }, enumInputContainer: { padding: 16, paddingRight: 0, justifyContent: 'center', alignItems: 'center', }, enumInput: { height: 32, width: 32, borderWidth: 1, borderColor: 'panelSecondaryForegroundBorder', justifyContent: 'center', alignItems: 'center', }, checkBox: { borderRadius: 3.5, }, radio: { borderRadius: 16, }, enumInputFill: { height: 20, width: 20, backgroundColor: 'panelForegroundSecondaryLabel', }, checkBoxFill: { borderRadius: 2.1875, }, radioFill: { borderRadius: 10, }, disabled: { opacity: 0, }, }; export default EnumSettingsOption;