[Neue] Base (#5395)
* Add fontScale, gate it, fix some computes * Add inter, integrate * Clean up * Apply to old Text component * Use numeric weight * Cleanup * Clean up appearance settings * Global tracking * Fix regular italic variant * Refactor settings and fontScale values * Remove flags * Get rid of lower weight font usage * Remove gate from settings * Refactor appearance settings for reuse * Add neue type nux * Update defaults * Load fonts, add fallback families * Load fonts via plugin in production * Fixes * Fix for web * Nits --------- Co-authored-by: Hailey <me@haileyok.com>
This commit is contained in:
parent
fb3be79820
commit
cbc7cd0808
45 changed files with 835 additions and 256 deletions
|
@ -230,6 +230,31 @@ module.exports = function (config) {
|
||||||
'./plugins/shareExtension/withShareExtensions.js',
|
'./plugins/shareExtension/withShareExtensions.js',
|
||||||
'./plugins/notificationsExtension/withNotificationsExtension.js',
|
'./plugins/notificationsExtension/withNotificationsExtension.js',
|
||||||
'./plugins/withAppDelegateReferrer.js',
|
'./plugins/withAppDelegateReferrer.js',
|
||||||
|
[
|
||||||
|
'expo-font',
|
||||||
|
{
|
||||||
|
fonts: [
|
||||||
|
// './assets/fonts/inter/Inter-Thin.otf',
|
||||||
|
// './assets/fonts/inter/Inter-ThinItalic.otf',
|
||||||
|
// './assets/fonts/inter/Inter-ExtraLight.otf',
|
||||||
|
// './assets/fonts/inter/Inter-ExtraLightItalic.otf',
|
||||||
|
// './assets/fonts/inter/Inter-Light.otf',
|
||||||
|
// './assets/fonts/inter/Inter-LightItalic.otf',
|
||||||
|
'./assets/fonts/inter/Inter-Regular.otf',
|
||||||
|
'./assets/fonts/inter/Inter-Italic.otf',
|
||||||
|
'./assets/fonts/inter/Inter-Medium.otf',
|
||||||
|
'./assets/fonts/inter/Inter-MediumItalic.otf',
|
||||||
|
'./assets/fonts/inter/Inter-SemiBold.otf',
|
||||||
|
'./assets/fonts/inter/Inter-SemiBoldItalic.otf',
|
||||||
|
'./assets/fonts/inter/Inter-Bold.otf',
|
||||||
|
'./assets/fonts/inter/Inter-BoldItalic.otf',
|
||||||
|
'./assets/fonts/inter/Inter-ExtraBold.otf',
|
||||||
|
'./assets/fonts/inter/Inter-ExtraBoldItalic.otf',
|
||||||
|
'./assets/fonts/inter/Inter-Black.otf',
|
||||||
|
'./assets/fonts/inter/Inter-BlackItalic.otf',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
].filter(Boolean),
|
].filter(Boolean),
|
||||||
extra: {
|
extra: {
|
||||||
eas: {
|
eas: {
|
||||||
|
|
BIN
assets/fonts/inter/Inter-Black.otf
Normal file
BIN
assets/fonts/inter/Inter-Black.otf
Normal file
Binary file not shown.
BIN
assets/fonts/inter/Inter-BlackItalic.otf
Normal file
BIN
assets/fonts/inter/Inter-BlackItalic.otf
Normal file
Binary file not shown.
BIN
assets/fonts/inter/Inter-Bold.otf
Normal file
BIN
assets/fonts/inter/Inter-Bold.otf
Normal file
Binary file not shown.
BIN
assets/fonts/inter/Inter-BoldItalic.otf
Normal file
BIN
assets/fonts/inter/Inter-BoldItalic.otf
Normal file
Binary file not shown.
BIN
assets/fonts/inter/Inter-ExtraBold.otf
Normal file
BIN
assets/fonts/inter/Inter-ExtraBold.otf
Normal file
Binary file not shown.
BIN
assets/fonts/inter/Inter-ExtraBoldItalic.otf
Normal file
BIN
assets/fonts/inter/Inter-ExtraBoldItalic.otf
Normal file
Binary file not shown.
BIN
assets/fonts/inter/Inter-ExtraLight.otf
Normal file
BIN
assets/fonts/inter/Inter-ExtraLight.otf
Normal file
Binary file not shown.
BIN
assets/fonts/inter/Inter-ExtraLightItalic.otf
Normal file
BIN
assets/fonts/inter/Inter-ExtraLightItalic.otf
Normal file
Binary file not shown.
BIN
assets/fonts/inter/Inter-Italic.otf
Normal file
BIN
assets/fonts/inter/Inter-Italic.otf
Normal file
Binary file not shown.
BIN
assets/fonts/inter/Inter-Light.otf
Normal file
BIN
assets/fonts/inter/Inter-Light.otf
Normal file
Binary file not shown.
BIN
assets/fonts/inter/Inter-LightItalic.otf
Normal file
BIN
assets/fonts/inter/Inter-LightItalic.otf
Normal file
Binary file not shown.
BIN
assets/fonts/inter/Inter-Medium.otf
Normal file
BIN
assets/fonts/inter/Inter-Medium.otf
Normal file
Binary file not shown.
BIN
assets/fonts/inter/Inter-MediumItalic.otf
Normal file
BIN
assets/fonts/inter/Inter-MediumItalic.otf
Normal file
Binary file not shown.
BIN
assets/fonts/inter/Inter-Regular.otf
Normal file
BIN
assets/fonts/inter/Inter-Regular.otf
Normal file
Binary file not shown.
BIN
assets/fonts/inter/Inter-SemiBold.otf
Normal file
BIN
assets/fonts/inter/Inter-SemiBold.otf
Normal file
Binary file not shown.
BIN
assets/fonts/inter/Inter-SemiBoldItalic.otf
Normal file
BIN
assets/fonts/inter/Inter-SemiBoldItalic.otf
Normal file
Binary file not shown.
BIN
assets/fonts/inter/Inter-Thin.otf
Normal file
BIN
assets/fonts/inter/Inter-Thin.otf
Normal file
Binary file not shown.
BIN
assets/fonts/inter/Inter-ThinItalic.otf
Normal file
BIN
assets/fonts/inter/Inter-ThinItalic.otf
Normal file
Binary file not shown.
1
assets/icons/textSize_stroke2_corner0_rounded.svg
Normal file
1
assets/icons/textSize_stroke2_corner0_rounded.svg
Normal file
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"><path fill="#000" fill-rule="evenodd" d="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" clip-rule="evenodd"/></svg>
|
After Width: | Height: | Size: 317 B |
1
assets/icons/titleCase_stroke2_corner0_rounded.svg
Normal file
1
assets/icons/titleCase_stroke2_corner0_rounded.svg
Normal file
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"><path fill="#000" fill-rule="evenodd" d="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" clip-rule="evenodd"/></svg>
|
After Width: | Height: | Size: 986 B |
|
@ -124,6 +124,7 @@
|
||||||
"expo-dev-client": "^4.0.14",
|
"expo-dev-client": "^4.0.14",
|
||||||
"expo-device": "~6.0.2",
|
"expo-device": "~6.0.2",
|
||||||
"expo-file-system": "^17.0.1",
|
"expo-file-system": "^17.0.1",
|
||||||
|
"expo-font": "~12.0.10",
|
||||||
"expo-haptics": "^13.0.1",
|
"expo-haptics": "^13.0.1",
|
||||||
"expo-image": "~1.12.9",
|
"expo-image": "~1.12.9",
|
||||||
"expo-image-manipulator": "^12.0.5",
|
"expo-image-manipulator": "^12.0.5",
|
||||||
|
|
|
@ -55,7 +55,7 @@ import {TestCtrls} from '#/view/com/testing/TestCtrls'
|
||||||
import {Provider as VideoVolumeProvider} from '#/view/com/util/post-embeds/VideoVolumeContext'
|
import {Provider as VideoVolumeProvider} from '#/view/com/util/post-embeds/VideoVolumeContext'
|
||||||
import * as Toast from '#/view/com/util/Toast'
|
import * as Toast from '#/view/com/util/Toast'
|
||||||
import {Shell} from '#/view/shell'
|
import {Shell} from '#/view/shell'
|
||||||
import {ThemeProvider as Alf} from '#/alf'
|
import {ThemeProvider as Alf, useFonts} from '#/alf'
|
||||||
import {useColorModeTheme} from '#/alf/util/useColorModeTheme'
|
import {useColorModeTheme} from '#/alf/util/useColorModeTheme'
|
||||||
import {NuxDialogs} from '#/components/dialogs/nuxs'
|
import {NuxDialogs} from '#/components/dialogs/nuxs'
|
||||||
import {useStarterPackEntry} from '#/components/hooks/useStarterPackEntry'
|
import {useStarterPackEntry} from '#/components/hooks/useStarterPackEntry'
|
||||||
|
@ -106,62 +106,60 @@ function InnerApp() {
|
||||||
}, [_])
|
}, [_])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Alf theme={theme}>
|
<StatsigProvider
|
||||||
<ThemeProvider theme={theme}>
|
// Resets the entire tree below when it changes:
|
||||||
<Splash isReady={isReady && hasCheckedReferrer}>
|
key={currentAccount?.did}>
|
||||||
<RootSiblingParent>
|
<Alf theme={theme}>
|
||||||
<VideoVolumeProvider>
|
<ThemeProvider theme={theme}>
|
||||||
<React.Fragment
|
<Splash isReady={isReady && hasCheckedReferrer}>
|
||||||
// Resets the entire tree below when it changes:
|
<RootSiblingParent>
|
||||||
key={currentAccount?.did}>
|
<VideoVolumeProvider>
|
||||||
<QueryProvider currentDid={currentAccount?.did}>
|
<QueryProvider currentDid={currentAccount?.did}>
|
||||||
<StatsigProvider>
|
<MessagesProvider>
|
||||||
<MessagesProvider>
|
{/* LabelDefsProvider MUST come before ModerationOptsProvider */}
|
||||||
{/* LabelDefsProvider MUST come before ModerationOptsProvider */}
|
<LabelDefsProvider>
|
||||||
<LabelDefsProvider>
|
<ModerationOptsProvider>
|
||||||
<ModerationOptsProvider>
|
<LoggedOutViewProvider>
|
||||||
<LoggedOutViewProvider>
|
<SelectedFeedProvider>
|
||||||
<SelectedFeedProvider>
|
<HiddenRepliesProvider>
|
||||||
<HiddenRepliesProvider>
|
<UnreadNotifsProvider>
|
||||||
<UnreadNotifsProvider>
|
<BackgroundNotificationPreferencesProvider>
|
||||||
<BackgroundNotificationPreferencesProvider>
|
<MutedThreadsProvider>
|
||||||
<MutedThreadsProvider>
|
<ProgressGuideProvider>
|
||||||
<ProgressGuideProvider>
|
<GestureHandlerRootView style={s.h100pct}>
|
||||||
<GestureHandlerRootView
|
<TestCtrls />
|
||||||
style={s.h100pct}>
|
<Shell />
|
||||||
<TestCtrls />
|
<NuxDialogs />
|
||||||
<Shell />
|
</GestureHandlerRootView>
|
||||||
<NuxDialogs />
|
</ProgressGuideProvider>
|
||||||
</GestureHandlerRootView>
|
</MutedThreadsProvider>
|
||||||
</ProgressGuideProvider>
|
</BackgroundNotificationPreferencesProvider>
|
||||||
</MutedThreadsProvider>
|
</UnreadNotifsProvider>
|
||||||
</BackgroundNotificationPreferencesProvider>
|
</HiddenRepliesProvider>
|
||||||
</UnreadNotifsProvider>
|
</SelectedFeedProvider>
|
||||||
</HiddenRepliesProvider>
|
</LoggedOutViewProvider>
|
||||||
</SelectedFeedProvider>
|
</ModerationOptsProvider>
|
||||||
</LoggedOutViewProvider>
|
</LabelDefsProvider>
|
||||||
</ModerationOptsProvider>
|
</MessagesProvider>
|
||||||
</LabelDefsProvider>
|
|
||||||
</MessagesProvider>
|
|
||||||
</StatsigProvider>
|
|
||||||
</QueryProvider>
|
</QueryProvider>
|
||||||
</React.Fragment>
|
</VideoVolumeProvider>
|
||||||
</VideoVolumeProvider>
|
</RootSiblingParent>
|
||||||
</RootSiblingParent>
|
</Splash>
|
||||||
</Splash>
|
</ThemeProvider>
|
||||||
</ThemeProvider>
|
</Alf>
|
||||||
</Alf>
|
</StatsigProvider>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
const [isReady, setReady] = useState(false)
|
const [isReady, setReady] = useState(false)
|
||||||
|
const [loaded] = useFonts()
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
initPersistedState().then(() => setReady(true))
|
initPersistedState().then(() => setReady(true))
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
if (!isReady) {
|
if (!isReady || !loaded) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -46,7 +46,7 @@ import {Provider as VideoVolumeProvider} from '#/view/com/util/post-embeds/Video
|
||||||
import * as Toast from '#/view/com/util/Toast'
|
import * as Toast from '#/view/com/util/Toast'
|
||||||
import {ToastContainer} from '#/view/com/util/Toast.web'
|
import {ToastContainer} from '#/view/com/util/Toast.web'
|
||||||
import {Shell} from '#/view/shell/index'
|
import {Shell} from '#/view/shell/index'
|
||||||
import {ThemeProvider as Alf} from '#/alf'
|
import {ThemeProvider as Alf, useFonts} from '#/alf'
|
||||||
import {useColorModeTheme} from '#/alf/util/useColorModeTheme'
|
import {useColorModeTheme} from '#/alf/util/useColorModeTheme'
|
||||||
import {NuxDialogs} from '#/components/dialogs/nuxs'
|
import {NuxDialogs} from '#/components/dialogs/nuxs'
|
||||||
import {useStarterPackEntry} from '#/components/hooks/useStarterPackEntry'
|
import {useStarterPackEntry} from '#/components/hooks/useStarterPackEntry'
|
||||||
|
@ -96,62 +96,61 @@ function InnerApp() {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<KeyboardProvider enabled={false}>
|
<KeyboardProvider enabled={false}>
|
||||||
<Alf theme={theme}>
|
<StatsigProvider
|
||||||
<ThemeProvider theme={theme}>
|
// Resets the entire tree below when it changes:
|
||||||
<RootSiblingParent>
|
key={currentAccount?.did}>
|
||||||
<VideoVolumeProvider>
|
<Alf theme={theme}>
|
||||||
<ActiveVideoProvider>
|
<ThemeProvider theme={theme}>
|
||||||
<React.Fragment
|
<RootSiblingParent>
|
||||||
// Resets the entire tree below when it changes:
|
<VideoVolumeProvider>
|
||||||
key={currentAccount?.did}>
|
<ActiveVideoProvider>
|
||||||
<QueryProvider currentDid={currentAccount?.did}>
|
<QueryProvider currentDid={currentAccount?.did}>
|
||||||
<StatsigProvider>
|
<MessagesProvider>
|
||||||
<MessagesProvider>
|
{/* LabelDefsProvider MUST come before ModerationOptsProvider */}
|
||||||
{/* LabelDefsProvider MUST come before ModerationOptsProvider */}
|
<LabelDefsProvider>
|
||||||
<LabelDefsProvider>
|
<ModerationOptsProvider>
|
||||||
<ModerationOptsProvider>
|
<LoggedOutViewProvider>
|
||||||
<LoggedOutViewProvider>
|
<SelectedFeedProvider>
|
||||||
<SelectedFeedProvider>
|
<HiddenRepliesProvider>
|
||||||
<HiddenRepliesProvider>
|
<UnreadNotifsProvider>
|
||||||
<UnreadNotifsProvider>
|
<BackgroundNotificationPreferencesProvider>
|
||||||
<BackgroundNotificationPreferencesProvider>
|
<MutedThreadsProvider>
|
||||||
<MutedThreadsProvider>
|
<SafeAreaProvider>
|
||||||
<SafeAreaProvider>
|
<ProgressGuideProvider>
|
||||||
<ProgressGuideProvider>
|
<Shell />
|
||||||
<Shell />
|
<NuxDialogs />
|
||||||
<NuxDialogs />
|
</ProgressGuideProvider>
|
||||||
</ProgressGuideProvider>
|
</SafeAreaProvider>
|
||||||
</SafeAreaProvider>
|
</MutedThreadsProvider>
|
||||||
</MutedThreadsProvider>
|
</BackgroundNotificationPreferencesProvider>
|
||||||
</BackgroundNotificationPreferencesProvider>
|
</UnreadNotifsProvider>
|
||||||
</UnreadNotifsProvider>
|
</HiddenRepliesProvider>
|
||||||
</HiddenRepliesProvider>
|
</SelectedFeedProvider>
|
||||||
</SelectedFeedProvider>
|
</LoggedOutViewProvider>
|
||||||
</LoggedOutViewProvider>
|
</ModerationOptsProvider>
|
||||||
</ModerationOptsProvider>
|
</LabelDefsProvider>
|
||||||
</LabelDefsProvider>
|
</MessagesProvider>
|
||||||
</MessagesProvider>
|
|
||||||
</StatsigProvider>
|
|
||||||
</QueryProvider>
|
</QueryProvider>
|
||||||
</React.Fragment>
|
<ToastContainer />
|
||||||
<ToastContainer />
|
</ActiveVideoProvider>
|
||||||
</ActiveVideoProvider>
|
</VideoVolumeProvider>
|
||||||
</VideoVolumeProvider>
|
</RootSiblingParent>
|
||||||
</RootSiblingParent>
|
</ThemeProvider>
|
||||||
</ThemeProvider>
|
</Alf>
|
||||||
</Alf>
|
</StatsigProvider>
|
||||||
</KeyboardProvider>
|
</KeyboardProvider>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
const [isReady, setReady] = useState(false)
|
const [isReady, setReady] = useState(false)
|
||||||
|
const [loaded] = useFonts()
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
initPersistedState().then(() => setReady(true))
|
initPersistedState().then(() => setReady(true))
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
if (!isReady) {
|
if (!isReady || !loaded) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -225,43 +225,43 @@ export const atoms = {
|
||||||
},
|
},
|
||||||
text_2xs: {
|
text_2xs: {
|
||||||
fontSize: tokens.fontSize._2xs,
|
fontSize: tokens.fontSize._2xs,
|
||||||
letterSpacing: 0.25,
|
letterSpacing: tokens.TRACKING,
|
||||||
},
|
},
|
||||||
text_xs: {
|
text_xs: {
|
||||||
fontSize: tokens.fontSize.xs,
|
fontSize: tokens.fontSize.xs,
|
||||||
letterSpacing: 0.25,
|
letterSpacing: tokens.TRACKING,
|
||||||
},
|
},
|
||||||
text_sm: {
|
text_sm: {
|
||||||
fontSize: tokens.fontSize.sm,
|
fontSize: tokens.fontSize.sm,
|
||||||
letterSpacing: 0.25,
|
letterSpacing: tokens.TRACKING,
|
||||||
},
|
},
|
||||||
text_md: {
|
text_md: {
|
||||||
fontSize: tokens.fontSize.md,
|
fontSize: tokens.fontSize.md,
|
||||||
letterSpacing: 0.25,
|
letterSpacing: tokens.TRACKING,
|
||||||
},
|
},
|
||||||
text_lg: {
|
text_lg: {
|
||||||
fontSize: tokens.fontSize.lg,
|
fontSize: tokens.fontSize.lg,
|
||||||
letterSpacing: 0.25,
|
letterSpacing: tokens.TRACKING,
|
||||||
},
|
},
|
||||||
text_xl: {
|
text_xl: {
|
||||||
fontSize: tokens.fontSize.xl,
|
fontSize: tokens.fontSize.xl,
|
||||||
letterSpacing: 0.25,
|
letterSpacing: tokens.TRACKING,
|
||||||
},
|
},
|
||||||
text_2xl: {
|
text_2xl: {
|
||||||
fontSize: tokens.fontSize._2xl,
|
fontSize: tokens.fontSize._2xl,
|
||||||
letterSpacing: 0.25,
|
letterSpacing: tokens.TRACKING,
|
||||||
},
|
},
|
||||||
text_3xl: {
|
text_3xl: {
|
||||||
fontSize: tokens.fontSize._3xl,
|
fontSize: tokens.fontSize._3xl,
|
||||||
letterSpacing: 0.25,
|
letterSpacing: tokens.TRACKING,
|
||||||
},
|
},
|
||||||
text_4xl: {
|
text_4xl: {
|
||||||
fontSize: tokens.fontSize._4xl,
|
fontSize: tokens.fontSize._4xl,
|
||||||
letterSpacing: 0.25,
|
letterSpacing: tokens.TRACKING,
|
||||||
},
|
},
|
||||||
text_5xl: {
|
text_5xl: {
|
||||||
fontSize: tokens.fontSize._5xl,
|
fontSize: tokens.fontSize._5xl,
|
||||||
letterSpacing: 0.25,
|
letterSpacing: tokens.TRACKING,
|
||||||
},
|
},
|
||||||
leading_tight: {
|
leading_tight: {
|
||||||
lineHeight: 1.15,
|
lineHeight: 1.15,
|
||||||
|
@ -273,10 +273,7 @@ export const atoms = {
|
||||||
lineHeight: 1.5,
|
lineHeight: 1.5,
|
||||||
},
|
},
|
||||||
tracking_normal: {
|
tracking_normal: {
|
||||||
letterSpacing: 0,
|
letterSpacing: tokens.TRACKING,
|
||||||
},
|
|
||||||
tracking_wide: {
|
|
||||||
letterSpacing: 0.25,
|
|
||||||
},
|
},
|
||||||
font_normal: {
|
font_normal: {
|
||||||
fontWeight: tokens.fontWeight.normal,
|
fontWeight: tokens.fontWeight.normal,
|
||||||
|
|
111
src/alf/fonts.ts
Normal file
111
src/alf/fonts.ts
Normal file
|
@ -0,0 +1,111 @@
|
||||||
|
import {useFonts as defaultUseFonts} from 'expo-font'
|
||||||
|
|
||||||
|
import {isNative, isWeb} from '#/platform/detection'
|
||||||
|
import {Device, device} from '#/storage'
|
||||||
|
|
||||||
|
const FAMILIES = `-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Liberation Sans", Helvetica, Arial, sans-serif`
|
||||||
|
|
||||||
|
const factor = 0.0625 // 1 - (15/16)
|
||||||
|
const fontScaleMultipliers: Record<Device['fontScale'], number> = {
|
||||||
|
'-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']) ?? '0'
|
||||||
|
}
|
||||||
|
|
||||||
|
export function setFontScale(fontScale: Device['fontScale']) {
|
||||||
|
device.set(['fontScale'], fontScale)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getFontFamily() {
|
||||||
|
return device.get(['fontFamily']) || 'theme'
|
||||||
|
}
|
||||||
|
|
||||||
|
export function setFontFamily(fontFamily: Device['fontFamily']) {
|
||||||
|
device.set(['fontFamily'], fontFamily)
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Unused fonts are commented out, but the files are there if we need them.
|
||||||
|
*/
|
||||||
|
export function useFonts() {
|
||||||
|
/**
|
||||||
|
* For native, the `expo-font` config plugin embeds the fonts in the
|
||||||
|
* application binary. But `expo-font` isn't supported on web, so we fall
|
||||||
|
* back to async loading here.
|
||||||
|
*/
|
||||||
|
if (isNative) return [true, null]
|
||||||
|
return defaultUseFonts({
|
||||||
|
// 'Inter-Thin': require('../../assets/fonts/inter/Inter-Thin.otf'),
|
||||||
|
// 'Inter-ThinItalic': require('../../assets/fonts/inter/Inter-ThinItalic.otf'),
|
||||||
|
// 'Inter-ExtraLight': require('../../assets/fonts/inter/Inter-ExtraLight.otf'),
|
||||||
|
// 'Inter-ExtraLightItalic': require('../../assets/fonts/inter/Inter-ExtraLightItalic.otf'),
|
||||||
|
// 'Inter-Light': require('../../assets/fonts/inter/Inter-Light.otf'),
|
||||||
|
// 'Inter-LightItalic': require('../../assets/fonts/inter/Inter-LightItalic.otf'),
|
||||||
|
'Inter-Regular': require('../../assets/fonts/inter/Inter-Regular.otf'),
|
||||||
|
'Inter-Italic': require('../../assets/fonts/inter/Inter-Italic.otf'),
|
||||||
|
'Inter-Medium': require('../../assets/fonts/inter/Inter-Medium.otf'),
|
||||||
|
'Inter-MediumItalic': require('../../assets/fonts/inter/Inter-MediumItalic.otf'),
|
||||||
|
'Inter-SemiBold': require('../../assets/fonts/inter/Inter-SemiBold.otf'),
|
||||||
|
'Inter-SemiBoldItalic': require('../../assets/fonts/inter/Inter-SemiBoldItalic.otf'),
|
||||||
|
'Inter-Bold': require('../../assets/fonts/inter/Inter-Bold.otf'),
|
||||||
|
'Inter-BoldItalic': require('../../assets/fonts/inter/Inter-BoldItalic.otf'),
|
||||||
|
'Inter-ExtraBold': require('../../assets/fonts/inter/Inter-ExtraBold.otf'),
|
||||||
|
'Inter-ExtraBoldItalic': require('../../assets/fonts/inter/Inter-ExtraBoldItalic.otf'),
|
||||||
|
'Inter-Black': require('../../assets/fonts/inter/Inter-Black.otf'),
|
||||||
|
'Inter-BlackItalic': require('../../assets/fonts/inter/Inter-BlackItalic.otf'),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Unused fonts are commented out, but the files are there if we need them.
|
||||||
|
*/
|
||||||
|
export function applyFonts(
|
||||||
|
style: Record<string, any>,
|
||||||
|
fontFamily: 'system' | 'theme',
|
||||||
|
) {
|
||||||
|
if (fontFamily === 'theme') {
|
||||||
|
style.fontFamily =
|
||||||
|
{
|
||||||
|
// '100': 'Inter-Thin',
|
||||||
|
// '200': 'Inter-ExtraLight',
|
||||||
|
// '300': 'Inter-Light',
|
||||||
|
'100': 'Inter-Regular',
|
||||||
|
'200': 'Inter-Regular',
|
||||||
|
'300': 'Inter-Regular',
|
||||||
|
'400': 'Inter-Regular',
|
||||||
|
'500': 'Inter-Medium',
|
||||||
|
'600': 'Inter-SemiBold',
|
||||||
|
'700': 'Inter-Bold',
|
||||||
|
'800': 'Inter-ExtraBold',
|
||||||
|
'900': 'Inter-Black',
|
||||||
|
}[style.fontWeight as string] || 'Inter-Regular'
|
||||||
|
|
||||||
|
if (style.fontStyle === 'italic') {
|
||||||
|
if (style.fontFamily === 'Inter-Regular') {
|
||||||
|
style.fontFamily = 'Inter-Italic'
|
||||||
|
} else {
|
||||||
|
style.fontFamily += 'Italic'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// fallback families only supported on web
|
||||||
|
if (isWeb) {
|
||||||
|
style.fontFamily += `, ${FAMILIES}`
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// fallback families only supported on web
|
||||||
|
if (isWeb) {
|
||||||
|
style.fontFamily = style.fontFamily || FAMILIES
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,25 +1,47 @@
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import {useMediaQuery} from 'react-responsive'
|
import {useMediaQuery} from 'react-responsive'
|
||||||
|
|
||||||
|
import {
|
||||||
|
computeFontScaleMultiplier,
|
||||||
|
getFontFamily,
|
||||||
|
getFontScale,
|
||||||
|
setFontFamily as persistFontFamily,
|
||||||
|
setFontScale as persistFontScale,
|
||||||
|
} from '#/alf/fonts'
|
||||||
import {createThemes, defaultTheme} from '#/alf/themes'
|
import {createThemes, defaultTheme} from '#/alf/themes'
|
||||||
import {Theme, ThemeName} from '#/alf/types'
|
import {Theme, ThemeName} from '#/alf/types'
|
||||||
import {BLUE_HUE, GREEN_HUE, RED_HUE} from '#/alf/util/colorGeneration'
|
import {BLUE_HUE, GREEN_HUE, RED_HUE} from '#/alf/util/colorGeneration'
|
||||||
|
import {Device} from '#/storage'
|
||||||
|
|
||||||
export {atoms} from '#/alf/atoms'
|
export {atoms} from '#/alf/atoms'
|
||||||
|
export * from '#/alf/fonts'
|
||||||
export * as tokens from '#/alf/tokens'
|
export * as tokens from '#/alf/tokens'
|
||||||
export * from '#/alf/types'
|
export * from '#/alf/types'
|
||||||
export * from '#/alf/util/flatten'
|
export * from '#/alf/util/flatten'
|
||||||
export * from '#/alf/util/platform'
|
export * from '#/alf/util/platform'
|
||||||
export * from '#/alf/util/themeSelector'
|
export * from '#/alf/util/themeSelector'
|
||||||
|
|
||||||
/*
|
export type Alf = {
|
||||||
* Context
|
|
||||||
*/
|
|
||||||
export const Context = React.createContext<{
|
|
||||||
themeName: ThemeName
|
themeName: ThemeName
|
||||||
theme: Theme
|
theme: Theme
|
||||||
themes: ReturnType<typeof createThemes>
|
themes: ReturnType<typeof createThemes>
|
||||||
}>({
|
fonts: {
|
||||||
|
scale: Exclude<Device['fontScale'], undefined>
|
||||||
|
scaleMultiplier: number
|
||||||
|
family: Device['fontFamily']
|
||||||
|
setFontScale: (fontScale: Exclude<Device['fontScale'], undefined>) => void
|
||||||
|
setFontFamily: (fontFamily: Device['fontFamily']) => void
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Feature flags or other gated options
|
||||||
|
*/
|
||||||
|
flags: {}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Context
|
||||||
|
*/
|
||||||
|
export const Context = React.createContext<Alf>({
|
||||||
themeName: 'light',
|
themeName: 'light',
|
||||||
theme: defaultTheme,
|
theme: defaultTheme,
|
||||||
themes: createThemes({
|
themes: createThemes({
|
||||||
|
@ -29,12 +51,48 @@ export const Context = React.createContext<{
|
||||||
positive: GREEN_HUE,
|
positive: GREEN_HUE,
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
|
fonts: {
|
||||||
|
scale: getFontScale(),
|
||||||
|
scaleMultiplier: computeFontScaleMultiplier(getFontScale()),
|
||||||
|
family: getFontFamily(),
|
||||||
|
setFontScale: () => {},
|
||||||
|
setFontFamily: () => {},
|
||||||
|
},
|
||||||
|
flags: {},
|
||||||
})
|
})
|
||||||
|
|
||||||
export function ThemeProvider({
|
export function ThemeProvider({
|
||||||
children,
|
children,
|
||||||
theme: themeName,
|
theme: themeName,
|
||||||
}: React.PropsWithChildren<{theme: ThemeName}>) {
|
}: React.PropsWithChildren<{theme: ThemeName}>) {
|
||||||
|
const [fontScale, setFontScale] = React.useState<Alf['fonts']['scale']>(() =>
|
||||||
|
getFontScale(),
|
||||||
|
)
|
||||||
|
const [fontScaleMultiplier, setFontScaleMultiplier] = React.useState(() =>
|
||||||
|
computeFontScaleMultiplier(fontScale),
|
||||||
|
)
|
||||||
|
const setFontScaleAndPersist = React.useCallback<
|
||||||
|
Alf['fonts']['setFontScale']
|
||||||
|
>(
|
||||||
|
fontScale => {
|
||||||
|
setFontScale(fontScale)
|
||||||
|
persistFontScale(fontScale)
|
||||||
|
setFontScaleMultiplier(computeFontScaleMultiplier(fontScale))
|
||||||
|
},
|
||||||
|
[setFontScale],
|
||||||
|
)
|
||||||
|
const [fontFamily, setFontFamily] = React.useState<Alf['fonts']['family']>(
|
||||||
|
() => getFontFamily(),
|
||||||
|
)
|
||||||
|
const setFontFamilyAndPersist = React.useCallback<
|
||||||
|
Alf['fonts']['setFontFamily']
|
||||||
|
>(
|
||||||
|
fontFamily => {
|
||||||
|
setFontFamily(fontFamily)
|
||||||
|
persistFontFamily(fontFamily)
|
||||||
|
},
|
||||||
|
[setFontFamily],
|
||||||
|
)
|
||||||
const themes = React.useMemo(() => {
|
const themes = React.useMemo(() => {
|
||||||
return createThemes({
|
return createThemes({
|
||||||
hues: {
|
hues: {
|
||||||
|
@ -44,28 +102,47 @@ export function ThemeProvider({
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
}, [])
|
}, [])
|
||||||
const theme = themes[themeName]
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Context.Provider
|
<Context.Provider
|
||||||
value={React.useMemo(
|
value={React.useMemo<Alf>(
|
||||||
() => ({
|
() => ({
|
||||||
themes,
|
themes,
|
||||||
themeName: themeName,
|
themeName: themeName,
|
||||||
theme: theme,
|
theme: themes[themeName],
|
||||||
|
fonts: {
|
||||||
|
scale: fontScale,
|
||||||
|
scaleMultiplier: fontScaleMultiplier,
|
||||||
|
family: fontFamily,
|
||||||
|
setFontScale: setFontScaleAndPersist,
|
||||||
|
setFontFamily: setFontFamilyAndPersist,
|
||||||
|
},
|
||||||
|
flags: {},
|
||||||
}),
|
}),
|
||||||
[theme, themeName, themes],
|
[
|
||||||
|
themeName,
|
||||||
|
themes,
|
||||||
|
fontScale,
|
||||||
|
setFontScaleAndPersist,
|
||||||
|
fontFamily,
|
||||||
|
setFontFamilyAndPersist,
|
||||||
|
fontScaleMultiplier,
|
||||||
|
],
|
||||||
)}>
|
)}>
|
||||||
{children}
|
{children}
|
||||||
</Context.Provider>
|
</Context.Provider>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function useAlf() {
|
||||||
|
return React.useContext(Context)
|
||||||
|
}
|
||||||
|
|
||||||
export function useTheme(theme?: ThemeName) {
|
export function useTheme(theme?: ThemeName) {
|
||||||
const ctx = React.useContext(Context)
|
const alf = useAlf()
|
||||||
return React.useMemo(() => {
|
return React.useMemo(() => {
|
||||||
return theme ? ctx.themes[theme] : ctx.theme
|
return theme ? alf.themes[theme] : alf.theme
|
||||||
}, [theme, ctx])
|
}, [theme, alf])
|
||||||
}
|
}
|
||||||
|
|
||||||
export function useBreakpoints() {
|
export function useBreakpoints() {
|
||||||
|
|
|
@ -1,3 +1,7 @@
|
||||||
|
import {Platform} from 'react-native'
|
||||||
|
|
||||||
|
export const TRACKING = Platform.OS === 'android' ? 0.1 : 0
|
||||||
|
|
||||||
export const color = {
|
export const color = {
|
||||||
temp_purple: 'rgb(105 0 255)',
|
temp_purple: 'rgb(105 0 255)',
|
||||||
temp_purple_dark: 'rgb(83 0 202)',
|
temp_purple_dark: 'rgb(83 0 202)',
|
||||||
|
|
|
@ -7,7 +7,6 @@ import {
|
||||||
PressableProps,
|
PressableProps,
|
||||||
StyleProp,
|
StyleProp,
|
||||||
StyleSheet,
|
StyleSheet,
|
||||||
Text,
|
|
||||||
TextProps,
|
TextProps,
|
||||||
TextStyle,
|
TextStyle,
|
||||||
View,
|
View,
|
||||||
|
@ -17,7 +16,7 @@ import {LinearGradient} from 'expo-linear-gradient'
|
||||||
|
|
||||||
import {android, atoms as a, flatten, select, tokens, useTheme} from '#/alf'
|
import {android, atoms as a, flatten, select, tokens, useTheme} from '#/alf'
|
||||||
import {Props as SVGIconProps} from '#/components/icons/common'
|
import {Props as SVGIconProps} from '#/components/icons/common'
|
||||||
import {normalizeTextStyles} from '#/components/Typography'
|
import {Text} from '#/components/Typography'
|
||||||
|
|
||||||
export type ButtonVariant = 'solid' | 'outline' | 'ghost' | 'gradient'
|
export type ButtonVariant = 'solid' | 'outline' | 'ghost' | 'gradient'
|
||||||
export type ButtonColor =
|
export type ButtonColor =
|
||||||
|
@ -635,14 +634,7 @@ export function ButtonText({children, style, ...rest}: ButtonTextProps) {
|
||||||
const textStyles = useSharedButtonTextStyles()
|
const textStyles = useSharedButtonTextStyles()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Text
|
<Text {...rest} style={[a.font_bold, a.text_center, textStyles, style]}>
|
||||||
{...rest}
|
|
||||||
style={normalizeTextStyles([
|
|
||||||
a.font_bold,
|
|
||||||
a.text_center,
|
|
||||||
textStyles,
|
|
||||||
style,
|
|
||||||
])}>
|
|
||||||
{children}
|
{children}
|
||||||
</Text>
|
</Text>
|
||||||
)
|
)
|
||||||
|
|
|
@ -37,6 +37,7 @@ import {Portal} from '#/components/Portal'
|
||||||
|
|
||||||
export {useDialogContext, useDialogControl} from '#/components/Dialog/context'
|
export {useDialogContext, useDialogControl} from '#/components/Dialog/context'
|
||||||
export * from '#/components/Dialog/types'
|
export * from '#/components/Dialog/types'
|
||||||
|
export * from '#/components/Dialog/utils'
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
export const Input = createInput(BottomSheetTextInput)
|
export const Input = createInput(BottomSheetTextInput)
|
||||||
|
|
||||||
|
|
|
@ -27,6 +27,7 @@ import {Portal} from '#/components/Portal'
|
||||||
|
|
||||||
export {useDialogContext, useDialogControl} from '#/components/Dialog/context'
|
export {useDialogContext, useDialogControl} from '#/components/Dialog/context'
|
||||||
export * from '#/components/Dialog/types'
|
export * from '#/components/Dialog/types'
|
||||||
|
export * from '#/components/Dialog/utils'
|
||||||
export {Input} from '#/components/forms/TextField'
|
export {Input} from '#/components/forms/TextField'
|
||||||
|
|
||||||
const stopPropagation = (e: any) => e.stopPropagation()
|
const stopPropagation = (e: any) => e.stopPropagation()
|
||||||
|
|
18
src/components/Dialog/utils.ts
Normal file
18
src/components/Dialog/utils.ts
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
import React from 'react'
|
||||||
|
|
||||||
|
import {DialogControlProps} from '#/components/Dialog/types'
|
||||||
|
|
||||||
|
export function useAutoOpen(control: DialogControlProps, showTimeout?: number) {
|
||||||
|
React.useEffect(() => {
|
||||||
|
if (showTimeout) {
|
||||||
|
const timeout = setTimeout(() => {
|
||||||
|
control.open()
|
||||||
|
}, showTimeout)
|
||||||
|
return () => {
|
||||||
|
clearTimeout(timeout)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
control.open()
|
||||||
|
}
|
||||||
|
}, [control, showTimeout])
|
||||||
|
}
|
|
@ -3,7 +3,7 @@ import {StyleProp, TextProps as RNTextProps, TextStyle} from 'react-native'
|
||||||
import {UITextView} from 'react-native-uitextview'
|
import {UITextView} from 'react-native-uitextview'
|
||||||
|
|
||||||
import {isNative} from '#/platform/detection'
|
import {isNative} from '#/platform/detection'
|
||||||
import {atoms, flatten, useTheme, web} from '#/alf'
|
import {Alf, applyFonts, atoms, flatten, useAlf, useTheme, web} from '#/alf'
|
||||||
|
|
||||||
export type TextProps = RNTextProps & {
|
export type TextProps = RNTextProps & {
|
||||||
/**
|
/**
|
||||||
|
@ -34,19 +34,30 @@ export function leading<
|
||||||
* If the `lineHeight` value is > 2, we assume it's an absolute value and
|
* If the `lineHeight` value is > 2, we assume it's an absolute value and
|
||||||
* returns it as-is.
|
* returns it as-is.
|
||||||
*/
|
*/
|
||||||
export function normalizeTextStyles(styles: StyleProp<TextStyle>) {
|
export function normalizeTextStyles(
|
||||||
|
styles: StyleProp<TextStyle>,
|
||||||
|
{
|
||||||
|
fontScale,
|
||||||
|
fontFamily,
|
||||||
|
}: {
|
||||||
|
fontScale: number
|
||||||
|
fontFamily: Alf['fonts']['family']
|
||||||
|
} & Pick<Alf, 'flags'>,
|
||||||
|
) {
|
||||||
const s = flatten(styles)
|
const s = flatten(styles)
|
||||||
// should always be defined on these components
|
// should always be defined on these components
|
||||||
const fontSize = s.fontSize || atoms.text_md.fontSize
|
s.fontSize = (s.fontSize || atoms.text_md.fontSize) * fontScale
|
||||||
|
|
||||||
if (s?.lineHeight) {
|
if (s?.lineHeight) {
|
||||||
if (s.lineHeight !== 0 && s.lineHeight <= 2) {
|
if (s.lineHeight !== 0 && s.lineHeight <= 2) {
|
||||||
s.lineHeight = Math.round(fontSize * s.lineHeight)
|
s.lineHeight = Math.round(s.fontSize * s.lineHeight)
|
||||||
}
|
}
|
||||||
} else if (!isNative) {
|
} else if (!isNative) {
|
||||||
s.lineHeight = s.fontSize
|
s.lineHeight = s.fontSize
|
||||||
}
|
}
|
||||||
|
|
||||||
|
applyFonts(s, fontFamily)
|
||||||
|
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -54,8 +65,13 @@ export function normalizeTextStyles(styles: StyleProp<TextStyle>) {
|
||||||
* Our main text component. Use this most of the time.
|
* Our main text component. Use this most of the time.
|
||||||
*/
|
*/
|
||||||
export function Text({style, selectable, ...rest}: TextProps) {
|
export function Text({style, selectable, ...rest}: TextProps) {
|
||||||
|
const {fonts, flags} = useAlf()
|
||||||
const t = useTheme()
|
const t = useTheme()
|
||||||
const s = normalizeTextStyles([atoms.text_sm, t.atoms.text, flatten(style)])
|
const s = normalizeTextStyles([atoms.text_sm, t.atoms.text, flatten(style)], {
|
||||||
|
fontScale: fonts.scaleMultiplier,
|
||||||
|
fontFamily: fonts.family,
|
||||||
|
flags,
|
||||||
|
})
|
||||||
|
|
||||||
return <UITextView selectable={selectable} uiTextView style={s} {...rest} />
|
return <UITextView selectable={selectable} uiTextView style={s} {...rest} />
|
||||||
}
|
}
|
||||||
|
|
119
src/components/dialogs/nuxs/NeueTypography.tsx
Normal file
119
src/components/dialogs/nuxs/NeueTypography.tsx
Normal file
|
@ -0,0 +1,119 @@
|
||||||
|
import React from 'react'
|
||||||
|
import {View} from 'react-native'
|
||||||
|
import {msg, Trans} from '@lingui/macro'
|
||||||
|
import {useLingui} from '@lingui/react'
|
||||||
|
|
||||||
|
import {AppearanceToggleButtonGroup} from '#/screens/Settings/AppearanceSettings'
|
||||||
|
import {atoms as a, useAlf, useTheme} from '#/alf'
|
||||||
|
import * as Dialog from '#/components/Dialog'
|
||||||
|
import {useNuxDialogContext} from '#/components/dialogs/nuxs'
|
||||||
|
import {Divider} from '#/components/Divider'
|
||||||
|
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'
|
||||||
|
|
||||||
|
export function NeueTypography() {
|
||||||
|
const t = useTheme()
|
||||||
|
const {_} = useLingui()
|
||||||
|
const nuxDialogs = useNuxDialogContext()
|
||||||
|
const control = Dialog.useDialogControl()
|
||||||
|
const {fonts} = useAlf()
|
||||||
|
|
||||||
|
Dialog.useAutoOpen(control, 3e3)
|
||||||
|
|
||||||
|
const onClose = React.useCallback(() => {
|
||||||
|
nuxDialogs.dismissActiveNux()
|
||||||
|
}, [nuxDialogs])
|
||||||
|
|
||||||
|
const onChangeFontFamily = React.useCallback(
|
||||||
|
(values: string[]) => {
|
||||||
|
const next = values[0] === 'system' ? 'system' : 'theme'
|
||||||
|
fonts.setFontFamily(next)
|
||||||
|
},
|
||||||
|
[fonts],
|
||||||
|
)
|
||||||
|
|
||||||
|
const onChangeFontScale = React.useCallback(
|
||||||
|
(values: string[]) => {
|
||||||
|
const next = values[0] || ('0' as any)
|
||||||
|
fonts.setFontScale(next)
|
||||||
|
},
|
||||||
|
[fonts],
|
||||||
|
)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Dialog.Outer control={control} onClose={onClose}>
|
||||||
|
<Dialog.Handle />
|
||||||
|
|
||||||
|
<Dialog.ScrollableInner label={_(msg`Introducing new font settings`)}>
|
||||||
|
<View style={[a.gap_xl]}>
|
||||||
|
<View style={[a.gap_md]}>
|
||||||
|
<Text style={[a.text_3xl, {fontWeight: '900'}]}>
|
||||||
|
<Trans>Introducing new font settings ✨</Trans>
|
||||||
|
</Text>
|
||||||
|
<Text style={[a.text_lg, a.leading_snug]}>
|
||||||
|
<Trans>
|
||||||
|
To the ensure the best possible experience, we're introducing a
|
||||||
|
new theme font, along with adjustable font sizing settings.
|
||||||
|
</Trans>
|
||||||
|
</Text>
|
||||||
|
<Text
|
||||||
|
style={[a.text_sm, a.leading_snug, t.atoms.text_contrast_medium]}>
|
||||||
|
<Trans>
|
||||||
|
Defaults are shown below. You can edit these in your Appearance
|
||||||
|
Settings later.
|
||||||
|
</Trans>
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
<Divider />
|
||||||
|
|
||||||
|
<View style={[a.gap_lg]}>
|
||||||
|
<AppearanceToggleButtonGroup
|
||||||
|
title={_(msg`Font`)}
|
||||||
|
description={_(
|
||||||
|
msg`For the best experience, we recommend using the theme font.`,
|
||||||
|
)}
|
||||||
|
icon={Aa}
|
||||||
|
items={[
|
||||||
|
{
|
||||||
|
label: _(msg`System`),
|
||||||
|
name: 'system',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: _(msg`Theme`),
|
||||||
|
name: 'theme',
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
values={[fonts.family]}
|
||||||
|
onChange={onChangeFontFamily}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<AppearanceToggleButtonGroup
|
||||||
|
title={_(msg`Font size`)}
|
||||||
|
icon={TextSize}
|
||||||
|
items={[
|
||||||
|
{
|
||||||
|
label: _(msg`Smaller`),
|
||||||
|
name: '-1',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: _(msg`Default`),
|
||||||
|
name: '0',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: _(msg`Larger`),
|
||||||
|
name: '1',
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
values={[fonts.scale]}
|
||||||
|
onChange={onChangeFontScale}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
<Dialog.Close />
|
||||||
|
</Dialog.ScrollableInner>
|
||||||
|
</Dialog.Outer>
|
||||||
|
)
|
||||||
|
}
|
|
@ -1,4 +1,5 @@
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
|
import {AppBskyActorDefs} from '@atproto/api'
|
||||||
|
|
||||||
import {useGate} from '#/lib/statsig/statsig'
|
import {useGate} from '#/lib/statsig/statsig'
|
||||||
import {logger} from '#/logger'
|
import {logger} from '#/logger'
|
||||||
|
@ -8,9 +9,16 @@ import {
|
||||||
useRemoveNuxsMutation,
|
useRemoveNuxsMutation,
|
||||||
useUpsertNuxMutation,
|
useUpsertNuxMutation,
|
||||||
} from '#/state/queries/nuxs'
|
} from '#/state/queries/nuxs'
|
||||||
import {useSession} from '#/state/session'
|
import {
|
||||||
|
usePreferencesQuery,
|
||||||
|
UsePreferencesQueryResponse,
|
||||||
|
} from '#/state/queries/preferences'
|
||||||
|
import {useProfileQuery} from '#/state/queries/profile'
|
||||||
|
import {SessionAccount, useSession} from '#/state/session'
|
||||||
import {useOnboardingState} from '#/state/shell'
|
import {useOnboardingState} from '#/state/shell'
|
||||||
|
import {NeueTypography} from '#/components/dialogs/nuxs/NeueTypography'
|
||||||
import {isSnoozed, snooze, unsnooze} from '#/components/dialogs/nuxs/snoozing'
|
import {isSnoozed, snooze, unsnooze} from '#/components/dialogs/nuxs/snoozing'
|
||||||
|
// NUXs
|
||||||
import {TenMillion} from '#/components/dialogs/nuxs/TenMillion'
|
import {TenMillion} from '#/components/dialogs/nuxs/TenMillion'
|
||||||
import {IS_DEV} from '#/env'
|
import {IS_DEV} from '#/env'
|
||||||
|
|
||||||
|
@ -21,11 +29,27 @@ type Context = {
|
||||||
|
|
||||||
const queuedNuxs: {
|
const queuedNuxs: {
|
||||||
id: Nux
|
id: Nux
|
||||||
enabled?: (props: {gate: ReturnType<typeof useGate>}) => boolean
|
enabled?: (props: {
|
||||||
|
gate: ReturnType<typeof useGate>
|
||||||
|
currentAccount: SessionAccount
|
||||||
|
currentProfile: AppBskyActorDefs.ProfileViewDetailed
|
||||||
|
preferences: UsePreferencesQueryResponse
|
||||||
|
}) => boolean
|
||||||
}[] = [
|
}[] = [
|
||||||
{
|
{
|
||||||
id: Nux.TenMillionDialog,
|
id: Nux.TenMillionDialog,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
id: Nux.NeueTypography,
|
||||||
|
enabled(props) {
|
||||||
|
if (props.currentProfile.createdAt) {
|
||||||
|
if (new Date(props.currentProfile.createdAt) < new Date('2024-09-25')) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
},
|
||||||
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
const Context = React.createContext<Context>({
|
const Context = React.createContext<Context>({
|
||||||
|
@ -38,12 +62,31 @@ export function useNuxDialogContext() {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function NuxDialogs() {
|
export function NuxDialogs() {
|
||||||
const {hasSession} = useSession()
|
const {currentAccount} = useSession()
|
||||||
const onboardingState = useOnboardingState()
|
const {data: preferences} = usePreferencesQuery()
|
||||||
return hasSession && !onboardingState.isActive ? <Inner /> : null
|
const {data: profile} = useProfileQuery({did: currentAccount?.did})
|
||||||
|
const onboardingActive = useOnboardingState().isActive
|
||||||
|
|
||||||
|
const isLoading =
|
||||||
|
!currentAccount || !preferences || !profile || onboardingActive
|
||||||
|
return !isLoading ? (
|
||||||
|
<Inner
|
||||||
|
currentAccount={currentAccount}
|
||||||
|
currentProfile={profile}
|
||||||
|
preferences={preferences}
|
||||||
|
/>
|
||||||
|
) : null
|
||||||
}
|
}
|
||||||
|
|
||||||
function Inner() {
|
function Inner({
|
||||||
|
currentAccount,
|
||||||
|
currentProfile,
|
||||||
|
preferences,
|
||||||
|
}: {
|
||||||
|
currentAccount: SessionAccount
|
||||||
|
currentProfile: AppBskyActorDefs.ProfileViewDetailed
|
||||||
|
preferences: UsePreferencesQueryResponse
|
||||||
|
}) {
|
||||||
const gate = useGate()
|
const gate = useGate()
|
||||||
const {nuxs} = useNuxs()
|
const {nuxs} = useNuxs()
|
||||||
const [snoozed, setSnoozed] = React.useState(() => {
|
const [snoozed, setSnoozed] = React.useState(() => {
|
||||||
|
@ -80,10 +123,19 @@ function Inner() {
|
||||||
const nux = nuxs.find(nux => nux.id === id)
|
const nux = nuxs.find(nux => nux.id === id)
|
||||||
|
|
||||||
// check if completed first
|
// check if completed first
|
||||||
if (nux && nux.completed) continue
|
if (nux && nux.completed) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
// then check gate (track exposure)
|
// then check gate (track exposure)
|
||||||
if (enabled && !enabled({gate})) continue
|
if (
|
||||||
|
enabled &&
|
||||||
|
!enabled({gate, currentAccount, currentProfile, preferences})
|
||||||
|
) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.debug(`NUX dialogs: activating '${id}' NUX`)
|
||||||
|
|
||||||
// we have a winner
|
// we have a winner
|
||||||
setActiveNux(id)
|
setActiveNux(id)
|
||||||
|
@ -104,7 +156,16 @@ function Inner() {
|
||||||
|
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}, [nuxs, snoozed, snoozeNuxDialog, upsertNux, gate])
|
}, [
|
||||||
|
nuxs,
|
||||||
|
snoozed,
|
||||||
|
snoozeNuxDialog,
|
||||||
|
upsertNux,
|
||||||
|
gate,
|
||||||
|
currentAccount,
|
||||||
|
currentProfile,
|
||||||
|
preferences,
|
||||||
|
])
|
||||||
|
|
||||||
const ctx = React.useMemo(() => {
|
const ctx = React.useMemo(() => {
|
||||||
return {
|
return {
|
||||||
|
@ -116,6 +177,7 @@ function Inner() {
|
||||||
return (
|
return (
|
||||||
<Context.Provider value={ctx}>
|
<Context.Provider value={ctx}>
|
||||||
{activeNux === Nux.TenMillionDialog && <TenMillion />}
|
{activeNux === Nux.TenMillionDialog && <TenMillion />}
|
||||||
|
{activeNux === Nux.NeueTypography && <NeueTypography />}
|
||||||
</Context.Provider>
|
</Context.Provider>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
5
src/components/icons/TextSize.tsx
Normal file
5
src/components/icons/TextSize.tsx
Normal file
|
@ -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',
|
||||||
|
})
|
5
src/components/icons/TitleCase.tsx
Normal file
5
src/components/icons/TitleCase.tsx
Normal file
|
@ -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',
|
||||||
|
})
|
|
@ -79,13 +79,13 @@ export const s = StyleSheet.create({
|
||||||
|
|
||||||
// font weights
|
// font weights
|
||||||
fw600: {fontWeight: '600'},
|
fw600: {fontWeight: '600'},
|
||||||
bold: {fontWeight: 'bold'},
|
bold: {fontWeight: '700'},
|
||||||
fw500: {fontWeight: '500'},
|
fw500: {fontWeight: '500'},
|
||||||
semiBold: {fontWeight: '500'},
|
semiBold: {fontWeight: '500'},
|
||||||
fw400: {fontWeight: '400'},
|
fw400: {fontWeight: '400'},
|
||||||
normal: {fontWeight: '400'},
|
normal: {fontWeight: '400'},
|
||||||
fw300: {fontWeight: '300'},
|
fw300: {fontWeight: '400'},
|
||||||
light: {fontWeight: '300'},
|
light: {fontWeight: '400'},
|
||||||
fw200: {fontWeight: '200'},
|
fw200: {fontWeight: '200'},
|
||||||
|
|
||||||
// text decoration
|
// text decoration
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import {Platform} from 'react-native'
|
import {Platform} from 'react-native'
|
||||||
|
|
||||||
|
import {tokens} from '#/alf'
|
||||||
import {darkPalette, dimPalette, lightPalette} from '#/alf/themes'
|
import {darkPalette, dimPalette, lightPalette} from '#/alf/themes'
|
||||||
import {colors} from './styles'
|
import {colors} from './styles'
|
||||||
import type {Theme} from './ThemeContext'
|
import type {Theme} from './ThemeContext'
|
||||||
|
@ -88,163 +89,163 @@ export const defaultTheme: Theme = {
|
||||||
typography: {
|
typography: {
|
||||||
'2xl-thin': {
|
'2xl-thin': {
|
||||||
fontSize: 18,
|
fontSize: 18,
|
||||||
letterSpacing: 0.25,
|
letterSpacing: tokens.TRACKING,
|
||||||
fontWeight: '300',
|
fontWeight: '400',
|
||||||
},
|
},
|
||||||
'2xl': {
|
'2xl': {
|
||||||
fontSize: 18,
|
fontSize: 18,
|
||||||
letterSpacing: 0.25,
|
letterSpacing: tokens.TRACKING,
|
||||||
fontWeight: '400',
|
fontWeight: '400',
|
||||||
},
|
},
|
||||||
'2xl-medium': {
|
'2xl-medium': {
|
||||||
fontSize: 18,
|
fontSize: 18,
|
||||||
letterSpacing: 0.25,
|
letterSpacing: tokens.TRACKING,
|
||||||
fontWeight: '500',
|
fontWeight: '500',
|
||||||
},
|
},
|
||||||
'2xl-bold': {
|
'2xl-bold': {
|
||||||
fontSize: 18,
|
fontSize: 18,
|
||||||
letterSpacing: 0.25,
|
letterSpacing: tokens.TRACKING,
|
||||||
fontWeight: '700',
|
fontWeight: '700',
|
||||||
},
|
},
|
||||||
'2xl-heavy': {
|
'2xl-heavy': {
|
||||||
fontSize: 18,
|
fontSize: 18,
|
||||||
letterSpacing: 0.25,
|
letterSpacing: tokens.TRACKING,
|
||||||
fontWeight: '800',
|
fontWeight: '800',
|
||||||
},
|
},
|
||||||
'xl-thin': {
|
'xl-thin': {
|
||||||
fontSize: 17,
|
fontSize: 17,
|
||||||
letterSpacing: 0.25,
|
letterSpacing: tokens.TRACKING,
|
||||||
fontWeight: '300',
|
fontWeight: '400',
|
||||||
},
|
},
|
||||||
xl: {
|
xl: {
|
||||||
fontSize: 17,
|
fontSize: 17,
|
||||||
letterSpacing: 0.25,
|
letterSpacing: tokens.TRACKING,
|
||||||
fontWeight: '400',
|
fontWeight: '400',
|
||||||
},
|
},
|
||||||
'xl-medium': {
|
'xl-medium': {
|
||||||
fontSize: 17,
|
fontSize: 17,
|
||||||
letterSpacing: 0.25,
|
letterSpacing: tokens.TRACKING,
|
||||||
fontWeight: '500',
|
fontWeight: '500',
|
||||||
},
|
},
|
||||||
'xl-bold': {
|
'xl-bold': {
|
||||||
fontSize: 17,
|
fontSize: 17,
|
||||||
letterSpacing: 0.25,
|
letterSpacing: tokens.TRACKING,
|
||||||
fontWeight: '700',
|
fontWeight: '700',
|
||||||
},
|
},
|
||||||
'xl-heavy': {
|
'xl-heavy': {
|
||||||
fontSize: 17,
|
fontSize: 17,
|
||||||
letterSpacing: 0.25,
|
letterSpacing: tokens.TRACKING,
|
||||||
fontWeight: '800',
|
fontWeight: '800',
|
||||||
},
|
},
|
||||||
'lg-thin': {
|
'lg-thin': {
|
||||||
fontSize: 16,
|
fontSize: 16,
|
||||||
letterSpacing: 0.25,
|
letterSpacing: tokens.TRACKING,
|
||||||
fontWeight: '300',
|
fontWeight: '400',
|
||||||
},
|
},
|
||||||
lg: {
|
lg: {
|
||||||
fontSize: 16,
|
fontSize: 16,
|
||||||
letterSpacing: 0.25,
|
letterSpacing: tokens.TRACKING,
|
||||||
fontWeight: '400',
|
fontWeight: '400',
|
||||||
},
|
},
|
||||||
'lg-medium': {
|
'lg-medium': {
|
||||||
fontSize: 16,
|
fontSize: 16,
|
||||||
letterSpacing: 0.25,
|
letterSpacing: tokens.TRACKING,
|
||||||
fontWeight: '500',
|
fontWeight: '500',
|
||||||
},
|
},
|
||||||
'lg-bold': {
|
'lg-bold': {
|
||||||
fontSize: 16,
|
fontSize: 16,
|
||||||
letterSpacing: 0.25,
|
letterSpacing: tokens.TRACKING,
|
||||||
fontWeight: '700',
|
fontWeight: '700',
|
||||||
},
|
},
|
||||||
'lg-heavy': {
|
'lg-heavy': {
|
||||||
fontSize: 16,
|
fontSize: 16,
|
||||||
letterSpacing: 0.25,
|
letterSpacing: tokens.TRACKING,
|
||||||
fontWeight: '800',
|
fontWeight: '800',
|
||||||
},
|
},
|
||||||
'md-thin': {
|
'md-thin': {
|
||||||
fontSize: 15,
|
fontSize: 15,
|
||||||
letterSpacing: 0.25,
|
letterSpacing: tokens.TRACKING,
|
||||||
fontWeight: '300',
|
fontWeight: '400',
|
||||||
},
|
},
|
||||||
md: {
|
md: {
|
||||||
fontSize: 15,
|
fontSize: 15,
|
||||||
letterSpacing: 0.25,
|
letterSpacing: tokens.TRACKING,
|
||||||
fontWeight: '400',
|
fontWeight: '400',
|
||||||
},
|
},
|
||||||
'md-medium': {
|
'md-medium': {
|
||||||
fontSize: 15,
|
fontSize: 15,
|
||||||
letterSpacing: 0.25,
|
letterSpacing: tokens.TRACKING,
|
||||||
fontWeight: '500',
|
fontWeight: '500',
|
||||||
},
|
},
|
||||||
'md-bold': {
|
'md-bold': {
|
||||||
fontSize: 15,
|
fontSize: 15,
|
||||||
letterSpacing: 0.25,
|
letterSpacing: tokens.TRACKING,
|
||||||
fontWeight: '700',
|
fontWeight: '700',
|
||||||
},
|
},
|
||||||
'md-heavy': {
|
'md-heavy': {
|
||||||
fontSize: 15,
|
fontSize: 15,
|
||||||
letterSpacing: 0.25,
|
letterSpacing: tokens.TRACKING,
|
||||||
fontWeight: '800',
|
fontWeight: '800',
|
||||||
},
|
},
|
||||||
'sm-thin': {
|
'sm-thin': {
|
||||||
fontSize: 14,
|
fontSize: 14,
|
||||||
letterSpacing: 0.25,
|
letterSpacing: tokens.TRACKING,
|
||||||
fontWeight: '300',
|
fontWeight: '400',
|
||||||
},
|
},
|
||||||
sm: {
|
sm: {
|
||||||
fontSize: 14,
|
fontSize: 14,
|
||||||
letterSpacing: 0.25,
|
letterSpacing: tokens.TRACKING,
|
||||||
fontWeight: '400',
|
fontWeight: '400',
|
||||||
},
|
},
|
||||||
'sm-medium': {
|
'sm-medium': {
|
||||||
fontSize: 14,
|
fontSize: 14,
|
||||||
letterSpacing: 0.25,
|
letterSpacing: tokens.TRACKING,
|
||||||
fontWeight: '500',
|
fontWeight: '500',
|
||||||
},
|
},
|
||||||
'sm-bold': {
|
'sm-bold': {
|
||||||
fontSize: 14,
|
fontSize: 14,
|
||||||
letterSpacing: 0.25,
|
letterSpacing: tokens.TRACKING,
|
||||||
fontWeight: '700',
|
fontWeight: '700',
|
||||||
},
|
},
|
||||||
'sm-heavy': {
|
'sm-heavy': {
|
||||||
fontSize: 14,
|
fontSize: 14,
|
||||||
letterSpacing: 0.25,
|
letterSpacing: tokens.TRACKING,
|
||||||
fontWeight: '800',
|
fontWeight: '800',
|
||||||
},
|
},
|
||||||
'xs-thin': {
|
'xs-thin': {
|
||||||
fontSize: 13,
|
fontSize: 13,
|
||||||
letterSpacing: 0.25,
|
letterSpacing: tokens.TRACKING,
|
||||||
fontWeight: '300',
|
fontWeight: '400',
|
||||||
},
|
},
|
||||||
xs: {
|
xs: {
|
||||||
fontSize: 13,
|
fontSize: 13,
|
||||||
letterSpacing: 0.25,
|
letterSpacing: tokens.TRACKING,
|
||||||
fontWeight: '400',
|
fontWeight: '400',
|
||||||
},
|
},
|
||||||
'xs-medium': {
|
'xs-medium': {
|
||||||
fontSize: 13,
|
fontSize: 13,
|
||||||
letterSpacing: 0.25,
|
letterSpacing: tokens.TRACKING,
|
||||||
fontWeight: '500',
|
fontWeight: '500',
|
||||||
},
|
},
|
||||||
'xs-bold': {
|
'xs-bold': {
|
||||||
fontSize: 13,
|
fontSize: 13,
|
||||||
letterSpacing: 0.25,
|
letterSpacing: tokens.TRACKING,
|
||||||
fontWeight: '700',
|
fontWeight: '700',
|
||||||
},
|
},
|
||||||
'xs-heavy': {
|
'xs-heavy': {
|
||||||
fontSize: 13,
|
fontSize: 13,
|
||||||
letterSpacing: 0.25,
|
letterSpacing: tokens.TRACKING,
|
||||||
fontWeight: '800',
|
fontWeight: '800',
|
||||||
},
|
},
|
||||||
|
|
||||||
'title-2xl': {
|
'title-2xl': {
|
||||||
fontSize: 34,
|
fontSize: 34,
|
||||||
letterSpacing: 0.25,
|
letterSpacing: tokens.TRACKING,
|
||||||
fontWeight: '500',
|
fontWeight: '500',
|
||||||
},
|
},
|
||||||
'title-xl': {
|
'title-xl': {
|
||||||
fontSize: 28,
|
fontSize: 28,
|
||||||
letterSpacing: 0.25,
|
letterSpacing: tokens.TRACKING,
|
||||||
fontWeight: '500',
|
fontWeight: '500',
|
||||||
},
|
},
|
||||||
'title-lg': {
|
'title-lg': {
|
||||||
|
@ -254,32 +255,32 @@ export const defaultTheme: Theme = {
|
||||||
title: {
|
title: {
|
||||||
fontWeight: '500',
|
fontWeight: '500',
|
||||||
fontSize: 20,
|
fontSize: 20,
|
||||||
letterSpacing: 0.15,
|
letterSpacing: tokens.TRACKING,
|
||||||
},
|
},
|
||||||
'title-sm': {
|
'title-sm': {
|
||||||
fontWeight: 'bold',
|
fontWeight: 'bold',
|
||||||
fontSize: 17,
|
fontSize: 17,
|
||||||
letterSpacing: 0.15,
|
letterSpacing: tokens.TRACKING,
|
||||||
},
|
},
|
||||||
'post-text': {
|
'post-text': {
|
||||||
fontSize: 16,
|
fontSize: 16,
|
||||||
letterSpacing: 0.2,
|
letterSpacing: tokens.TRACKING,
|
||||||
fontWeight: '400',
|
fontWeight: '400',
|
||||||
},
|
},
|
||||||
'post-text-lg': {
|
'post-text-lg': {
|
||||||
fontSize: 20,
|
fontSize: 20,
|
||||||
letterSpacing: 0.2,
|
letterSpacing: tokens.TRACKING,
|
||||||
fontWeight: '400',
|
fontWeight: '400',
|
||||||
},
|
},
|
||||||
'button-lg': {
|
'button-lg': {
|
||||||
fontWeight: '500',
|
fontWeight: '500',
|
||||||
fontSize: 18,
|
fontSize: 18,
|
||||||
letterSpacing: 0.5,
|
letterSpacing: tokens.TRACKING,
|
||||||
},
|
},
|
||||||
button: {
|
button: {
|
||||||
fontWeight: '500',
|
fontWeight: '500',
|
||||||
fontSize: 14,
|
fontSize: 14,
|
||||||
letterSpacing: 0.5,
|
letterSpacing: tokens.TRACKING,
|
||||||
},
|
},
|
||||||
mono: {
|
mono: {
|
||||||
fontSize: 14,
|
fontSize: 14,
|
||||||
|
|
|
@ -14,17 +14,21 @@ import {s} from '#/lib/styles'
|
||||||
import {useSetThemePrefs, useThemePrefs} from '#/state/shell'
|
import {useSetThemePrefs, useThemePrefs} from '#/state/shell'
|
||||||
import {SimpleViewHeader} from '#/view/com/util/SimpleViewHeader'
|
import {SimpleViewHeader} from '#/view/com/util/SimpleViewHeader'
|
||||||
import {ScrollView} from '#/view/com/util/Views'
|
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 ToggleButton from '#/components/forms/ToggleButton'
|
import * as ToggleButton from '#/components/forms/ToggleButton'
|
||||||
|
import {Props as SVGIconProps} from '#/components/icons/common'
|
||||||
import {Moon_Stroke2_Corner0_Rounded as MoonIcon} from '#/components/icons/Moon'
|
import {Moon_Stroke2_Corner0_Rounded as MoonIcon} from '#/components/icons/Moon'
|
||||||
import {Phone_Stroke2_Corner0_Rounded as PhoneIcon} from '#/components/icons/Phone'
|
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'
|
import {Text} from '#/components/Typography'
|
||||||
|
|
||||||
type Props = NativeStackScreenProps<CommonNavigatorParams, 'AppearanceSettings'>
|
type Props = NativeStackScreenProps<CommonNavigatorParams, 'AppearanceSettings'>
|
||||||
export function AppearanceSettingsScreen({}: Props) {
|
export function AppearanceSettingsScreen({}: Props) {
|
||||||
const {_} = useLingui()
|
|
||||||
const t = useTheme()
|
const t = useTheme()
|
||||||
|
const {_} = useLingui()
|
||||||
const {isTabletOrMobile} = useWebMediaQueries()
|
const {isTabletOrMobile} = useWebMediaQueries()
|
||||||
|
const {fonts} = useAlf()
|
||||||
|
|
||||||
const {colorMode, darkTheme} = useThemePrefs()
|
const {colorMode, darkTheme} = useThemePrefs()
|
||||||
const {setColorMode, setDarkTheme} = useSetThemePrefs()
|
const {setColorMode, setDarkTheme} = useSetThemePrefs()
|
||||||
|
@ -54,6 +58,22 @@ export function AppearanceSettingsScreen({}: Props) {
|
||||||
[setDarkTheme, darkTheme],
|
[setDarkTheme, darkTheme],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const onChangeFontFamily = useCallback(
|
||||||
|
(values: string[]) => {
|
||||||
|
const next = values[0] === 'system' ? 'system' : 'theme'
|
||||||
|
fonts.setFontFamily(next)
|
||||||
|
},
|
||||||
|
[fonts],
|
||||||
|
)
|
||||||
|
|
||||||
|
const onChangeFontScale = useCallback(
|
||||||
|
(values: string[]) => {
|
||||||
|
const next = values[0] || ('0' as any)
|
||||||
|
fonts.setFontScale(next)
|
||||||
|
},
|
||||||
|
[fonts],
|
||||||
|
)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<LayoutAnimationConfig skipExiting skipEntering>
|
<LayoutAnimationConfig skipExiting skipEntering>
|
||||||
<View testID="preferencesThreadsScreen" style={s.hContentRegion}>
|
<View testID="preferencesThreadsScreen" style={s.hContentRegion}>
|
||||||
|
@ -71,65 +91,143 @@ export function AppearanceSettingsScreen({}: Props) {
|
||||||
</View>
|
</View>
|
||||||
</SimpleViewHeader>
|
</SimpleViewHeader>
|
||||||
|
|
||||||
<View style={[a.p_xl, a.gap_lg]}>
|
<View style={[a.gap_3xl, a.pt_xl, a.px_xl]}>
|
||||||
<View style={[a.flex_row, a.align_center, a.gap_md]}>
|
<View style={[a.gap_lg]}>
|
||||||
<PhoneIcon style={t.atoms.text} />
|
<AppearanceToggleButtonGroup
|
||||||
<Text style={a.text_md}>
|
title={_(msg`Color mode`)}
|
||||||
<Trans>Mode</Trans>
|
icon={PhoneIcon}
|
||||||
</Text>
|
items={[
|
||||||
</View>
|
{
|
||||||
<ToggleButton.Group
|
label: _(msg`System`),
|
||||||
label={_(msg`Dark mode`)}
|
name: 'system',
|
||||||
values={[colorMode]}
|
},
|
||||||
onChange={onChangeAppearance}>
|
{
|
||||||
<ToggleButton.Button label={_(msg`System`)} name="system">
|
label: _(msg`Light`),
|
||||||
<ToggleButton.ButtonText>
|
name: 'light',
|
||||||
<Trans>System</Trans>
|
},
|
||||||
</ToggleButton.ButtonText>
|
{
|
||||||
</ToggleButton.Button>
|
label: _(msg`Dark`),
|
||||||
<ToggleButton.Button label={_(msg`Light`)} name="light">
|
name: 'dark',
|
||||||
<ToggleButton.ButtonText>
|
},
|
||||||
<Trans>Light</Trans>
|
]}
|
||||||
</ToggleButton.ButtonText>
|
values={[colorMode]}
|
||||||
</ToggleButton.Button>
|
onChange={onChangeAppearance}
|
||||||
<ToggleButton.Button label={_(msg`Dark`)} name="dark">
|
/>
|
||||||
<ToggleButton.ButtonText>
|
|
||||||
<Trans>Dark</Trans>
|
|
||||||
</ToggleButton.ButtonText>
|
|
||||||
</ToggleButton.Button>
|
|
||||||
</ToggleButton.Group>
|
|
||||||
{colorMode !== 'light' && (
|
|
||||||
<Animated.View
|
|
||||||
entering={native(FadeInDown)}
|
|
||||||
exiting={native(FadeOutDown)}
|
|
||||||
style={[a.mt_md, a.gap_lg]}>
|
|
||||||
<View style={[a.flex_row, a.align_center, a.gap_md]}>
|
|
||||||
<MoonIcon style={t.atoms.text} />
|
|
||||||
<Text style={a.text_md}>
|
|
||||||
<Trans>Dark theme</Trans>
|
|
||||||
</Text>
|
|
||||||
</View>
|
|
||||||
|
|
||||||
<ToggleButton.Group
|
{colorMode !== 'light' && (
|
||||||
label={_(msg`Dark theme`)}
|
<Animated.View
|
||||||
values={[darkTheme ?? 'dim']}
|
entering={native(FadeInDown)}
|
||||||
onChange={onChangeDarkTheme}>
|
exiting={native(FadeOutDown)}>
|
||||||
<ToggleButton.Button label={_(msg`Dim`)} name="dim">
|
<AppearanceToggleButtonGroup
|
||||||
<ToggleButton.ButtonText>
|
title={_(msg`Dark theme`)}
|
||||||
<Trans>Dim</Trans>
|
icon={MoonIcon}
|
||||||
</ToggleButton.ButtonText>
|
items={[
|
||||||
</ToggleButton.Button>
|
{
|
||||||
<ToggleButton.Button label={_(msg`Dark`)} name="dark">
|
label: _(msg`Dim`),
|
||||||
<ToggleButton.ButtonText>
|
name: 'dim',
|
||||||
<Trans>Dark</Trans>
|
},
|
||||||
</ToggleButton.ButtonText>
|
{
|
||||||
</ToggleButton.Button>
|
label: _(msg`Dark`),
|
||||||
</ToggleButton.Group>
|
name: 'dark',
|
||||||
</Animated.View>
|
},
|
||||||
)}
|
]}
|
||||||
|
values={[darkTheme ?? 'dim']}
|
||||||
|
onChange={onChangeDarkTheme}
|
||||||
|
/>
|
||||||
|
</Animated.View>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<AppearanceToggleButtonGroup
|
||||||
|
title={_(msg`Font`)}
|
||||||
|
description={_(
|
||||||
|
msg`For the best experience, we recommend using the theme font.`,
|
||||||
|
)}
|
||||||
|
icon={Aa}
|
||||||
|
items={[
|
||||||
|
{
|
||||||
|
label: _(msg`System`),
|
||||||
|
name: 'system',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: _(msg`Theme`),
|
||||||
|
name: 'theme',
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
values={[fonts.family]}
|
||||||
|
onChange={onChangeFontFamily}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<AppearanceToggleButtonGroup
|
||||||
|
title={_(msg`Font size`)}
|
||||||
|
icon={TextSize}
|
||||||
|
items={[
|
||||||
|
{
|
||||||
|
label: _(msg`Smaller`),
|
||||||
|
name: '-1',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: _(msg`Default`),
|
||||||
|
name: '0',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: _(msg`Larger`),
|
||||||
|
name: '1',
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
values={[fonts.scale]}
|
||||||
|
onChange={onChangeFontScale}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
</View>
|
</View>
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
</View>
|
</View>
|
||||||
</LayoutAnimationConfig>
|
</LayoutAnimationConfig>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function AppearanceToggleButtonGroup({
|
||||||
|
title,
|
||||||
|
description,
|
||||||
|
icon: Icon,
|
||||||
|
items,
|
||||||
|
values,
|
||||||
|
onChange,
|
||||||
|
}: {
|
||||||
|
title: string
|
||||||
|
description?: string
|
||||||
|
icon: React.ComponentType<SVGIconProps>
|
||||||
|
items: {
|
||||||
|
label: string
|
||||||
|
name: string
|
||||||
|
}[]
|
||||||
|
values: string[]
|
||||||
|
onChange: (values: string[]) => void
|
||||||
|
}) {
|
||||||
|
const t = useTheme()
|
||||||
|
return (
|
||||||
|
<View style={[a.gap_md]}>
|
||||||
|
<View style={[a.gap_xs]}>
|
||||||
|
<View style={[a.flex_row, a.align_center, a.gap_md]}>
|
||||||
|
<Icon style={t.atoms.text} />
|
||||||
|
<Text style={[a.text_md, a.font_bold]}>{title}</Text>
|
||||||
|
</View>
|
||||||
|
{description && (
|
||||||
|
<Text
|
||||||
|
style={[a.text_sm, a.leading_snug, t.atoms.text_contrast_medium]}>
|
||||||
|
{description}
|
||||||
|
</Text>
|
||||||
|
)}
|
||||||
|
</View>
|
||||||
|
<ToggleButton.Group label={title} values={values} onChange={onChange}>
|
||||||
|
{items.map(item => (
|
||||||
|
<ToggleButton.Button
|
||||||
|
key={item.name}
|
||||||
|
label={item.label}
|
||||||
|
name={item.name}>
|
||||||
|
<ToggleButton.ButtonText>{item.label}</ToggleButton.ButtonText>
|
||||||
|
</ToggleButton.Button>
|
||||||
|
))}
|
||||||
|
</ToggleButton.Group>
|
||||||
|
</View>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
|
@ -4,15 +4,22 @@ import {BaseNux} from '#/state/queries/nuxs/types'
|
||||||
|
|
||||||
export enum Nux {
|
export enum Nux {
|
||||||
TenMillionDialog = 'TenMillionDialog',
|
TenMillionDialog = 'TenMillionDialog',
|
||||||
|
NeueTypography = 'NeueTypography',
|
||||||
}
|
}
|
||||||
|
|
||||||
export const nuxNames = new Set(Object.values(Nux))
|
export const nuxNames = new Set(Object.values(Nux))
|
||||||
|
|
||||||
export type AppNux = BaseNux<{
|
export type AppNux =
|
||||||
id: Nux.TenMillionDialog
|
| BaseNux<{
|
||||||
data: undefined
|
id: Nux.TenMillionDialog
|
||||||
}>
|
data: undefined
|
||||||
|
}>
|
||||||
|
| BaseNux<{
|
||||||
|
id: Nux.NeueTypography
|
||||||
|
data: undefined
|
||||||
|
}>
|
||||||
|
|
||||||
export const NuxSchemas: Record<Nux, zod.ZodObject<any> | undefined> = {
|
export const NuxSchemas: Record<Nux, zod.ZodObject<any> | undefined> = {
|
||||||
[Nux.TenMillionDialog]: undefined,
|
[Nux.TenMillionDialog]: undefined,
|
||||||
|
[Nux.NeueTypography]: undefined,
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,8 @@ import {MMKV} from 'react-native-mmkv'
|
||||||
|
|
||||||
import {Device} from '#/storage/schema'
|
import {Device} from '#/storage/schema'
|
||||||
|
|
||||||
|
export * from '#/storage/schema'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generic storage class. DO NOT use this directly. Instead, use the exported
|
* Generic storage class. DO NOT use this directly. Instead, use the exported
|
||||||
* storage instances below.
|
* storage instances below.
|
||||||
|
|
|
@ -2,5 +2,7 @@
|
||||||
* Device data that's specific to the device and does not vary based account
|
* Device data that's specific to the device and does not vary based account
|
||||||
*/
|
*/
|
||||||
export type Device = {
|
export type Device = {
|
||||||
|
fontScale: '-2' | '-1' | '0' | '1' | '2'
|
||||||
|
fontFamily: 'system' | 'theme'
|
||||||
lastNuxDialog: string | undefined
|
lastNuxDialog: string | undefined
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import {Text as RNText, TextProps} from 'react-native'
|
import {StyleSheet, Text as RNText, TextProps} from 'react-native'
|
||||||
import {UITextView} from 'react-native-uitextview'
|
import {UITextView} from 'react-native-uitextview'
|
||||||
|
|
||||||
import {lh, s} from 'lib/styles'
|
import {lh, s} from 'lib/styles'
|
||||||
import {TypographyVariant, useTheme} from 'lib/ThemeContext'
|
import {TypographyVariant, useTheme} from 'lib/ThemeContext'
|
||||||
import {isIOS, isWeb} from 'platform/detection'
|
import {isIOS, isWeb} from 'platform/detection'
|
||||||
|
import {applyFonts, useAlf} from '#/alf'
|
||||||
|
|
||||||
export type CustomTextProps = TextProps & {
|
export type CustomTextProps = TextProps & {
|
||||||
type?: TypographyVariant
|
type?: TypographyVariant
|
||||||
|
@ -32,11 +33,28 @@ export function Text({
|
||||||
const theme = useTheme()
|
const theme = useTheme()
|
||||||
const typography = theme.typography[type]
|
const typography = theme.typography[type]
|
||||||
const lineHeightStyle = lineHeight ? lh(theme, type, lineHeight) : undefined
|
const lineHeightStyle = lineHeight ? lh(theme, type, lineHeight) : undefined
|
||||||
|
const {fonts} = useAlf()
|
||||||
|
|
||||||
if (selectable && isIOS) {
|
if (selectable && isIOS) {
|
||||||
|
const flattened = StyleSheet.flatten([
|
||||||
|
s.black,
|
||||||
|
typography,
|
||||||
|
lineHeightStyle,
|
||||||
|
style,
|
||||||
|
])
|
||||||
|
|
||||||
|
applyFonts(flattened, fonts.family)
|
||||||
|
|
||||||
|
// should always be defined on `typography`
|
||||||
|
// @ts-ignore
|
||||||
|
if (flattened.fontSize) {
|
||||||
|
// @ts-ignore
|
||||||
|
flattened.fontSize = flattened.fontSize * fonts.scaleMultiplier
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<UITextView
|
<UITextView
|
||||||
style={[s.black, typography, lineHeightStyle, style]}
|
style={flattened}
|
||||||
selectable={selectable}
|
selectable={selectable}
|
||||||
uiTextView
|
uiTextView
|
||||||
{...props}>
|
{...props}>
|
||||||
|
@ -45,15 +63,26 @@ export function Text({
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const flattened = StyleSheet.flatten([
|
||||||
|
s.black,
|
||||||
|
typography,
|
||||||
|
isWeb && fontFamilyStyle,
|
||||||
|
lineHeightStyle,
|
||||||
|
style,
|
||||||
|
])
|
||||||
|
|
||||||
|
applyFonts(flattened, fonts.family)
|
||||||
|
|
||||||
|
// should always be defined on `typography`
|
||||||
|
// @ts-ignore
|
||||||
|
if (flattened.fontSize) {
|
||||||
|
// @ts-ignore
|
||||||
|
flattened.fontSize = flattened.fontSize * fonts.scaleMultiplier
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<RNText
|
<RNText
|
||||||
style={[
|
style={flattened}
|
||||||
s.black,
|
|
||||||
typography,
|
|
||||||
isWeb && fontFamilyStyle,
|
|
||||||
lineHeightStyle,
|
|
||||||
style,
|
|
||||||
]}
|
|
||||||
// @ts-ignore web only -esb
|
// @ts-ignore web only -esb
|
||||||
dataSet={Object.assign({tooltip: title}, dataSet || {})}
|
dataSet={Object.assign({tooltip: title}, dataSet || {})}
|
||||||
selectable={selectable}
|
selectable={selectable}
|
||||||
|
|
|
@ -12219,6 +12219,13 @@ expo-file-system@^17.0.1, expo-file-system@~17.0.1:
|
||||||
resolved "https://registry.yarnpkg.com/expo-file-system/-/expo-file-system-17.0.1.tgz#b9f8af8c1c06ec71d96fd7a0d2567fa9e1c88f15"
|
resolved "https://registry.yarnpkg.com/expo-file-system/-/expo-file-system-17.0.1.tgz#b9f8af8c1c06ec71d96fd7a0d2567fa9e1c88f15"
|
||||||
integrity sha512-dYpnZJqTGj6HCYJyXAgpFkQWsiCH3HY1ek2cFZVHFoEc5tLz9gmdEgTF6nFHurvmvfmXqxi7a5CXyVm0aFYJBw==
|
integrity sha512-dYpnZJqTGj6HCYJyXAgpFkQWsiCH3HY1ek2cFZVHFoEc5tLz9gmdEgTF6nFHurvmvfmXqxi7a5CXyVm0aFYJBw==
|
||||||
|
|
||||||
|
expo-font@~12.0.10:
|
||||||
|
version "12.0.10"
|
||||||
|
resolved "https://registry.yarnpkg.com/expo-font/-/expo-font-12.0.10.tgz#62deaf1f46159d7839f01305f44079268781b1db"
|
||||||
|
integrity sha512-Q1i2NuYri3jy32zdnBaHHCya1wH1yMAsI+3CCmj9zlQzlhsS9Bdwcj2W3c5eU5FvH2hsNQy4O+O1NnM6o/pDaQ==
|
||||||
|
dependencies:
|
||||||
|
fontfaceobserver "^2.1.0"
|
||||||
|
|
||||||
expo-font@~12.0.5:
|
expo-font@~12.0.5:
|
||||||
version "12.0.5"
|
version "12.0.5"
|
||||||
resolved "https://registry.yarnpkg.com/expo-font/-/expo-font-12.0.5.tgz#3451c2bd3f98859b127a6484d3474a292889b93f"
|
resolved "https://registry.yarnpkg.com/expo-font/-/expo-font-12.0.5.tgz#3451c2bd3f98859b127a6484d3474a292889b93f"
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue