import { PlatformPressable } from '@react-navigation/elements' import { StaticScreenProps, useNavigation } from '@react-navigation/native' import { type ReactNode } from 'react' import { FlatList, Platform, StyleSheet, View, type ViewProps, type ViewStyle } from 'react-native' import { useSafeAreaInsets } from 'react-native-safe-area-context' import { Badge, Heading, Icon, Text } from '../components' import { useTheme } from '../hooks' import { isDefined } from '../types' import { chatTypeLabel } from '../utils' import { useGroups } from './notification_settings/hooks/groups' import { useChatTypes } from './preferred_app/hooks/use_chat_types' // ========================================= // ====== Factory Constants & Types ======== // ========================================= enum SectionTypes { header, hidden, setting, view, link, } type SectionListData = Array< | DataItem<{ title: string }, SectionTypes.header> | DataItem | DataItem | DataItem | DataItem > interface DataItem { type: TName data: T sectionOuterStyle?: ViewStyle sectionInnerStyle?: ViewStyle showBottomBorder?: boolean } // ================================= // ====== Components =============== // ================================= export type NotificationSettingsScreenProps = StaticScreenProps<{}> export function NotificationSettingsScreen({}: NotificationSettingsScreenProps) { const navigation = useNavigation() const styles = useStyles() const { data: chatTypes } = useChatTypes() const { data: groups } = useGroups() // Build section data const listData = [ { type: SectionTypes.header, data: { title: 'Preferred app' }, sectionInnerStyle: styles.sectionInnerHeader, }, { type: SectionTypes.view, data: { children: ( Choose which app receives each type of chat notification ), }, showBottomBorder: true, }, ...chatTypes.map(type => ({ type: SectionTypes.link, data: { title: chatTypeLabel(type.title), rightLabel: type.preferredApp, onPress: () => navigation.navigate('PreferredAppSelection', { chatTypeId: type.id, }), }, sectionInnerStyle: { paddingVertical: 0 }, showBottomBorder: true, })), { type: groups.length === 0 ? SectionTypes.hidden : SectionTypes.header, data: { title: 'Manage chat settings', }, sectionInnerStyle: styles.sectionInnerHeader, }, { type: groups.length === 0 ? SectionTypes.hidden : SectionTypes.view, data: { children: ( Notification settings for all of your group, team and event related conversations ), }, showBottomBorder: true, }, ...groups.map(group => ({ type: SectionTypes.link, data: { title: group.name, subtitle: group.myGroupMembership?.notificationLevelDescription, rightLabel: group.sourceType, onPress: () => navigation.navigate('GroupNotificationSettings', { groupId: group.id, title: group.name || 'Group', }), }, sectionInnerStyle: { paddingVertical: 0 }, showBottomBorder: true, })), ].filter(item => item.type !== SectionTypes.hidden) const headerIndices = listData .map(({ type }, index) => (type === SectionTypes.header ? index : undefined)) .filter(isDefined) return ( { const [isStart, isEnd] = [ index === 0 || headerIndices.includes(index), index === listData.length - 1 || headerIndices.includes(index + 1), ] switch (item.type) { case SectionTypes.header: return ( {item.data.title} ) case SectionTypes.setting: return ( ) case SectionTypes.view: return ( ) case SectionTypes.link: return ( ) case SectionTypes.hidden: default: return null } }} /> ) } interface ListSectionProps { isStart?: boolean isEnd?: boolean showBottomBorder?: boolean outerStyle?: ViewStyle innerStyle?: ViewStyle children: ReactNode } function ListSection({ isStart, isEnd, showBottomBorder, outerStyle, innerStyle, children, }: ListSectionProps) { const styles = useStyles({ isStart, isEnd }) const bottomBorder = showBottomBorder ? styles.sectionInnerBottomBorder : {} return ( {children} ) } interface SettingRowProps { title: string style?: ViewStyle rightItem?: ReactNode rightLabel?: string rightItemStyle?: ViewStyle } function SettingRow({ title, style, rightLabel, rightItem, rightItemStyle = {} }: SettingRowProps) { const styles = useStyles() return ( {title} {Boolean(rightLabel) && {rightLabel}} {Boolean(rightItem) && {rightItem}} ) } interface LinkRowProps { title: string subtitle?: string rightLabel: string onPress: () => void } function LinkRow({ title, subtitle, rightLabel, onPress }: LinkRowProps) { const styles = useLinkRowStyles() const isSourceType = rightLabel === 'Team' || rightLabel === 'Group' || rightLabel === 'PlanTeam' return ( {title} {Boolean(subtitle) && {subtitle}} {isSourceType ? ( ) : ( {rightLabel} )} {Platform.OS === 'ios' && ( )} ) } // ================================= // ====== Styles =================== // ================================= const useStyles = ({}: { isStart?: boolean; isEnd?: boolean } = {}) => { const { colors } = useTheme() const { bottom } = useSafeAreaInsets() const headerBottomPadding = 0 return StyleSheet.create({ listContainer: { flex: 1, }, contentContainer: { paddingBottom: bottom, }, sectionOuterBase: { paddingLeft: 16, }, sectionInnerBase: { paddingRight: 16, paddingVertical: 16, }, sectionInnerHeader: { paddingTop: 24, paddingBottom: headerBottomPadding, }, sectionInnerBottomBorder: { borderBottomColor: colors.borderColorDefaultBase, borderBottomWidth: 1, }, sectionDescription: { color: colors.textColorDefaultSecondary, }, settingRow: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', gap: 8, }, settingRowContent: { flex: 1, }, settingRowText: { lineHeight: 20, }, }) } const useLinkRowStyles = () => { const theme = useTheme() return StyleSheet.create({ row: { paddingLeft: 0, paddingVertical: 16, }, innerContainer: { flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between', gap: 12, }, leftContent: { flex: 1, gap: 4, }, rightContent: { flexDirection: 'row', alignItems: 'center', gap: 8, }, title: {}, rightLabelText: { color: theme.colors.textColorDefaultSecondary, }, icon: { color: theme.colors.iconColorDefaultDisabled, }, }) }