diff --git a/src/components/Dialog/index.tsx b/src/components/Dialog/index.tsx
index cdce3765f0..d5d92048ad 100644
--- a/src/components/Dialog/index.tsx
+++ b/src/components/Dialog/index.tsx
@@ -37,6 +37,7 @@ import {Portal} from '#/components/Portal'
export {useDialogContext, useDialogControl} from '#/components/Dialog/context'
export * from '#/components/Dialog/types'
+export * from '#/components/Dialog/utils'
// @ts-ignore
export const Input = createInput(BottomSheetTextInput)
diff --git a/src/components/Dialog/index.web.tsx b/src/components/Dialog/index.web.tsx
index aff1842f77..bf20bd2956 100644
--- a/src/components/Dialog/index.web.tsx
+++ b/src/components/Dialog/index.web.tsx
@@ -27,6 +27,7 @@ import {Portal} from '#/components/Portal'
export {useDialogContext, useDialogControl} from '#/components/Dialog/context'
export * from '#/components/Dialog/types'
+export * from '#/components/Dialog/utils'
export {Input} from '#/components/forms/TextField'
const stopPropagation = (e: any) => e.stopPropagation()
diff --git a/src/components/Dialog/utils.ts b/src/components/Dialog/utils.ts
new file mode 100644
index 0000000000..058d6e8041
--- /dev/null
+++ b/src/components/Dialog/utils.ts
@@ -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])
+}
diff --git a/src/components/dialogs/nuxs/NeueTypography.tsx b/src/components/dialogs/nuxs/NeueTypography.tsx
new file mode 100644
index 0000000000..f33cea8e78
--- /dev/null
+++ b/src/components/dialogs/nuxs/NeueTypography.tsx
@@ -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 (
+
+
+
+
+
+
+
+ Introducing new font settings ✨
+
+
+
+ To the ensure the best possible experience, we're introducing a
+ new theme font, along with adjustable font sizing settings.
+
+
+
+
+ Defaults are shown below. You can edit these in your Appearance
+ Settings later.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ )
+}
diff --git a/src/components/dialogs/nuxs/index.tsx b/src/components/dialogs/nuxs/index.tsx
index a38c87b68e..e826895cba 100644
--- a/src/components/dialogs/nuxs/index.tsx
+++ b/src/components/dialogs/nuxs/index.tsx
@@ -1,4 +1,5 @@
import React from 'react'
+import {AppBskyActorDefs} from '@atproto/api'
import {useGate} from '#/lib/statsig/statsig'
import {logger} from '#/logger'
@@ -8,9 +9,16 @@ import {
useRemoveNuxsMutation,
useUpsertNuxMutation,
} 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 {NeueTypography} from '#/components/dialogs/nuxs/NeueTypography'
import {isSnoozed, snooze, unsnooze} from '#/components/dialogs/nuxs/snoozing'
+// NUXs
import {TenMillion} from '#/components/dialogs/nuxs/TenMillion'
import {IS_DEV} from '#/env'
@@ -21,11 +29,27 @@ type Context = {
const queuedNuxs: {
id: Nux
- enabled?: (props: {gate: ReturnType}) => boolean
+ enabled?: (props: {
+ gate: ReturnType
+ currentAccount: SessionAccount
+ currentProfile: AppBskyActorDefs.ProfileViewDetailed
+ preferences: UsePreferencesQueryResponse
+ }) => boolean
}[] = [
{
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({
@@ -38,12 +62,31 @@ export function useNuxDialogContext() {
}
export function NuxDialogs() {
- const {hasSession} = useSession()
- const onboardingState = useOnboardingState()
- return hasSession && !onboardingState.isActive ? : null
+ const {currentAccount} = useSession()
+ const {data: preferences} = usePreferencesQuery()
+ const {data: profile} = useProfileQuery({did: currentAccount?.did})
+ const onboardingActive = useOnboardingState().isActive
+
+ const isLoading =
+ !currentAccount || !preferences || !profile || onboardingActive
+ return isLoading ? null : (
+
+ )
}
-function Inner() {
+function Inner({
+ currentAccount,
+ currentProfile,
+ preferences,
+}: {
+ currentAccount: SessionAccount
+ currentProfile: AppBskyActorDefs.ProfileViewDetailed
+ preferences: UsePreferencesQueryResponse
+}) {
const gate = useGate()
const {nuxs} = useNuxs()
const [snoozed, setSnoozed] = React.useState(() => {
@@ -83,7 +126,13 @@ function Inner() {
if (nux && nux.completed) continue
// 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
setActiveNux(id)
@@ -104,7 +153,16 @@ function Inner() {
break
}
- }, [nuxs, snoozed, snoozeNuxDialog, upsertNux, gate])
+ }, [
+ nuxs,
+ snoozed,
+ snoozeNuxDialog,
+ upsertNux,
+ gate,
+ currentAccount,
+ currentProfile,
+ preferences,
+ ])
const ctx = React.useMemo(() => {
return {
@@ -116,6 +174,7 @@ function Inner() {
return (
{activeNux === Nux.TenMillionDialog && }
+ {activeNux === Nux.NeueTypography && }
)
}
diff --git a/src/state/queries/nuxs/definitions.ts b/src/state/queries/nuxs/definitions.ts
index 865967d37a..63a8079623 100644
--- a/src/state/queries/nuxs/definitions.ts
+++ b/src/state/queries/nuxs/definitions.ts
@@ -4,15 +4,22 @@ import {BaseNux} from '#/state/queries/nuxs/types'
export enum Nux {
TenMillionDialog = 'TenMillionDialog',
+ NeueTypography = 'NeueTypography',
}
export const nuxNames = new Set(Object.values(Nux))
-export type AppNux = BaseNux<{
- id: Nux.TenMillionDialog
- data: undefined
-}>
+export type AppNux =
+ | BaseNux<{
+ id: Nux.TenMillionDialog
+ data: undefined
+ }>
+ | BaseNux<{
+ id: Nux.NeueTypography
+ data: undefined
+ }>
export const NuxSchemas: Record | undefined> = {
[Nux.TenMillionDialog]: undefined,
+ [Nux.NeueTypography]: undefined,
}