From 419c5f81e7799f87e98ff761a48b71f90abb3b87 Mon Sep 17 00:00:00 2001 From: Julien Texier Date: Thu, 26 Sep 2024 10:21:01 +0300 Subject: [PATCH] refactor: settings components split --- src/app/(tabs)/settings.tsx | 203 +----------------- src/app/menu-list/[item].tsx | 3 +- .../settings/AppearanceMenuTarget.tsx | 35 +++ .../settings/LanguageMenuTarget.tsx | 27 +++ .../settings/SystemInfoMenuTarget.tsx | 120 +++++++++++ src/components/settings/hooks.tsx | 51 +++++ 6 files changed, 240 insertions(+), 199 deletions(-) create mode 100644 src/components/settings/AppearanceMenuTarget.tsx create mode 100644 src/components/settings/LanguageMenuTarget.tsx create mode 100644 src/components/settings/SystemInfoMenuTarget.tsx create mode 100644 src/components/settings/hooks.tsx diff --git a/src/app/(tabs)/settings.tsx b/src/app/(tabs)/settings.tsx index f0af14d3..032a9610 100644 --- a/src/app/(tabs)/settings.tsx +++ b/src/app/(tabs)/settings.tsx @@ -1,27 +1,17 @@ import { msg } from '@lingui/macro'; -import { - applicationId, - getAndroidId, - getIosIdForVendorAsync, - nativeApplicationVersion, -} from 'expo-application'; -import { setStringAsync } from 'expo-clipboard'; -import { modelName, osVersion, platformApiLevel } from 'expo-device'; -import { updateId as expoUpdateId } from 'expo-updates'; -import capitalize from 'lodash/capitalize'; -import { ComponentProps, FunctionComponent, useEffect, useState } from 'react'; -import { Alert, Platform, TouchableOpacity, View } from 'react-native'; +import { Alert } from 'react-native'; + import { useHeaderPlaygroundButton } from '~app/playground/utils'; import MenuList from '~components/common/MenuList'; -import { showToast } from '~components/common/Toaster'; -import { Icon, Text } from '~components/uikit'; -import config from '~constants/config'; +import { useMenuListItem } from '~components/settings/hooks'; +import { Icon } from '~components/uikit'; import { useAuthStore } from '~services/auth'; -import { useColorMode } from '~services/color-mode'; import { useI18n } from '~services/i18n'; import { styled } from '~styles'; export default function Settings() { + useHeaderPlaygroundButton(); + const { _ } = useI18n(); const logout = useAuthStore((s) => s.logout); function handleLogout() { @@ -31,8 +21,6 @@ export default function Settings() { ]); } - useHeaderPlaygroundButton(); - const items = [ useMenuListItem({ targetName: 'LanguageMenuTarget' }), // useMenuListItem({ targetName: 'AppearanceMenuTarget' }), // We currently do not support dark mode, so until we do, we should not show this option @@ -53,144 +41,6 @@ export default function Settings() { ); } -function LanguageMenuTarget() { - const { setLocale, locale, _ } = useI18n(); - - return ( - setLocale('en'), - }, - { - id: 'fi', - label: _(msg`Finnish`), - checked: locale === 'fi', - onPress: () => setLocale('fi'), - }, - ]} - /> - ); -} - -function AppearanceMenuTarget() { - const { _ } = useI18n(); - const { setColorMode, colorMode } = useColorMode(); - - return ( - setColorMode('system'), - }, - { - id: 'dark', - label: _(msg`Dark`), - checked: colorMode === 'dark', - onPress: () => setColorMode('dark'), - }, - { - id: 'light', - label: _(msg`Light`), - checked: colorMode === 'light', - onPress: () => setColorMode('light'), - }, - ]} - /> - ); -} - -function SystemInfoMenuTarget() { - const { _ } = useI18n(); - const [deviceId, setDeviceId] = useState(null); - - useEffect(() => { - async function fetchDeviceId() { - const id = - Platform.OS === 'android' - ? getAndroidId() - : await getIosIdForVendorAsync(); - setDeviceId(id); - } - fetchDeviceId(); - }, []); - - const items: ComponentProps['items'] = [ - { - id: 'deviceId', - label: _(msg`Device ID`), - currentValue: deviceId || _(msg`Fetching...`), - }, - { - id: 'modelName', - label: _(msg`Model name`), - currentValue: modelName, - }, - { - id: 'systemVersion', - label: _(msg`System version`), - currentValue: osVersion, - }, - { - id: 'apiLevel', - label: _(msg`API level`), - currentValue: platformApiLevel, - platform: 'android', - }, - { - id: 'version', - label: _(msg`Version`), - currentValue: nativeApplicationVersion, - }, - { - id: 'environment', - label: _(msg`App environment`), - currentValue: capitalize(config.appEnv), - }, - ]; - - const updateId = expoUpdateId; - - if (updateId) { - items.push({ - id: 'updateId', - label: _(msg`Update ID`), - currentValue: ( - { - await setStringAsync(updateId); - showToast({ - title: _(msg`Copied to clipboard`), - type: 'success', - icon: 'check', - }); - }} - > - - {updateId.substring(0, 8)} - - - ), - }); - } - - if (config.appEnv !== 'prod') { - items.push({ - id: 'bundleId', - label: _(msg`Bundle ID`), - currentValue: applicationId, - }); - } - - return ; -} - const Wrapper = styled('ScrollView', { flex: 1, }).attrs((p) => ({ @@ -198,44 +48,3 @@ const Wrapper = styled('ScrollView', { padding: p.theme.space.regular, }, })); - -export function useMenuListItem({ targetName }: { targetName: string }) { - const { locale, _ } = useI18n(); - const { colorMode } = useColorMode(); - - let label = ''; - let currentValue; - let target: FunctionComponent = () => ; - - switch (targetName) { - case 'LanguageMenuTarget': - label = _(msg`Language`); - currentValue = locale === 'en' ? _(msg`English`) : _(msg`Suomi`); - target = LanguageMenuTarget; - break; - case 'AppearanceMenuTarget': - label = _(msg`Appearance`); - currentValue = - colorMode === 'light' - ? _(msg`Light`) - : colorMode === 'dark' - ? _(msg`Dark`) - : _(msg`Automatic`); - target = AppearanceMenuTarget; - break; - case 'SystemInfoMenuTarget': - label = _(msg`Info`); - target = SystemInfoMenuTarget; - break; - default: - break; - } - - return { - id: targetName, - label, - currentValue, - target, - targetName, - }; -} diff --git a/src/app/menu-list/[item].tsx b/src/app/menu-list/[item].tsx index 186157ee..8b47b481 100644 --- a/src/app/menu-list/[item].tsx +++ b/src/app/menu-list/[item].tsx @@ -1,10 +1,9 @@ import { Stack, router, useLocalSearchParams } from 'expo-router'; import { useEffect } from 'react'; +import { useMenuListItem } from '~components/settings/hooks'; import { styled } from '~styles'; -import { useMenuListItem } from '../(tabs)/settings'; - export default function MenuListItem() { const { item } = useLocalSearchParams<{ item: string }>(); const { target, label } = useMenuListItem({ targetName: item as string }); diff --git a/src/components/settings/AppearanceMenuTarget.tsx b/src/components/settings/AppearanceMenuTarget.tsx new file mode 100644 index 00000000..da7dc45c --- /dev/null +++ b/src/components/settings/AppearanceMenuTarget.tsx @@ -0,0 +1,35 @@ +import { msg } from '@lingui/macro'; + +import MenuList from '~components/common/MenuList'; +import { useColorMode } from '~services/color-mode'; +import { useI18n } from '~services/i18n'; + +export function AppearanceMenuTarget() { + const { _ } = useI18n(); + const { setColorMode, colorMode } = useColorMode(); + + return ( + setColorMode('system'), + }, + { + id: 'dark', + label: _(msg`Dark`), + checked: colorMode === 'dark', + onPress: () => setColorMode('dark'), + }, + { + id: 'light', + label: _(msg`Light`), + checked: colorMode === 'light', + onPress: () => setColorMode('light'), + }, + ]} + /> + ); +} diff --git a/src/components/settings/LanguageMenuTarget.tsx b/src/components/settings/LanguageMenuTarget.tsx new file mode 100644 index 00000000..0796b924 --- /dev/null +++ b/src/components/settings/LanguageMenuTarget.tsx @@ -0,0 +1,27 @@ +import { msg } from '@lingui/macro'; + +import MenuList from '~components/common/MenuList'; +import { useI18n } from '~services/i18n'; + +export function LanguageMenuTarget() { + const { setLocale, locale, _ } = useI18n(); + + return ( + setLocale('en'), + }, + { + id: 'fi', + label: _(msg`Finnish`), + checked: locale === 'fi', + onPress: () => setLocale('fi'), + }, + ]} + /> + ); +} diff --git a/src/components/settings/SystemInfoMenuTarget.tsx b/src/components/settings/SystemInfoMenuTarget.tsx new file mode 100644 index 00000000..3bb313bf --- /dev/null +++ b/src/components/settings/SystemInfoMenuTarget.tsx @@ -0,0 +1,120 @@ +import { msg } from '@lingui/macro'; +import { + applicationId, + getAndroidId, + getIosIdForVendorAsync, + nativeApplicationVersion, +} from 'expo-application'; +import { setStringAsync } from 'expo-clipboard'; +import { modelName, osVersion, platformApiLevel } from 'expo-device'; +import { updateId as expoUpdateId } from 'expo-updates'; +import capitalize from 'lodash/capitalize'; +import { ComponentProps, useEffect, useState } from 'react'; +import { Platform, TouchableOpacity } from 'react-native'; + +import MenuList from '~components/common/MenuList'; +import { showToast } from '~components/common/Toaster'; +import { Text } from '~components/uikit'; +import config from '~constants/config'; +import { useI18n } from '~services/i18n'; + +export function SystemInfoMenuTarget() { + const { _ } = useI18n(); + const { deviceId, loading } = useDeviceId(); + + const items: ComponentProps['items'] = [ + { + id: 'deviceId', + label: _(msg`Device ID`), + currentValue: loading + ? _(msg`Fetching...`) + : deviceId || _(msg`Unavailable`), + }, + { + id: 'modelName', + label: _(msg`Model name`), + currentValue: modelName, + }, + { + id: 'systemVersion', + label: _(msg`System version`), + currentValue: osVersion, + }, + { + id: 'apiLevel', + label: _(msg`API level`), + currentValue: platformApiLevel, + platform: 'android', + }, + { + id: 'version', + label: _(msg`Version`), + currentValue: nativeApplicationVersion, + }, + { + id: 'environment', + label: _(msg`App environment`), + currentValue: capitalize(config.appEnv), + }, + ]; + + const updateId = expoUpdateId; + + if (updateId) { + items.push({ + id: 'updateId', + label: _(msg`Update ID`), + currentValue: ( + { + await setStringAsync(updateId); + showToast({ + title: _(msg`Copied to clipboard`), + type: 'success', + icon: 'check', + }); + }} + > + + {updateId.substring(0, 8)} + + + ), + }); + } + + if (config.appEnv !== 'prod') { + items.push({ + id: 'bundleId', + label: _(msg`Bundle ID`), + currentValue: applicationId, + }); + } + + return ; +} + +function useDeviceId() { + const [deviceId, setDeviceId] = useState(null); + const [loading, setLoading] = useState(true); + + useEffect(() => { + async function fetchDeviceId() { + try { + const id = + Platform.OS === 'android' + ? getAndroidId() + : await getIosIdForVendorAsync(); + setDeviceId(id); + } catch (error) { + console.error('Failed to fetch device ID:', error); + } finally { + setLoading(false); + } + } + + fetchDeviceId(); + }, []); + + return { deviceId, loading }; +} diff --git a/src/components/settings/hooks.tsx b/src/components/settings/hooks.tsx new file mode 100644 index 00000000..775da75d --- /dev/null +++ b/src/components/settings/hooks.tsx @@ -0,0 +1,51 @@ +import { msg } from '@lingui/macro'; +import { FunctionComponent } from 'react'; +import { View } from 'react-native'; + +import { useColorMode } from '~services/color-mode'; +import { useI18n } from '~services/i18n'; + +import { AppearanceMenuTarget } from './AppearanceMenuTarget'; +import { LanguageMenuTarget } from './LanguageMenuTarget'; +import { SystemInfoMenuTarget } from './SystemInfoMenuTarget'; + +export function useMenuListItem({ targetName }: { targetName: string }) { + const { locale, _ } = useI18n(); + const { colorMode } = useColorMode(); + + let label = ''; + let currentValue; + let target: FunctionComponent = () => ; + + switch (targetName) { + case 'LanguageMenuTarget': + label = _(msg`Language`); + currentValue = locale === 'en' ? _(msg`English`) : _(msg`Suomi`); + target = LanguageMenuTarget; + break; + case 'AppearanceMenuTarget': + label = _(msg`Appearance`); + currentValue = + colorMode === 'light' + ? _(msg`Light`) + : colorMode === 'dark' + ? _(msg`Dark`) + : _(msg`Automatic`); + target = AppearanceMenuTarget; + break; + case 'SystemInfoMenuTarget': + label = _(msg`Info`); + target = SystemInfoMenuTarget; + break; + default: + break; + } + + return { + id: targetName, + label, + currentValue, + target, + targetName, + }; +}