/** * Copyright (c) 2020 INESC TEC <https://www.inesctec.pt> * * This Source Code Form is subject to the terms of the European Union * Public License, v. 1.2. If a copy of the EUPL was not distributed with * this file, You can obtain one at https://opensource.org/licenses/EUPL-1.2. * * SPDX-License-Identifier: EUPL-1.2 */ import React, { useMemo } from 'react'; import { View, StyleSheet, Image } from 'react-native'; import { useSafeAreaInsets } from 'react-native-safe-area-context'; import PropTypes from 'prop-types'; import { useTheme } from '@app/contexts/Theme'; import { themes as commonThemes, sizes, iconSizes } from '@app/common/theme'; import Configuration from '@app/services/configuration'; import i18n, { languages } from '@app/services/i18n'; import TopComponent from '@app/common/components/TopComponent'; import Layout from '@app/common/components/Layout'; import ButtonWrapper from '@app/common/components/ButtonWrapper'; import Text from '@app/common/components/FormattedText'; import Icon from '@app/common/components/Icon'; import Switch from '@app/common/components/Switch'; import Toggle from '@app/common/components/Toggle'; import List from '@app/common/components/List'; import { getThemedImage } from '@app/common/assets/images'; const styles = (colors, insets) => StyleSheet.create({ container: { }, closeButton: { alignSelf: 'flex-start', padding: sizes.size8, margin: -sizes.size8, }, header: { marginBottom: sizes.size16, }, layoutContainer: { flex: 1, backgroundColor: colors.transparent, zIndex: 10, }, itemsContainer: { marginBottom: sizes.size48, }, topItems: { marginBottom: sizes.size48, }, tracingButton: { flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between', width: '100%', marginBottom: sizes.size16, }, tracingItem: { flexDirection: 'column', alignItems: 'flex-start', }, tracingLabelContainer: { flexDirection: 'row', alignItems: 'center', }, tracingLabel: { marginRight: sizes.size8, }, imagesContainer: { position: 'absolute', bottom: 0, width: '100%', zIndex: 0, }, sponsors: { position: 'absolute', flexDirection: 'row', bottom: sizes.size24 + insets.bottom, left: sizes.size24, zIndex: 0, }, republicaPortuguesaImage: { marginRight: sizes.size24, }, dgsImage: { }, splashImage: { alignSelf: 'flex-end', }, version: { alignSelf: 'flex-end', marginBottom: sizes.size8, }, }); function renderTracingButton(tracingEnabled, onPressTracing, colors, memoizedStyle) { return ( <> <View style={memoizedStyle.tracingButton}> <Text textColor={tracingEnabled ? colors.settingsMainButtonTextColor : colors.settingsAltButtonTextColor} weight='bold'>{i18n.translate('screens.settings.tracing.label')}</Text> <View style={memoizedStyle.tracingLabelContainer}> <Text textColor={tracingEnabled ? colors.settingsMainButtonTextColor : colors.settingsAltButtonTextColor} weight='bold' style={memoizedStyle.tracingLabel}>{tracingEnabled ? i18n.translate('common.words.enabled') : i18n.translate('common.words.disabled')}</Text> <Switch value={tracingEnabled} onValueChange={onPressTracing} accessibilityLabel={i18n.translate('screens.settings.tracing.accessibility.label')} accessibilityHint={i18n.translate(`screens.settings.tracing.accessibility.hint.${tracingEnabled ? 'deactivate' : 'activate'}`)} /> </View> </View> <Text size='small' textColor={tracingEnabled ? colors.settingsMainButtonTextColor : colors.settingsAltButtonTextColor}>{i18n.translate(`screens.settings.tracing.description.${tracingEnabled ? 'enabled' : 'disabled'}`)}</Text> </> ); } export default function Info(props) { const { appVersion, appBuild, language, theme, tracingEnabled, isInfected, onClose, onPressLanguage, onPressTheme, onPressSupport, onPressLegalInformation, onPressHowToUse, onPressFaqs, onPressTracing, onPressDebug, } = props; const insets = useSafeAreaInsets(); const { name, colors } = useTheme(); const memoizedStyle = useMemo(() => styles(colors, insets), [name, insets]); const languagesNames = Object.values(languages).map(({ languageTag, countryCode }) => ({ id: languageTag, label: countryCode })); const themesNames = Object.values(commonThemes.names).map(id => ({ id, label: i18n.translate(`screens.settings.theme.${id}`) })); const topItems = [ { id: 1, onPress: onPressTracing, style: { ...memoizedStyle.tracingItem, backgroundColor: tracingEnabled ? colors.settingsMainButtonBackgroundColor : colors.settingsAltButtonBackgroundColor, }, disabled: isInfected, accessibilityRole: 'switch', accessibilityValue: { text: tracingEnabled }, accessibilityLabel: i18n.translate('screens.settings.tracing.accessibility.label'), accessibilityHint: i18n.translate(`screens.settings.tracing.accessibility.hint.${tracingEnabled ? 'deactivate' : 'activate'}`), renderItem: () => renderTracingButton(tracingEnabled, onPressTracing, colors, memoizedStyle), }, { id: 2, title: i18n.translate('screens.settings.language.label'), onPress: onPressLanguage, accessibilityLabel: i18n.translate('screens.settings.language.accessibility.label'), accessibilityHint: i18n.translate('screens.settings.language.accessibility.hint'), accessibilityRole: 'switch', accessibilityValue: { text: language.name }, icon: <Toggle value={language.languageTag} options={languagesNames} onPress={onPressLanguage} />, }, { id: 3, title: i18n.translate('screens.settings.theme.label'), onPress: onPressTheme, accessibilityLabel: i18n.translate('screens.settings.theme.accessibility.label'), accessibilityHint: i18n.translate('screens.settings.theme.accessibility.hint'), accessibilityRole: 'switch', accessibilityValue: { text: i18n.translate(`screens.settings.theme.${theme}`) }, icon: <Toggle value={theme} options={themesNames} onPress={onPressTheme} />, }, ]; const bottomItems = [ { id: 1, title: i18n.translate('screens.settings.how_to_use.label'), onPress: onPressHowToUse, accessibilityLabel: i18n.translate('screens.settings.how_to_use.accessibility.label'), accessibilityHint: i18n.translate('screens.settings.how_to_use.accessibility.hint'), }, { id: 2, title: i18n.translate('screens.settings.faqs.label'), onPress: onPressFaqs, accessibilityLabel: i18n.translate('screens.settings.faqs.accessibility.label'), accessibilityHint: i18n.translate('screens.settings.faqs.accessibility.hint'), icon: <Icon name='external_link' width={iconSizes.size12} height={iconSizes.size12} />, }, { id: 3, title: i18n.translate('screens.settings.support.label'), onPress: onPressSupport, accessibilityLabel: i18n.translate('screens.settings.support.accessibility.label'), accessibilityHint: i18n.translate('screens.settings.support.accessibility.hint'), icon: <Icon name='external_link' width={iconSizes.size12} height={iconSizes.size12} />, }, { id: 4, title: i18n.translate('screens.settings.legal_information.label'), onPress: onPressLegalInformation, accessibilityLabel: i18n.translate('screens.settings.legal_information.accessibility.label'), accessibilityHint: i18n.translate('screens.settings.legal_information.accessibility.hint'), }, ]; if (!Configuration.RELEASE) { bottomItems.push( { id: 5, title: i18n.translate('screens.settings.debug.label'), onPress: onPressDebug, accessibilityLabel: i18n.translate('screens.settings.debug.accessibility.label'), accessibilityHint: i18n.translate('screens.settings.debug.accessibility.hint'), }, ); } return ( <TopComponent style={memoizedStyle.container}> <Layout style={memoizedStyle.layoutContainer}> <View style={memoizedStyle.header}> <ButtonWrapper onPress={onClose} style={memoizedStyle.closeButton} accessibilityLabel={i18n.translate('screens.settings.actions.back.accessibility.label')} accessibilityHint={i18n.translate('screens.settings.actions.back.accessibility.hint')} > <Icon name='close' width={iconSizes.size24} height={iconSizes.size24} /> </ButtonWrapper> </View> <View style={memoizedStyle.itemsContainer}> <Text size='small' weight='bold' textColor={colors.settingsLabelTextColor} style={memoizedStyle.version}>{i18n.translate('screens.settings.version', { version: appVersion, build: appBuild })}</Text> <List items={topItems} style={memoizedStyle.topItems} /> <List items={bottomItems} style={memoizedStyle.bottomItems} /> </View> </Layout> <View style={memoizedStyle.imagesContainer}> <View style={memoizedStyle.sponsors}> <Image source={getThemedImage('republica_portuguesa', name)} style={memoizedStyle.republicaPortuguesaImage} /> <Image source={getThemedImage('logo_dgs', name)} style={memoizedStyle.dgsImage} /> </View> <Image source={getThemedImage('splash', name)} style={memoizedStyle.splashImage} /> </View> </TopComponent> ); } Info.defaultProps = { appVersion: '1.0.0', appBuild: '0', tracingEnabled: false, isInfected: false, onClose: () => { }, onPressTracing: () => { }, onPressLanguage: () => { }, onPressTheme: () => { }, onPressSupport: () => { }, onPressHowToUse: () => { }, onPressFaqs: () => { }, onPressLegalInformation: () => { }, onPressDebug: () => { }, }; Info.propTypes = { appVersion: PropTypes.string, appBuild: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), language: PropTypes.shape({ name: PropTypes.string, languageTag: PropTypes.string, isRTL: PropTypes.bool, }).isRequired, theme: PropTypes.oneOf([commonThemes.names.dark, commonThemes.names.light, commonThemes.names.auto]).isRequired, tracingEnabled: PropTypes.bool, isInfected: PropTypes.bool, onClose: PropTypes.func, onPressTracing: PropTypes.func, onPressLanguage: PropTypes.func, onPressTheme: PropTypes.func, onPressSupport: PropTypes.func, onPressHowToUse: PropTypes.func, onPressFaqs: PropTypes.func, onPressLegalInformation: PropTypes.func, onPressDebug: PropTypes.func, };