diff --git a/assets/icons/textSize_stroke2_corner0_rounded.svg b/assets/icons/textSize_stroke2_corner0_rounded.svg new file mode 100644 index 0000000000..6c7537d100 --- /dev/null +++ b/assets/icons/textSize_stroke2_corner0_rounded.svg @@ -0,0 +1 @@ + diff --git a/assets/icons/titleCase_stroke2_corner0_rounded.svg b/assets/icons/titleCase_stroke2_corner0_rounded.svg new file mode 100644 index 0000000000..facdfc0e16 --- /dev/null +++ b/assets/icons/titleCase_stroke2_corner0_rounded.svg @@ -0,0 +1 @@ + diff --git a/src/alf/fonts.ts b/src/alf/fonts.ts index c38ba6be3c..f20de30b94 100644 --- a/src/alf/fonts.ts +++ b/src/alf/fonts.ts @@ -2,8 +2,21 @@ import {useFonts as defaultUseFonts} from 'expo-font' import {Device, device} from '#/storage' +const factor = 0.0625 // 1 - (15/16) +const fontScaleMultipliers: Record = { + '-2': 1 - factor * 3, + '-1': 1 - factor * 2, + '0': 1 - factor * 1, // default + '1': 1, + '2': 1 + factor * 1, +} + +export function computeFontScaleMultiplier(scale: Device['fontScale']) { + return fontScaleMultipliers[scale] +} + export function getFontScale() { - return device.get(['fontScale']) || 1 + return device.get(['fontScale']) || '0' } export function setFontScale(fontScale: Device['fontScale']) { diff --git a/src/alf/index.tsx b/src/alf/index.tsx index 96eb49f95d..20cd9c4583 100644 --- a/src/alf/index.tsx +++ b/src/alf/index.tsx @@ -3,6 +3,7 @@ import {useMediaQuery} from 'react-responsive' import {useGate} from '#/lib/statsig/statsig' import { + computeFontScaleMultiplier, getFontFamily, getFontScale, setFontFamily as persistFontFamily, @@ -27,6 +28,7 @@ export type Alf = { themes: ReturnType fonts: { scale: Exclude + scaleMultiplier: number family: Device['fontFamily'] setFontScale: (fontScale: Exclude) => void setFontFamily: (fontFamily: Device['fontFamily']) => void @@ -51,6 +53,7 @@ export const Context = React.createContext({ }), fonts: { scale: getFontScale(), + scaleMultiplier: computeFontScaleMultiplier(getFontScale()), family: getFontFamily(), setFontScale: () => {}, setFontFamily: () => {}, @@ -66,11 +69,11 @@ 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 getFontScale() - }, + const [fontScale, setFontScale] = React.useState(() => + getFontScale(), + ) + const [fontScaleMultiplier, setFontScaleMultiplier] = React.useState(() => + computeFontScaleMultiplier(fontScale), ) const setFontScaleAndPersist = React.useCallback< Alf['fonts']['setFontScale'] @@ -78,6 +81,7 @@ export function ThemeProvider({ fontScale => { setFontScale(fontScale) persistFontScale(fontScale) + setFontScaleMultiplier(computeFontScaleMultiplier(fontScale)) }, [setFontScale], ) @@ -112,6 +116,7 @@ export function ThemeProvider({ theme: themes[themeName], fonts: { scale: fontScale, + scaleMultiplier: fontScaleMultiplier, family: fontFamily, setFontScale: setFontScaleAndPersist, setFontFamily: setFontFamilyAndPersist, @@ -128,6 +133,7 @@ export function ThemeProvider({ setFontScaleAndPersist, fontFamily, setFontFamilyAndPersist, + fontScaleMultiplier, ], )}> {children} diff --git a/src/components/Typography.tsx b/src/components/Typography.tsx index 0cfeb84cc8..f889edd00d 100644 --- a/src/components/Typography.tsx +++ b/src/components/Typography.tsx @@ -72,7 +72,7 @@ export function Text({style, selectable, ...rest}: TextProps) { const {fonts, flags} = useAlf() const t = useTheme() const s = normalizeTextStyles([atoms.text_sm, t.atoms.text, flatten(style)], { - fontScale: fonts.scale, + fontScale: fonts.scaleMultiplier, fontFamily: fonts.family, flags, }) diff --git a/src/components/icons/TextSize.tsx b/src/components/icons/TextSize.tsx new file mode 100644 index 0000000000..73a6a085db --- /dev/null +++ b/src/components/icons/TextSize.tsx @@ -0,0 +1,5 @@ +import {createSinglePathSVG} from './TEMPLATE' + +export const TextSize_Stroke2_Corner0_Rounded = createSinglePathSVG({ + path: 'M9 5a1 1 0 0 1 1-1h12a1 1 0 1 1 0 2h-5v14a1 1 0 1 1-2 0V6h-5a1 1 0 0 1-1-1Zm-3.073 7v8a1 1 0 1 0 2 0v-8H12a1 1 0 1 0 0-2H6.971a1.015 1.015 0 0 0-.089 0H2a1 1 0 1 0 0 2h3.927Z', +}) diff --git a/src/components/icons/TitleCase.tsx b/src/components/icons/TitleCase.tsx new file mode 100644 index 0000000000..9d040c9e5a --- /dev/null +++ b/src/components/icons/TitleCase.tsx @@ -0,0 +1,5 @@ +import {createSinglePathSVG} from './TEMPLATE' + +export const TitleCase_Stroke2_Corner0_Rounded = createSinglePathSVG({ + path: 'M3.65 17.247c-.242.832-.632 1.178-1.325 1.178-.814 0-1.325-.476-1.325-1.23 0-.216.06-.51.173-.831L4.586 7.07c.364-1.014.979-1.482 1.966-1.482 1.022 0 1.629.45 2.001 1.473l3.43 9.303c.121.337.165.571.165.831 0 .72-.546 1.23-1.308 1.23-.736 0-1.126-.338-1.36-1.152l-.658-1.975H4.309l-.658 1.95ZM6.5 8.152l-1.62 5.12h3.335l-1.654-5.12H6.5Zm13.005 8.688c-.52.988-1.68 1.568-2.84 1.568-1.768 0-3.11-1.144-3.11-2.815 0-1.69 1.299-2.668 3.62-2.807l2.34-.138v-.615c0-.867-.607-1.369-1.56-1.369-.771 0-1.239.251-1.802.979-.277.312-.597.468-1.004.468-.615 0-1.057-.399-1.057-.97 0-.2.043-.382.13-.572.433-1.109 1.923-1.793 3.845-1.793 2.383 0 3.933 1.23 3.933 3.1v5.293c0 .84-.511 1.273-1.23 1.273-.684 0-1.16-.38-1.213-1.126v-.476h-.052Zm-3.43-1.386c0 .693.572 1.126 1.42 1.126 1.11 0 2.02-.719 2.02-1.723v-.676l-1.959.121c-.944.07-1.48.494-1.48 1.152Z', +}) diff --git a/src/screens/Settings/AppearanceSettings.tsx b/src/screens/Settings/AppearanceSettings.tsx index 04f39af6eb..712187b436 100644 --- a/src/screens/Settings/AppearanceSettings.tsx +++ b/src/screens/Settings/AppearanceSettings.tsx @@ -16,11 +16,11 @@ 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, useAlf, useTheme} from '#/alf' -import {Divider} from '#/components/Divider' -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' +import {TextSize_Stroke2_Corner0_Rounded as TextSize} from '#/components/icons/TextSize' +import {TitleCase_Stroke2_Corner0_Rounded as Aa} from '#/components/icons/TitleCase' import {Text} from '#/components/Typography' type Props = NativeStackScreenProps @@ -35,10 +35,8 @@ export function AppearanceSettingsScreen({}: Props) { 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 [fontFamily, setFontFamily] = React.useState(fonts.family) + const [fontScale, setFontScale] = React.useState(() => fonts.scale) const onChangeAppearance = useCallback( (keys: string[]) => { @@ -65,6 +63,24 @@ export function AppearanceSettingsScreen({}: Props) { [setDarkTheme, darkTheme], ) + const onChangeFontFamily = useCallback( + (values: string[]) => { + const next = values[0] === 'system' ? 'system' : 'theme' + setFontFamily(next) + fonts.setFontFamily(next) + }, + [setFontFamily, fonts], + ) + + const onChangeFontScale = useCallback( + (values: string[]) => { + const next = values[0] || ('0' as any) + setFontScale(next) + fonts.setFontScale(next) + }, + [setFontScale, fonts], + ) + return ( @@ -87,7 +103,7 @@ export function AppearanceSettingsScreen({}: Props) { - + Mode @@ -120,7 +136,7 @@ export function AppearanceSettingsScreen({}: Props) { style={[a.gap_md]}> - + Dark theme @@ -142,105 +158,80 @@ export function AppearanceSettingsScreen({}: Props) { )} + + {neue && ( + <> + + + + + + Font + + + + + For the best experience, we recommend using the theme + font. + + + + + + + System + + + + + Theme + + + + + + + + + + + Font size + + + + + + + + Smaller + + + + + Default + + + + + Larger + + + + + + )} - - {neue && ( - - Typography - - - - - - - Theme font - - - - Enables a consistent typeface for a clean user - experience across devices. To use your default system - font, disable this. - - - - - { - setFontFamily(checked) - fonts.setFontFamily(checked ? 'theme' : 'system') - }}> - - {fontFamily ? ( - Enabled - ) : ( - Disabled - )} - - - - - - - - - - - Theme font size - - - - Subtly scales the type size for a clean user experience - across devices. To use the font size defined by your - operating system, disable this. - - - - - { - setFontScale(checked) - fonts.setFontScale(checked ? 0.9375 : 1) - }}> - - {fontScale ? ( - Enabled - ) : ( - Disabled - )} - - - - - - )} diff --git a/src/storage/schema.ts b/src/storage/schema.ts index 1bffe646bb..1a9656fede 100644 --- a/src/storage/schema.ts +++ b/src/storage/schema.ts @@ -2,7 +2,7 @@ * Device data that's specific to the device and does not vary based account */ export type Device = { - fontScale: number + fontScale: '-2' | '-1' | '0' | '1' | '2' fontFamily: 'system' | 'theme' lastNuxDialog: string | undefined } diff --git a/src/view/com/util/text/Text.tsx b/src/view/com/util/text/Text.tsx index 2a6a4c6e85..c06a7f7700 100644 --- a/src/view/com/util/text/Text.tsx +++ b/src/view/com/util/text/Text.tsx @@ -51,7 +51,7 @@ export function Text({ // @ts-ignore if (flattened.fontSize) { // @ts-ignore - flattened.fontSize = flattened.fontSize * fonts.scale + flattened.fontSize = flattened.fontSize * fonts.scaleMultiplier } return ( @@ -81,7 +81,7 @@ export function Text({ // @ts-ignore if (flattened.fontSize) { // @ts-ignore - flattened.fontSize = flattened.fontSize * fonts.scale + flattened.fontSize = flattened.fontSize * fonts.scaleMultiplier } return (