Add neue type nux
This commit is contained in:
@@ -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()
|
||||||
|
|||||||
@@ -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>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -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 ? 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 gate = useGate()
|
||||||
const {nuxs} = useNuxs()
|
const {nuxs} = useNuxs()
|
||||||
const [snoozed, setSnoozed] = React.useState(() => {
|
const [snoozed, setSnoozed] = React.useState(() => {
|
||||||
@@ -83,7 +126,13 @@ function Inner() {
|
|||||||
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 +153,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 +174,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>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 =
|
||||||
|
| BaseNux<{
|
||||||
id: Nux.TenMillionDialog
|
id: Nux.TenMillionDialog
|
||||||
data: undefined
|
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,
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user