Files
RN_Template/RN_TEMPLATE/app/screens/ThemeScreen.tsx
2026-02-05 13:16:05 +08:00

109 lines
3.3 KiB
TypeScript

import { FC, useCallback } from "react"
import { TextStyle, View, ViewStyle } from "react-native"
import { useMMKVString } from "react-native-mmkv"
import { Icon } from "@/components/Icon"
import { ListItem } from "@/components/ListItem"
import { Screen } from "@/components/Screen"
import { Text } from "@/components/Text"
import { translate } from "@/i18n/translate"
import { AppStackScreenProps } from "@/navigators/navigationTypes"
import { useAppTheme } from "@/theme/context"
import { $styles } from "@/theme/styles"
import type { ImmutableThemeContextModeT } from "@/theme/types"
import type { ThemedStyle } from "@/theme/types"
import { s } from "@/utils/responsive"
import { storage } from "@/utils/storage"
import { useHeader } from "@/utils/useHeader"
type ThemeOption = {
value: ImmutableThemeContextModeT | "system"
labelKey: string
}
const THEME_OPTIONS: ThemeOption[] = [
{ value: "system", labelKey: "themeScreen:system" },
{ value: "light", labelKey: "themeScreen:light" },
{ value: "dark", labelKey: "themeScreen:dark" },
]
export const ThemeScreen: FC<AppStackScreenProps<"Theme">> = function ThemeScreen({ navigation }) {
const { themed, theme, setThemeContextOverride } = useAppTheme()
// Read the stored theme preference directly from MMKV
// undefined = system, "light" = light, "dark" = dark
const [themeScheme] = useMMKVString("ignite.themeScheme", storage)
// Determine current selection: if no stored value, it's "system"
const currentTheme = themeScheme === "light" || themeScheme === "dark" ? themeScheme : "system"
const handleSelectTheme = useCallback(
(value: ImmutableThemeContextModeT | "system") => {
if (value === "system") {
setThemeContextOverride(undefined)
} else {
setThemeContextOverride(value)
}
navigation.goBack()
},
[setThemeContextOverride, navigation],
)
useHeader(
{
title: translate("themeScreen:title"),
leftIcon: "back",
onLeftPress: () => navigation.goBack(),
},
[],
)
return (
<Screen
preset="scroll"
safeAreaEdges={["bottom"]}
contentContainerStyle={[$styles.container, themed($container)]}
>
<Text size="xs" style={themed($hint)}>
{translate("themeScreen:selectHint")}
</Text>
<View style={themed($listContainer)}>
{THEME_OPTIONS.map((option) => {
const isSelected = currentTheme === option.value
return (
<ListItem
key={option.value}
text={translate(option.labelKey as any)}
textStyle={isSelected ? themed($selectedText) : undefined}
RightComponent={
isSelected ? <Icon icon="check" size={20} color={theme.colors.tint} /> : undefined
}
onPress={() => handleSelectTheme(option.value)}
/>
)
})}
</View>
</Screen>
)
}
const $container: ThemedStyle<ViewStyle> = ({ spacing }) => ({
paddingTop: spacing.md,
})
const $hint: ThemedStyle<TextStyle> = ({ colors, spacing }) => ({
color: colors.textDim,
marginBottom: spacing.md,
})
const $listContainer: ThemedStyle<ViewStyle> = ({ colors }) => ({
backgroundColor: colors.palette.neutral200,
borderRadius: s(8),
overflow: "hidden",
})
const $selectedText: ThemedStyle<TextStyle> = ({ colors }) => ({
color: colors.tint,
fontWeight: "600",
})