diff --git a/src/alf/fonts.ts b/src/alf/fonts.ts index 5438408dc9..cae6068c7c 100644 --- a/src/alf/fonts.ts +++ b/src/alf/fonts.ts @@ -1,5 +1,24 @@ import {useFonts as defaultUseFonts} from 'expo-font' +import {Device, device} from '#/storage' + +export function getFontScale() { + const fontScale = device.get(['fontScale']) || 1 + return fontScale +} + +export function setFontScale(fontScale: Device['fontScale']) { + device.set(['fontScale'], fontScale) +} + +export function getFontFamily() { + return device.get(['fontFamily']) || 'system' +} + +export function setFontFamily(fontFamily: Device['fontFamily']) { + device.set(['fontFamily'], fontFamily) +} + export function useFonts() { return defaultUseFonts({ 'Inter-Thin': require('../../assets/fonts/inter/Inter-Thin.otf'), diff --git a/src/alf/index.tsx b/src/alf/index.tsx index d1d85e37bb..96eb49f95d 100644 --- a/src/alf/index.tsx +++ b/src/alf/index.tsx @@ -2,10 +2,16 @@ import React from 'react' import {useMediaQuery} from 'react-responsive' import {useGate} from '#/lib/statsig/statsig' +import { + getFontFamily, + getFontScale, + setFontFamily as persistFontFamily, + setFontScale as persistFontScale, +} from '#/alf/fonts' import {createThemes, defaultTheme} from '#/alf/themes' import {Theme, ThemeName} from '#/alf/types' import {BLUE_HUE, GREEN_HUE, RED_HUE} from '#/alf/util/colorGeneration' -import * as fontScaling from '#/alf/util/fontScaling' +import {Device} from '#/storage' export {atoms} from '#/alf/atoms' export * from '#/alf/fonts' @@ -19,8 +25,12 @@ export type Alf = { themeName: ThemeName theme: Theme themes: ReturnType - fontScale: number - setFontScale: (fontScale: number) => void + fonts: { + scale: Exclude + family: Device['fontFamily'] + setFontScale: (fontScale: Exclude) => void + setFontFamily: (fontFamily: Device['fontFamily']) => void + } flags: { neue: boolean } @@ -39,8 +49,12 @@ export const Context = React.createContext({ positive: GREEN_HUE, }, }), - fontScale: fontScaling.get(), - setFontScale: () => {}, + fonts: { + scale: getFontScale(), + family: getFontFamily(), + setFontScale: () => {}, + setFontFamily: () => {}, + }, flags: { neue: false, }, @@ -52,17 +66,33 @@ export function ThemeProvider({ }: React.PropsWithChildren<{theme: ThemeName}>) { const gate = useGate() const [neue] = React.useState(() => gate('typography_neue')) - const [fontScale, setFontScale] = React.useState(() => { - if (!neue) return 1 - return fontScaling.get() - }) - const setFontScaleAndPersist = React.useCallback( - (fontScale: number) => { + const [fontScale, setFontScale] = React.useState( + () => { + if (!neue) return 1 + return getFontScale() + }, + ) + const setFontScaleAndPersist = React.useCallback< + Alf['fonts']['setFontScale'] + >( + fontScale => { setFontScale(fontScale) - fontScaling.set(fontScale) + persistFontScale(fontScale) }, [setFontScale], ) + const [fontFamily, setFontFamily] = React.useState( + () => getFontFamily(), + ) + const setFontFamilyAndPersist = React.useCallback< + Alf['fonts']['setFontFamily'] + >( + fontFamily => { + setFontFamily(fontFamily) + persistFontFamily(fontFamily) + }, + [setFontFamily], + ) const themes = React.useMemo(() => { return createThemes({ hues: { @@ -80,13 +110,25 @@ export function ThemeProvider({ themes, themeName: themeName, theme: themes[themeName], - fontScale, - setFontScale: setFontScaleAndPersist, + fonts: { + scale: fontScale, + family: fontFamily, + setFontScale: setFontScaleAndPersist, + setFontFamily: setFontFamilyAndPersist, + }, flags: { neue, }, }), - [themeName, themes, fontScale, setFontScaleAndPersist, neue], + [ + themeName, + themes, + neue, + fontScale, + setFontScaleAndPersist, + fontFamily, + setFontFamilyAndPersist, + ], )}> {children} @@ -97,11 +139,6 @@ export function useAlf() { return React.useContext(Context) } -export function useFontScale() { - const {fontScale, setFontScale} = useAlf() - return {fontScale, setFontScale} -} - export function useTheme(theme?: ThemeName) { const alf = useAlf() return React.useMemo(() => { diff --git a/src/components/Typography.tsx b/src/components/Typography.tsx index 3d7c32ce6b..0cfeb84cc8 100644 --- a/src/components/Typography.tsx +++ b/src/components/Typography.tsx @@ -3,7 +3,7 @@ import {StyleProp, TextProps as RNTextProps, TextStyle} from 'react-native' import {UITextView} from 'react-native-uitextview' import {isNative} from '#/platform/detection' -import {applyFonts, atoms, flatten, useAlf, useTheme, web} from '#/alf' +import {Alf, applyFonts, atoms, flatten, useAlf, useTheme, web} from '#/alf' export type TextProps = RNTextProps & { /** @@ -36,7 +36,15 @@ export function leading< */ export function normalizeTextStyles( styles: StyleProp, - {fontScale, flags}: {fontScale: number; flags: {neue: boolean}}, + { + fontScale, + fontFamily, + flags, + }: { + fontScale: number + fontFamily: Alf['fonts']['family'] + flags: {neue: boolean} + }, ) { const s = flatten(styles) // should always be defined on these components @@ -50,7 +58,7 @@ export function normalizeTextStyles( s.lineHeight = s.fontSize } - if (flags.neue) { + if (flags.neue && fontFamily === 'theme') { applyFonts(s) } @@ -61,10 +69,11 @@ export function normalizeTextStyles( * Our main text component. Use this most of the time. */ export function Text({style, selectable, ...rest}: TextProps) { - const {fontScale, flags} = useAlf() + const {fonts, flags} = useAlf() const t = useTheme() const s = normalizeTextStyles([atoms.text_sm, t.atoms.text, flatten(style)], { - fontScale, + fontScale: fonts.scale, + fontFamily: fonts.family, flags, }) diff --git a/src/screens/Settings/AppearanceSettings.tsx b/src/screens/Settings/AppearanceSettings.tsx index 00a04bbfb6..57d7eff066 100644 --- a/src/screens/Settings/AppearanceSettings.tsx +++ b/src/screens/Settings/AppearanceSettings.tsx @@ -10,11 +10,13 @@ import {useLingui} from '@lingui/react' import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries' import {CommonNavigatorParams, NativeStackScreenProps} from '#/lib/routes/types' +import {useGate} from '#/lib/statsig/statsig' import {s} from '#/lib/styles' import {useSetThemePrefs, useThemePrefs} from '#/state/shell' import {SimpleViewHeader} from '#/view/com/util/SimpleViewHeader' import {ScrollView} from '#/view/com/util/Views' -import {atoms as a, native, useTheme} from '#/alf' +import {atoms as a, native, useAlf, useTheme} from '#/alf' +import * as Toggle from '#/components/forms/Toggle' import * as ToggleButton from '#/components/forms/ToggleButton' import {Moon_Stroke2_Corner0_Rounded as MoonIcon} from '#/components/icons/Moon' import {Phone_Stroke2_Corner0_Rounded as PhoneIcon} from '#/components/icons/Phone' @@ -22,13 +24,21 @@ import {Text} from '#/components/Typography' type Props = NativeStackScreenProps export function AppearanceSettingsScreen({}: Props) { - const {_} = useLingui() const t = useTheme() + const {_} = useLingui() + const gate = useGate() + const [neue] = React.useState(() => gate('typography_neue')) const {isTabletOrMobile} = useWebMediaQueries() + const {fonts} = useAlf() const {colorMode, darkTheme} = useThemePrefs() const {setColorMode, setDarkTheme} = useSetThemePrefs() + const [fontScale, setFontScale] = React.useState(() => fonts.scale !== 1) + const [fontFamily, setFontFamily] = React.useState( + () => fonts.family === 'theme', + ) + const onChangeAppearance = useCallback( (keys: string[]) => { const appearance = keys.find(key => key !== colorMode) as @@ -128,6 +138,34 @@ export function AppearanceSettingsScreen({}: Props) { )} + + {neue && ( + + Neue options + { + setFontScale(checked) + fonts.setFontScale(checked ? 0.9375 : 1) + }}> + Enable new font scale + + + { + setFontFamily(checked) + fonts.setFontFamily(checked ? 'theme' : 'system') + }}> + Use theme font + + + + )} diff --git a/src/storage/index.ts b/src/storage/index.ts index 819ffab7ec..4be08170dd 100644 --- a/src/storage/index.ts +++ b/src/storage/index.ts @@ -2,6 +2,8 @@ import {MMKV} from 'react-native-mmkv' import {Device} from '#/storage/schema' +export * from '#/storage/schema' + /** * Generic storage class. DO NOT use this directly. Instead, use the exported * storage instances below. diff --git a/src/storage/schema.ts b/src/storage/schema.ts index 6ef042e021..11f840c445 100644 --- a/src/storage/schema.ts +++ b/src/storage/schema.ts @@ -3,5 +3,6 @@ */ export type Device = { fontScale: number | undefined + fontFamily: 'system' | 'theme' lastNuxDialog: string | undefined } diff --git a/src/view/screens/Settings/index.tsx b/src/view/screens/Settings/index.tsx index 6770a96d9f..fe449fcdbc 100644 --- a/src/view/screens/Settings/index.tsx +++ b/src/view/screens/Settings/index.tsx @@ -54,7 +54,7 @@ import * as Toast from 'view/com/util/Toast' import {UserAvatar} from 'view/com/util/UserAvatar' import {ScrollView} from 'view/com/util/Views' import {DeactivateAccountDialog} from '#/screens/Settings/components/DeactivateAccountDialog' -import {atoms as a, useAlf, useTheme} from '#/alf' +import {atoms as a, useTheme} from '#/alf' import {useDialogControl} from '#/components/Dialog' import {BirthDateSettingsDialog} from '#/components/dialogs/BirthDateSettings' import {Email2FAToggle} from './Email2FAToggle' @@ -133,7 +133,6 @@ function SettingsAccountCard({ type Props = NativeStackScreenProps export function SettingsScreen({}: Props) { - const {setFontScale} = useAlf() const queryClient = useQueryClient() const pal = usePalette('default') const {_} = useLingui() @@ -820,16 +819,6 @@ export function SettingsScreen({}: Props) { {__DEV__ ? ( <> - setFontScale(0.9375)} - accessibilityRole="button" - accessibilityLabel={_(msg`Set test font scale`)} - accessibilityHint={_(msg`Set test font scale`)}> - - Enable neue font scale - -