Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

2350: Refactor settings and remove SectionList #2965

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
84 changes: 50 additions & 34 deletions native/src/components/SettingItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,57 +35,73 @@ const FlexEndContainer = styled.View`
padding: 0 5px;
`

const BadgeContainer = styled.View`
flex-direction: row;
align-items: center;
`

const Badge = styled.View<{ enabled: boolean }>`
width: 8px;
height: 8px;
border-radius: 4px;
background-color: ${props => (props.enabled ? 'limegreen' : 'red')};
`

type SettingItemValueProps = {
onPress: () => void
hasBadge: boolean
value: boolean
}

const SettingsItemValue = ({ value, hasBadge, onPress }: SettingItemValueProps): ReactElement => {
const { t } = useTranslation('settings')
if (hasBadge) {
return (
<BadgeContainer>
<Badge enabled={value} />
<Text> {value ? t('enabled') : t('disabled')}</Text>
</BadgeContainer>
)
}
return <SettingsSwitch value={value} onPress={onPress} />
}

type SettingItemProps = {
title: string
description?: string
onPress: () => void
bigTitle?: boolean
role?: Role
hasSwitch?: boolean
hasBadge?: boolean
value: boolean
value: boolean | null
}

const SettingItem = (props: SettingItemProps): ReactElement => {
const { title, description, onPress, value, hasBadge, hasSwitch, bigTitle, role } = props
const { t } = useTranslation('settings')

return (
<Pressable onPress={onPress} role={role ?? 'none'} accessible={false}>
<PadView>
<ContentContainer>
const SettingItem = ({
title,
description,
onPress,
value,
bigTitle,
role,
hasBadge = false,
}: SettingItemProps): ReactElement => (
<Pressable onPress={onPress} role={role ?? 'none'} accessible={false}>
<PadView>
<ContentContainer>
<View>
<Title bigTitle={bigTitle || false}>{title}</Title>
</View>
{!!description && (
<View>
<Title bigTitle={bigTitle || false}>{title}</Title>
<Description>{description}</Description>
</View>
{!!description && (
<View>
<Description>{description}</Description>
</View>
)}
</ContentContainer>
<FlexEndContainer>
{hasSwitch && <SettingsSwitch value={value} onPress={onPress} />}
{hasBadge && (
<View
style={{
flexDirection: 'row',
alignItems: 'center',
}}>
<Badge enabled={value} />
<Text> {value ? t('enabled') : t('disabled')}</Text>
</View>
)}
</FlexEndContainer>
</PadView>
</Pressable>
)
}
)}
</ContentContainer>
<FlexEndContainer>
{value !== null && <SettingsItemValue onPress={onPress} hasBadge={hasBadge} value={value} />}
</FlexEndContainer>
</PadView>
</Pressable>
)

export default SettingItem
42 changes: 10 additions & 32 deletions native/src/routes/Settings.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import React, { ReactElement } from 'react'
import { useTranslation } from 'react-i18next'
import { SectionList, SectionListData } from 'react-native'
import styled from 'styled-components/native'
import { FlatList } from 'react-native'

import { SettingsRouteType } from 'shared'

Expand All @@ -19,15 +18,6 @@ type SettingsProps = {
navigation: NavigationProps<SettingsRouteType>
}

type SectionType = SectionListData<SettingsSectionType> & {
title?: string | null
}

const SectionHeader = styled.Text`
padding: 20px;
color: ${props => props.theme.colors.textColor};
`

const Settings = ({ navigation }: SettingsProps): ReactElement => {
const appContext = useCityAppContext()
const showSnackbar = useSnackbar()
Expand All @@ -46,36 +36,24 @@ const Settings = ({ navigation }: SettingsProps): ReactElement => {

const renderItem = ({ item }: { item: SettingsSectionType }) => {
const { getSettingValue, onPress, ...otherProps } = item
const value = !!(getSettingValue && getSettingValue(settings))
const value = getSettingValue ? !!getSettingValue(settings) : null
return <SettingItem value={value} key={otherProps.title} onPress={safeOnPress(onPress)} {...otherProps} />
}

const renderSectionHeader = ({ section: { title } }: { section: SectionType }) => {
if (!title) {
return null
}

return <SectionHeader>{title}</SectionHeader>
}

const sections = createSettingsSections({
appContext,
navigation,
showSnackbar,
t,
})
const sections = createSettingsSections({ appContext, navigation, showSnackbar, t }).filter(
(it): it is SettingsSectionType => it !== null,
)

return (
<Layout>
<Caption title={t('layout:settings')} />
<SectionList
sections={sections}
extraData={settings}
<ItemSeparator />
<FlatList
data={sections}
extraData={appContext.settings}
renderItem={renderItem}
renderSectionHeader={renderSectionHeader}
ItemSeparatorComponent={ItemSeparator}
SectionSeparatorComponent={ItemSeparator}
stickySectionHeadersEnabled={false}
ListFooterComponent={ItemSeparator}
/>
</Layout>
)
Expand Down
14 changes: 7 additions & 7 deletions native/src/utils/__tests__/createSettingsSections.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,20 +48,20 @@ describe('createSettingsSections', () => {
navigation,
showSnackbar,
t,
})[0]!.data
})

describe('allowPushNotifications', () => {
it('should not include push notification setting if disabled', () => {
mockedPushNotificationsEnabled.mockImplementation(() => false)
const sections = createSettings()
expect(sections.find(it => it.title === 'privacyPolicy')).toBeTruthy()
expect(sections.find(it => it.title === 'pushNewsTitle')).toBeFalsy()
expect(sections.find(it => it?.title === 'privacyPolicy')).toBeTruthy()
expect(sections.find(it => it?.title === 'pushNewsTitle')).toBeFalsy()
})

it('should set correct setting on press', async () => {
mockedPushNotificationsEnabled.mockImplementation(() => true)
const sections = createSettings()
const pushNotificationSection = sections.find(it => it.title === 'pushNewsTitle')!
const pushNotificationSection = sections.find(it => it?.title === 'pushNewsTitle')!
await pushNotificationSection!.onPress()
expect(updateSettings).toHaveBeenCalledTimes(1)
expect(updateSettings).toHaveBeenCalledWith({ allowPushNotifications: false })
Expand All @@ -77,7 +77,7 @@ describe('createSettingsSections', () => {
it('should unsubscribe from push notification topic', async () => {
mockedPushNotificationsEnabled.mockImplementation(() => true)
const sections = createSettings()
const pushNotificationSection = sections.find(it => it.title === 'pushNewsTitle')!
const pushNotificationSection = sections.find(it => it?.title === 'pushNewsTitle')!

expect(mockUnsubscribeNews).not.toHaveBeenCalled()

Expand All @@ -95,7 +95,7 @@ describe('createSettingsSections', () => {
it('should subscribe to push notification topic if permission is granted', async () => {
mockedPushNotificationsEnabled.mockImplementation(() => true)
const sections = createSettings({ allowPushNotifications: false })
const pushNotificationSection = sections.find(it => it.title === 'pushNewsTitle')!
const pushNotificationSection = sections.find(it => it?.title === 'pushNewsTitle')!

expect(mockRequestPushNotificationPermission).not.toHaveBeenCalled()
expect(mockSubscribeNews).not.toHaveBeenCalled()
Expand All @@ -120,7 +120,7 @@ describe('createSettingsSections', () => {
it('should open settings and return false if permissions not granted', async () => {
mockedPushNotificationsEnabled.mockImplementation(() => true)
const sections = createSettings({ allowPushNotifications: false })
const pushNotificationSection = sections.find(it => it.title === 'pushNewsTitle')!
const pushNotificationSection = sections.find(it => it?.title === 'pushNewsTitle')!

expect(mockRequestPushNotificationPermission).not.toHaveBeenCalled()
expect(mockSubscribeNews).not.toHaveBeenCalled()
Expand Down
Loading
Loading