Files
RN_Template/RN_TEMPLATE/app/screens/SettingsScreen.tsx

171 lines
5.0 KiB
TypeScript
Raw Permalink Normal View History

2026-02-05 13:16:05 +08:00
import { FC, useCallback, useState } from "react"
import { TextStyle, View, ViewStyle } from "react-native"
import i18n from "i18next"
import { useMMKVString } from "react-native-mmkv"
import { Button } from "@/components/Button"
import { Dialog } from "@/components/Dialog"
import { Icon } from "@/components/Icon"
import { ListItem } from "@/components/ListItem"
import { Screen } from "@/components/Screen"
import { Text } from "@/components/Text"
import { useAuth } from "@/context/AuthContext"
import { translate } from "@/i18n/translate"
import { AppStackScreenProps } from "@/navigators/navigationTypes"
import { useAppTheme } from "@/theme/context"
import { $styles } from "@/theme/styles"
import type { ThemedStyle } from "@/theme/types"
import { s } from "@/utils/responsive"
import { storage } from "@/utils/storage"
import { useHeader } from "@/utils/useHeader"
// Language display names
const LANGUAGE_NAMES: Record<string, string> = {
en: "English",
zh: "中文",
ja: "日本語",
ko: "한국어",
es: "Español",
fr: "Français",
ar: "العربية",
hi: "हिन्दी",
}
// Theme display names
const THEME_NAMES: Record<string, string> = {
system: "System",
light: "Light",
dark: "Dark",
}
export const SettingsScreen: FC<AppStackScreenProps<"Settings">> = function SettingsScreen({
navigation,
}) {
const { themed, theme } = useAppTheme()
const { logout } = useAuth()
const [showLogoutDialog, setShowLogoutDialog] = useState(false)
// Read the stored theme preference directly from MMKV
const [themeScheme] = useMMKVString("ignite.themeScheme", storage)
// Get current theme name for display: if no stored value, it's "system"
const currentThemeName =
themeScheme === "light" || themeScheme === "dark"
? THEME_NAMES[themeScheme]
: THEME_NAMES.system
const handleLogoutPress = useCallback(() => {
setShowLogoutDialog(true)
}, [])
const handleLogoutConfirm = useCallback(() => {
setShowLogoutDialog(false)
logout()
}, [logout])
useHeader(
{
title: translate("settingsScreen:title"),
leftIcon: "back",
onLeftPress: () => navigation.goBack(),
},
[],
)
return (
<Screen preset="fixed" safeAreaEdges={["bottom"]} contentContainerStyle={$styles.flex1}>
{/* Content */}
<View style={[$styles.container, themed($container), $styles.flex1]}>
{/* Appearance Section */}
<Text preset="subheading" tx="settingsScreen:appearance" style={themed($sectionTitle)} />
<ListItem
tx="settingsScreen:theme"
leftIcon="moon"
leftIconColor={theme.colors.tint}
RightComponent={
<View style={$rightContainer}>
<Text size="xs" style={themed($themeText)}>
{currentThemeName}
</Text>
<Icon icon="caretRight" size={s(24)} color={theme.colors.textDim} />
</View>
}
onPress={() => navigation.navigate("Theme")}
/>
{/* Language Section */}
<Text
preset="subheading"
tx="settingsScreen:language"
style={themed($sectionTitleWithMargin)}
/>
<ListItem
tx="settingsScreen:currentLanguage"
leftIcon="globe"
leftIconColor={theme.colors.tint}
RightComponent={
<View style={$rightContainer}>
<Text size="xs" style={themed($languageText)}>
{LANGUAGE_NAMES[i18n.language?.split("-")[0] || "en"] || "English"}
</Text>
<Icon icon="caretRight" size={s(24)} color={theme.colors.textDim} />
</View>
}
onPress={() => navigation.navigate("Language")}
/>
</View>
{/* Logout Button - Fixed at bottom */}
<View style={themed($bottomContainer)}>
<Button tx="common:logOut" preset="reversed" onPress={handleLogoutPress} />
</View>
{/* Logout Confirmation Dialog */}
<Dialog
visible={showLogoutDialog}
onClose={() => setShowLogoutDialog(false)}
preset="destructive"
titleTx="securityScreen:confirmLogout"
messageTx="securityScreen:confirmLogoutMessage"
confirmTx="common:logOut"
onConfirm={handleLogoutConfirm}
onCancel={() => setShowLogoutDialog(false)}
/>
</Screen>
)
}
const $container: ThemedStyle<ViewStyle> = ({ spacing }) => ({
paddingTop: spacing.lg,
})
const $sectionTitle: ThemedStyle<TextStyle> = () => ({
// First section doesn't need top margin
})
const $sectionTitleWithMargin: ThemedStyle<TextStyle> = ({ spacing }) => ({
marginTop: spacing.xxl,
})
const $rightContainer: ViewStyle = {
flexDirection: "row",
alignItems: "center",
}
const $themeText: ThemedStyle<TextStyle> = ({ colors, spacing }) => ({
color: colors.textDim,
marginRight: spacing.xs,
})
const $languageText: ThemedStyle<TextStyle> = ({ colors, spacing }) => ({
color: colors.textDim,
marginRight: spacing.xs,
})
const $bottomContainer: ThemedStyle<ViewStyle> = ({ spacing }) => ({
paddingHorizontal: spacing.lg,
paddingBottom: spacing.md,
})