/** * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ import { Alert, AsyncStorage, Platform, SectionList, StyleSheet, View } from 'react-native'; import React, { useContext, useEffect } from 'react'; import { Text, ThemeContext } from 'react-native-elements'; import { action } from 'mobx'; import { observer } from 'mobx-react'; import { SafeAreaView } from 'react-native-safe-area-context'; import { useNavigation } from '@react-navigation/native'; import { useTranslation } from 'react-i18next'; import AppInfoFooter from '../components/AppInfoFooter'; import BrowserListItem from '../components/BrowserListItem'; import ButtonListItem from '../components/ButtonListItem'; import { isSystemThemeSupported } from '../utils/Device'; import Links from '../constants/Links'; import Screens from '../constants/Screens'; import ServerListItem from '../components/ServerListItem'; import SwitchListItem from '../components/SwitchListItem'; import { useStores } from '../hooks/useStores'; const SettingsScreen = observer(() => { const { rootStore } = useStores(); const navigation = useNavigation(); const { t } = useTranslation(); const { theme } = useContext(ThemeContext); useEffect(() => { // Fetch server info rootStore.serverStore.fetchInfo(); }, []); const onAddServer = () => { navigation.navigate(Screens.AddServerScreen); }; const onDeleteServer = index => { Alert.alert( t('alerts.deleteServer.title'), t('alerts.deleteServer.description', { serverName: rootStore.serverStore.servers[index]?.name }), [ { text: t('common.cancel') }, { text: t('alerts.deleteServer.confirm'), onPress: action(() => { // Remove server and update active server rootStore.serverStore.removeServer(index); rootStore.settingStore.activeServer = 0; if (rootStore.serverStore.servers.length > 0) { // More servers exist, navigate home navigation.replace(Screens.HomeScreen); navigation.navigate(Screens.HomeTab); } else { // No servers are present, navigate to add server screen navigation.replace(Screens.AddServerScreen); } }), style: 'destructive' } ] ); }; const onSelectServer = action(index => { rootStore.settingStore.activeServer = index; navigation.replace(Screens.HomeScreen); navigation.navigate(Screens.HomeTab); }); const onResetApplication = () => { Alert.alert( t('alerts.resetApplication.title'), t('alerts.resetApplication.description'), [ { text: t('common.cancel') }, { text: t('alerts.resetApplication.confirm'), onPress: action(() => { // Reset data in stores rootStore.reset(); AsyncStorage.clear(); // Navigate to the loading screen navigation.replace(Screens.AddServerScreen); }), style: 'destructive' } ] ); }; const AugmentedServerListItem = (props) => ( ); const getSections = () => { const settingsData = [{ key: 'keep-awake-switch', title: t('settings.keepAwake'), value: rootStore.settingStore.isScreenLockEnabled, onValueChange: action(value => rootStore.settingStore.isScreenLockEnabled = value) }]; // Orientation lock is not supported on iPad without disabling multitasking // https://docs.expo.io/versions/latest/sdk/screen-orientation/#warning if (Platform.OS !== 'ios' || !Platform.isPad) { settingsData.push({ key: 'rotation-lock-switch', title: t('settings.rotationLock'), value: rootStore.settingStore.isRotationLockEnabled, onValueChange: action(value => rootStore.settingStore.isRotationLockEnabled = value) }); } const playbackSettingsData = []; // TODO: Add Android support for native video player if (Platform.OS === 'ios') { playbackSettingsData.push({ key: 'native-video-switch', title: t('settings.nativeVideoPlayer'), subtitle: t('settings.minimumServerVersion'), value: rootStore.settingStore.isNativeVideoPlayerEnabled, onValueChange: action(value => { rootStore.settingStore.isNativeVideoPlayerEnabled = value; rootStore.isReloadRequired = true; }) }); if (Platform.Version > 12) { playbackSettingsData.push({ key: 'native-video-fmp4-switch', title: t('settings.fmp4Support'), value: rootStore.settingStore.isFmp4Enabled, disabled: !rootStore.settingStore.isNativeVideoPlayerEnabled, onValueChange: action(value => { rootStore.settingStore.isFmp4Enabled = value; rootStore.isReloadRequired = true; }) }); } } const appearanceSettingsData = [{ key: 'tab-labels-switch', title: t('settings.tabLabels'), value: rootStore.settingStore.isTabLabelsEnabled, onValueChange: action(value => rootStore.settingStore.isTabLabelsEnabled = value) }]; if (isSystemThemeSupported()) { appearanceSettingsData.push({ key: 'system-theme-switch', title: t('settings.systemTheme'), value: rootStore.settingStore.isSystemThemeEnabled, onValueChange: action(value => rootStore.settingStore.isSystemThemeEnabled = value) }); } // TODO: This should be able to select from a list not just a switch appearanceSettingsData.push({ key: 'theme-switch', title: t('settings.lightTheme'), disabled: rootStore.settingStore.isSystemThemeEnabled, value: rootStore.settingStore.themeId === 'light', onValueChange: action(value => rootStore.settingStore.themeId = value ? 'light' : 'dark') }); return [ { title: t('headings.servers'), data: rootStore.serverStore.servers.slice(), keyExtractor: (item, index) => `server-${index}`, renderItem: AugmentedServerListItem }, { title: t('headings.addServer'), hideHeader: true, data: [{ key: 'add-server-button', title: t('headings.addServer'), onPress: onAddServer }], renderItem: ButtonListItem }, { title: t('headings.settings'), data: settingsData, renderItem: SwitchListItem }, { title: t('headings.playback'), data: playbackSettingsData, renderItem: SwitchListItem }, { title: t('headings.appearance'), data: appearanceSettingsData, renderItem: SwitchListItem }, { title: t('headings.links'), data: Links.map(link => ({ ...link, name: t(link.name) })), renderItem: BrowserListItem }, { title: t('alerts.resetApplication.title'), hideHeader: true, data: [{ key: 'reset-app-button', title: t('alerts.resetApplication.title'), titleStyle: { color: theme.colors.error }, onPress: onResetApplication }], renderItem: ButtonListItem } ]; }; return ( {JSON.stringify(item)}} renderSectionHeader={({ section: { data, title, hideHeader } }) => { if (!data || data.length === 0) { return null; } if (hideHeader) { return ; } return ( {title} ); }} renderSectionFooter={({ section: { data } }) => { if (!data || data.length === 0) { return null; } return ; }} ListFooterComponent={AppInfoFooter} showsVerticalScrollIndicator={false} /> ); }); const styles = StyleSheet.create({ container: { flex: 1 }, header: { fontWeight: '600', paddingVertical: 8, paddingHorizontal: 15, marginBottom: 1 }, emptyHeader: { marginTop: 15 }, footer: { marginBottom: 15 } }); export default SettingsScreen;