Skip to content

Commit

Permalink
Merge branch 'main' into adjust-issue-template
Browse files Browse the repository at this point in the history
  • Loading branch information
ztefanie authored Nov 19, 2024
2 parents 37de471 + 68ae5c0 commit de8b4da
Show file tree
Hide file tree
Showing 14 changed files with 213 additions and 206 deletions.
5 changes: 2 additions & 3 deletions native/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,8 @@ Depending on whether you want to develop for Android, iOS or both the following

- Install Java JDK, SDK and Runtime (v8 or v11).
- Install the Android SDK by using
the [Android Support plugin](https://plugins.jetbrains.com/plugin/1792-android-support) in IntelliJ.
- Install the latest stable SDK Platform and Android SDK Tools in the SDK Manager (Settings > Appearance & Behaviour >
System Settings > Android SDK).
the [Android plugin](https://plugins.jetbrains.com/plugin/22989-android) in IntelliJ.
- Install the latest stable SDK Platform and Android SDK Tools in the SDK Manager (Settings > Languages & Frameworks > Android SDK Updater).
- Install and accept the SDK license in the SDK Manager.
- [optional] If you want to develop using an emulator, also install the Android Emulator in the Android SDK settings.

Expand Down
7 changes: 7 additions & 0 deletions native/src/components/CitySelector.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,12 @@ const SearchBar = styled.View`
padding: 0 10%;
`

const SearchCounter = styled.Text`
margin: 15px 0 10px;
color: ${props => props.theme.colors.textSecondaryColor};
font-weight: 500;
`

type CitySelectorProps = {
cities: CityModel[]
navigateToDashboard: (city: CityModel) => void
Expand Down Expand Up @@ -70,6 +76,7 @@ const CitySelector = ({ cities, navigateToDashboard }: CitySelectorProps): React
<CityGroup>{t('nearbyCities')}</CityGroup>
<NearbyCities cities={cities} navigateToDashboard={navigateToDashboard} filterText={filterText} />
</CityGroupContainer>
<SearchCounter>{t('search:searchResultsCount', { count: resultCities.length })}</SearchCounter>
{resultCities.length === 0 ? <NothingFound paddingTop /> : cityEntries}
</View>
</View>
Expand Down
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: () => Promise<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
26 changes: 17 additions & 9 deletions native/src/routes/SearchModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,11 @@ const Wrapper = styled.View`
background-color: ${props => props.theme.colors.backgroundColor};
`

const SearchCounter = styled.Text`
margin: 10px 20px;
color: ${props => props.theme.colors.textSecondaryColor};
`

export type SearchModalProps = {
allPossibleResults: SearchResult[]
languageCode: string
Expand Down Expand Up @@ -76,15 +81,18 @@ const SearchModal = ({
<SearchHeader query={query} closeSearchBar={onClose} onSearchChanged={setQuery} />
<KeyboardAvoidingView behavior={Platform.OS === 'ios' ? 'padding' : undefined} style={{ flex: 1 }}>
{query.length > 0 && (
<List
items={searchResults}
renderItem={renderItem}
accessibilityLabel={t('searchResultsCount', { count: searchResults.length })}
style={{ flex: 1 }}
noItemsMessage={
<FeedbackContainer routeType={SEARCH_ROUTE} language={languageCode} cityCode={cityCode} query={query} />
}
/>
<>
<SearchCounter>{t('searchResultsCount', { count: searchResults.length })}</SearchCounter>
<List
items={searchResults}
renderItem={renderItem}
accessibilityLabel={t('searchResultsCount', { count: searchResults.length })}
style={{ flex: 1 }}
noItemsMessage={
<FeedbackContainer routeType={SEARCH_ROUTE} language={languageCode} cityCode={cityCode} query={query} />
}
/>
</>
)}
</KeyboardAvoidingView>
</Wrapper>
Expand Down
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 @@ -48,36 +38,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

0 comments on commit de8b4da

Please sign in to comment.