diff --git a/src/components/dialogs/BlueskyPlus/index.tsx b/src/components/dialogs/BlueskyPlus/index.tsx
index 3686733676..27c83d55dc 100644
--- a/src/components/dialogs/BlueskyPlus/index.tsx
+++ b/src/components/dialogs/BlueskyPlus/index.tsx
@@ -91,9 +91,9 @@ function DialogInner({control}: {control: Dialog.DialogControlProps}) {
a.overflow_hidden,
gtMobile ? {width: '100%', maxWidth: 400, minWidth: 200} : a.w_full,
]}>
-
+
-
+
Let's build the social web.
diff --git a/src/components/dialogs/nuxs/index.tsx b/src/components/dialogs/nuxs/index.tsx
index 701ae84e69..11d622a4dc 100644
--- a/src/components/dialogs/nuxs/index.tsx
+++ b/src/components/dialogs/nuxs/index.tsx
@@ -3,12 +3,7 @@ import {AppBskyActorDefs} from '@atproto/api'
import {useGate} from '#/lib/statsig/statsig'
import {logger} from '#/logger'
-import {
- Nux,
- useNuxs,
- useRemoveNuxsMutation,
- useUpsertNuxMutation,
-} from '#/state/queries/nuxs'
+import {Nux, useNuxs, useResetNuxs, useSaveNux} from '#/state/queries/nuxs'
import {
usePreferencesQuery,
UsePreferencesQueryResponse,
@@ -85,8 +80,8 @@ function Inner({
return isSnoozed()
})
const [activeNux, setActiveNux] = React.useState()
- const {mutateAsync: upsertNux} = useUpsertNuxMutation()
- const {mutate: removeNuxs} = useRemoveNuxsMutation()
+ const {mutateAsync: saveNux} = useSaveNux()
+ const {mutate: resetNuxs} = useResetNuxs()
const snoozeNuxDialog = React.useCallback(() => {
snooze()
@@ -102,7 +97,7 @@ function Inner({
// @ts-ignore
window.clearNuxDialog = (id: Nux) => {
if (!IS_DEV || !id) return
- removeNuxs([id])
+ resetNuxs([id])
unsnooze()
}
}
@@ -136,7 +131,7 @@ function Inner({
snoozeNuxDialog()
// immediately update remote data (affects next reload)
- upsertNux({
+ saveNux({
id,
completed: true,
data: undefined,
@@ -152,7 +147,7 @@ function Inner({
nuxs,
snoozed,
snoozeNuxDialog,
- upsertNux,
+ saveNux,
gate,
currentAccount,
currentProfile,
diff --git a/src/components/icons/BlueskyPlus.tsx b/src/components/icons/BlueskyPlus.tsx
index f5da7ff0b1..3261aa5e2e 100644
--- a/src/components/icons/BlueskyPlus.tsx
+++ b/src/components/icons/BlueskyPlus.tsx
@@ -1,34 +1,15 @@
-import React from 'react'
-import {StyleSheet, TextProps} from 'react-native'
-import Svg, {
- Defs,
- LinearGradient,
- Path,
- PathProps,
- Stop,
- SvgProps,
-} from 'react-native-svg'
+import Svg, {Path} from 'react-native-svg'
import {msg} from '@lingui/macro'
import {useLingui} from '@lingui/react'
-import {useTheme} from '#/alf'
-
-type Props = {
- fill?: PathProps['fill']
- style?: TextProps['style']
-} & Omit
+import {Props, useCommonSVGProps} from '#/components/icons/common'
/**
* Full Bluesky+ logo with butterfly
*/
export function Full(props: Props) {
- const t = useTheme()
- const {fill, ...rest} = props
- const gradient = fill === 'nordic'
- const styles = StyleSheet.flatten(props.style)
- const _fill = gradient
- ? 'url(#nordic)'
- : fill || styles?.color || t.palette.primary_500
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
+ const {fill, size: _size, style, gradient, ...rest} = useCommonSVGProps(props)
// @ts-ignore it's fiiiiine
const size = parseInt(rest.width || 100)
const ratio = 90 / 448
@@ -41,10 +22,10 @@ export function Full(props: Props) {
accessibilityLabel={_(msg`Bluesky Plus`)}
accessibilityHint=""
{...rest}
- style={[styles, {width: size, height: size * ratio}]}>
- {gradient && }
+ style={[style, {width: size, height: size * ratio}]}>
+ {gradient}
- {gradient && }
+ style={[style, {width: size, height: size * ratio}]}>
+ {gradient}
)
}
-
-function Gradients() {
- return (
-
-
-
-
-
-
- )
-}
diff --git a/src/screens/Subscriptions/index.tsx b/src/screens/Subscriptions/index.tsx
index 2e2d92d627..e2c2ba8e28 100644
--- a/src/screens/Subscriptions/index.tsx
+++ b/src/screens/Subscriptions/index.tsx
@@ -1,16 +1,15 @@
-import React from 'react'
import {View} from 'react-native'
import {msg, Trans} from '@lingui/macro'
import {useLingui} from '@lingui/react'
import {NativeStackScreenProps} from '@react-navigation/native-stack'
import {CommonNavigatorParams} from '#/lib/routes/types'
-import {PurchasesState,usePurchases} from '#/state/purchases'
+import {PurchasesState, usePurchases} from '#/state/purchases'
import {useManageSubscription} from '#/state/purchases/hooks/useManageSubscription'
import {SubscriptionGroupId} from '#/state/purchases/types'
import {APISubscription} from '#/state/purchases/types'
import {CenteredView} from '#/view/com/util/Views'
-import {atoms as a, tokens,useTheme} from '#/alf'
+import {atoms as a, tokens, useTheme} from '#/alf'
import {Admonition} from '#/components/Admonition'
import {Button, ButtonIcon, ButtonText} from '#/components/Button'
import {useDialogControl} from '#/components/Dialog'
@@ -23,7 +22,7 @@ import {Clock_Stroke2_Corner0_Rounded as Clock} from '#/components/icons/Clock'
import {PlusLarge_Stroke2_Corner0_Rounded as Plus} from '#/components/icons/Plus'
import {SettingsGear2_Stroke2_Corner0_Rounded as Gear} from '#/components/icons/SettingsGear2'
import * as Layout from '#/components/Layout'
-import {createStaticClick,InlineLinkText, Link} from '#/components/Link'
+import {createStaticClick, InlineLinkText, Link} from '#/components/Link'
import {Loader} from '#/components/Loader'
import {Text} from '#/components/Typography'
@@ -69,7 +68,6 @@ function Core({
s => s.group === SubscriptionGroupId.Core,
)
const isSubscribedToCore = !!coreSubscriptions.length
- console.log(state)
const features = [
{
@@ -116,7 +114,7 @@ function Core({
return (
-
+
{isSubscribedToCore ? (
@@ -246,17 +244,6 @@ function Core({
EULA
-
-
-
- Learn more about Bluesky+ and our roadmap{' '}
-
- here
-
-
-
)
diff --git a/src/state/queries/nuxs/definitions.ts b/src/state/queries/nuxs/definitions.ts
index 8166602c8a..0558fbf155 100644
--- a/src/state/queries/nuxs/definitions.ts
+++ b/src/state/queries/nuxs/definitions.ts
@@ -3,16 +3,16 @@ import zod from 'zod'
import {BaseNux} from '#/state/queries/nuxs/types'
export enum Nux {
- NeueTypography = 'NeueTypography',
+ BlueskyPlus_Core_RightNav = 'BlueskyPlus_Core_RightNav',
}
export const nuxNames = new Set(Object.values(Nux))
export type AppNux = BaseNux<{
- id: Nux.NeueTypography
+ id: Nux.BlueskyPlus_Core_RightNav
data: undefined
}>
export const NuxSchemas: Record | undefined> = {
- [Nux.NeueTypography]: undefined,
+ [Nux.BlueskyPlus_Core_RightNav]: undefined,
}
diff --git a/src/state/queries/nuxs/index.ts b/src/state/queries/nuxs/index.ts
index 94c1242301..6ad59c7a46 100644
--- a/src/state/queries/nuxs/index.ts
+++ b/src/state/queries/nuxs/index.ts
@@ -10,49 +10,78 @@ import {useAgent} from '#/state/session'
export {Nux} from '#/state/queries/nuxs/definitions'
-export function useNuxs() {
- const {data, isSuccess} = usePreferencesQuery()
+export function useNuxs():
+ | {
+ nuxs: AppNux[]
+ status: 'ready'
+ }
+ | {
+ nuxs: undefined
+ status: 'loading' | 'error'
+ } {
+ const {data, isSuccess, isError} = usePreferencesQuery()
+ const status = isSuccess ? 'ready' : isError ? 'error' : 'loading'
- if (data && isSuccess) {
- const nuxs = data.bskyAppState.nuxs
+ if (status === 'ready') {
+ const nuxs = data?.bskyAppState?.nuxs
?.map(parseAppNux)
?.filter(Boolean) as AppNux[]
if (nuxs) {
return {
nuxs,
- isSuccess,
+ status,
+ }
+ } else {
+ return {
+ nuxs: [],
+ status,
}
}
}
return {
nuxs: undefined,
- isSuccess,
+ status,
}
}
-export function useNux(id: T) {
- const {nuxs, isSuccess} = useNuxs()
+export function useNux(
+ id: T,
+):
+ | {
+ nux: Extract | undefined
+ status: 'ready'
+ }
+ | {
+ nux: undefined
+ status: 'loading' | 'error'
+ } {
+ const {nuxs, status} = useNuxs()
- if (nuxs && isSuccess) {
+ if (status === 'ready') {
const nux = nuxs.find(nux => nux.id === id)
if (nux) {
return {
nux: nux as Extract,
- isSuccess,
+ status,
+ }
+ } else {
+ return {
+ nux: undefined,
+ status,
}
}
}
return {
nux: undefined,
- isSuccess,
+ status,
}
}
-export function useUpsertNuxMutation() {
+export function useSaveNux() {
const queryClient = useQueryClient()
const agent = useAgent()
@@ -68,7 +97,7 @@ export function useUpsertNuxMutation() {
})
}
-export function useRemoveNuxsMutation() {
+export function useResetNuxs() {
const queryClient = useQueryClient()
const agent = useAgent()
diff --git a/src/view/shell/desktop/RightNav.tsx b/src/view/shell/desktop/RightNav.tsx
index 354f549db2..6c7a8157b7 100644
--- a/src/view/shell/desktop/RightNav.tsx
+++ b/src/view/shell/desktop/RightNav.tsx
@@ -8,6 +8,7 @@ import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries'
import {useKawaiiMode} from '#/state/preferences/kawaii'
import {usePurchases} from '#/state/purchases'
import {EntitlementId} from '#/state/purchases/types'
+import {Nux, useNux, useResetNuxs,useSaveNux} from '#/state/queries/nuxs'
import {useSession} from '#/state/session'
import {DesktopFeeds} from '#/view/shell/desktop/Feeds'
import {DesktopSearch} from '#/view/shell/desktop/Search'
@@ -19,21 +20,53 @@ import {Divider} from '#/components/Divider'
import {GradientFill} from '#/components/GradientFill'
import {Full as BlueskyPlusLogo} from '#/components/icons/BlueskyPlus'
import {SquareArrowTopRight_Stroke2_Corner0_Rounded as SquareArrowTopRight} from '#/components/icons/SquareArrowTopRight'
-import {InlineLinkText} from '#/components/Link'
+import {createStaticClick,InlineLinkText} from '#/components/Link'
import {ProgressGuideList} from '#/components/ProgressGuide/List'
import {Text} from '#/components/Typography'
+import {IS_DEV} from '#/env'
export function DesktopRightNav({routeName}: {routeName: string}) {
const t = useTheme()
const {_} = useLingui()
const {hasSession, currentAccount} = useSession()
- const subscriptionsDialogControl = useDialogControl()
- const purchases = usePurchases()
- const isSubscribed =
- purchases.status === 'ready' &&
- purchases.entitlements?.some(e => e.id === EntitlementId.Core)
-
const kawaii = useKawaiiMode()
+ const bskyPlusDialogControl = useDialogControl()
+
+ const {mutateAsync: resetNuxs} = useResetNuxs()
+ const nux = useNux(Nux.BlueskyPlus_Core_RightNav)
+ const {mutateAsync: saveNux} = useSaveNux()
+ const purchases = usePurchases()
+ const [hideNux, setHideNux] = React.useState(false)
+ const bskyPlusNux = React.useMemo(() => {
+ const purchasesReady = purchases.status === 'ready'
+ const nuxReady = nux.status === 'ready'
+ const ready = purchasesReady && nuxReady
+ const subscribed =
+ purchasesReady &&
+ purchases.entitlements.some(e => e.id === EntitlementId.Core)
+ const nuxSeen = Boolean(nuxReady && nux.nux)
+ const active = !ready
+ ? false
+ : subscribed || nuxSeen
+ ? false
+ : hideNux
+ ? false
+ : true
+ return {
+ active,
+ nuxSeen,
+ }
+ }, [hideNux, nux, purchases])
+
+ const onPressBlueskyPlusNux = () => {
+ setHideNux(true)
+ bskyPlusDialogControl.open()
+ saveNux({
+ id: Nux.BlueskyPlus_Core_RightNav,
+ completed: true,
+ data: undefined,
+ }).catch(_e => {})
+ }
const {isTablet} = useWebMediaQueries()
if (isTablet) {
@@ -72,11 +105,10 @@ export function DesktopRightNav({routeName}: {routeName: string}) {
>
)}
- {/* TODO NUX this */}
- {!isSubscribed && (
+ {bskyPlusNux.active ? (
-
+ ) : (
+ <>
+ {bskyPlusNux.nuxSeen && IS_DEV && (
+
+
+ Dev only:{' '}
+ {
+ setHideNux(false)
+ resetNuxs([Nux.BlueskyPlus_Core_RightNav])
+ })}>
+ reset Bluesky+ NUX
+
+
+
+ )}
+ >
)}
+
+
{hasSession && (
<>