Add neue type nux

This commit is contained in:
Eric Bailey
2024-09-18 14:05:38 -05:00
parent 77167d0b51
commit f86844eaca
6 changed files with 217 additions and 12 deletions
+1
View File
@@ -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)
+1
View File
@@ -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()
+18
View 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])
}
@@ -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>
)
}
+67 -8
View File
@@ -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<typeof useGate>}) => boolean
enabled?: (props: {
gate: ReturnType<typeof useGate>
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<Context>({
@@ -38,12 +62,31 @@ export function useNuxDialogContext() {
}
export function NuxDialogs() {
const {hasSession} = useSession()
const onboardingState = useOnboardingState()
return hasSession && !onboardingState.isActive ? <Inner /> : 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 : (
<Inner
currentAccount={currentAccount}
currentProfile={profile}
preferences={preferences}
/>
)
}
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 (
<Context.Provider value={ctx}>
{activeNux === Nux.TenMillionDialog && <TenMillion />}
{activeNux === Nux.NeueTypography && <NeueTypography />}
</Context.Provider>
)
}
+11 -4
View File
@@ -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<Nux, zod.ZodObject<any> | undefined> = {
[Nux.TenMillionDialog]: undefined,
[Nux.NeueTypography]: undefined,
}