about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/state/queries/nuxs/definitions.ts29
-rw-r--r--src/state/queries/nuxs/index.ts83
-rw-r--r--src/state/queries/nuxs/types.ts9
-rw-r--r--src/state/queries/nuxs/util.ts52
-rw-r--r--src/state/queries/preferences/const.ts1
5 files changed, 174 insertions, 0 deletions
diff --git a/src/state/queries/nuxs/definitions.ts b/src/state/queries/nuxs/definitions.ts
new file mode 100644
index 000000000..c5cb1e9d9
--- /dev/null
+++ b/src/state/queries/nuxs/definitions.ts
@@ -0,0 +1,29 @@
+import zod from 'zod'
+
+import {BaseNux} from '#/state/queries/nuxs/types'
+
+export enum Nux {
+  One = 'one',
+  Two = 'two',
+}
+
+export const nuxNames = new Set(Object.values(Nux))
+
+export type AppNux =
+  | BaseNux<{
+      id: Nux.One
+      data: {
+        likes: number
+      }
+    }>
+  | BaseNux<{
+      id: Nux.Two
+      data: undefined
+    }>
+
+export const NuxSchemas = {
+  [Nux.One]: zod.object({
+    likes: zod.number(),
+  }),
+  [Nux.Two]: undefined,
+}
diff --git a/src/state/queries/nuxs/index.ts b/src/state/queries/nuxs/index.ts
new file mode 100644
index 000000000..2945e67eb
--- /dev/null
+++ b/src/state/queries/nuxs/index.ts
@@ -0,0 +1,83 @@
+import {useMutation, useQueryClient} from '@tanstack/react-query'
+
+import {AppNux, Nux} from '#/state/queries/nuxs/definitions'
+import {parseAppNux, serializeAppNux} from '#/state/queries/nuxs/util'
+import {
+  preferencesQueryKey,
+  usePreferencesQuery,
+} from '#/state/queries/preferences'
+import {useAgent} from '#/state/session'
+
+export {Nux} from '#/state/queries/nuxs/definitions'
+
+export function useNuxs() {
+  const {data, ...rest} = usePreferencesQuery()
+
+  if (data && rest.isSuccess) {
+    const nuxs = data.bskyAppState.nuxs
+      ?.map(parseAppNux)
+      ?.filter(Boolean) as AppNux[]
+
+    if (nuxs) {
+      return {
+        nuxs,
+        ...rest,
+      }
+    }
+  }
+
+  return {
+    nuxs: undefined,
+    ...rest,
+  }
+}
+
+export function useNux<T extends Nux>(id: T) {
+  const {nuxs, ...rest} = useNuxs()
+
+  if (nuxs && rest.isSuccess) {
+    const nux = nuxs.find(nux => nux.id === id)
+
+    if (nux) {
+      return {
+        nux: nux as Extract<AppNux, {id: T}>,
+        ...rest,
+      }
+    }
+  }
+
+  return {
+    nux: undefined,
+    ...rest,
+  }
+}
+
+export function useUpsertNuxMutation() {
+  const queryClient = useQueryClient()
+  const agent = useAgent()
+
+  return useMutation({
+    mutationFn: async (nux: AppNux) => {
+      await agent.bskyAppUpsertNux(serializeAppNux(nux))
+      // triggers a refetch
+      await queryClient.invalidateQueries({
+        queryKey: preferencesQueryKey,
+      })
+    },
+  })
+}
+
+export function useRemoveNuxsMutation() {
+  const queryClient = useQueryClient()
+  const agent = useAgent()
+
+  return useMutation({
+    mutationFn: async (ids: string[]) => {
+      await agent.bskyAppRemoveNuxs(ids)
+      // triggers a refetch
+      await queryClient.invalidateQueries({
+        queryKey: preferencesQueryKey,
+      })
+    },
+  })
+}
diff --git a/src/state/queries/nuxs/types.ts b/src/state/queries/nuxs/types.ts
new file mode 100644
index 000000000..5b7918470
--- /dev/null
+++ b/src/state/queries/nuxs/types.ts
@@ -0,0 +1,9 @@
+import {AppBskyActorDefs} from '@atproto/api'
+
+export type Data = Record<string, unknown> | undefined
+
+export type BaseNux<
+  T extends Pick<AppBskyActorDefs.Nux, 'id' | 'expiresAt'> & {data: Data},
+> = T & {
+  completed: boolean
+}
diff --git a/src/state/queries/nuxs/util.ts b/src/state/queries/nuxs/util.ts
new file mode 100644
index 000000000..d65b86a34
--- /dev/null
+++ b/src/state/queries/nuxs/util.ts
@@ -0,0 +1,52 @@
+import {AppBskyActorDefs, nuxSchema} from '@atproto/api'
+
+import {
+  AppNux,
+  Nux,
+  nuxNames,
+  NuxSchemas,
+} from '#/state/queries/nuxs/definitions'
+
+export function parseAppNux(nux: AppBskyActorDefs.Nux): AppNux | undefined {
+  if (!nuxNames.has(nux.id as Nux)) return
+  if (!nuxSchema.safeParse(nux).success) return
+
+  const {data, ...rest} = nux
+
+  const schema = NuxSchemas[nux.id as Nux]
+
+  if (schema && data) {
+    const parsedData = JSON.parse(data)
+
+    if (!schema.safeParse(parsedData).success) return
+
+    return {
+      ...rest,
+      data: parsedData,
+    } as AppNux
+  }
+
+  return {
+    ...rest,
+    data: undefined,
+  } as AppNux
+}
+
+export function serializeAppNux(nux: AppNux): AppBskyActorDefs.Nux {
+  const {data, ...rest} = nux
+  const schema = NuxSchemas[nux.id as Nux]
+
+  const result: AppBskyActorDefs.Nux = {
+    ...rest,
+    data: undefined,
+  }
+
+  if (schema) {
+    schema.parse(data)
+    result.data = JSON.stringify(data)
+  }
+
+  nuxSchema.parse(result)
+
+  return result
+}
diff --git a/src/state/queries/preferences/const.ts b/src/state/queries/preferences/const.ts
index 1ae7d2068..e07f40ec5 100644
--- a/src/state/queries/preferences/const.ts
+++ b/src/state/queries/preferences/const.ts
@@ -37,5 +37,6 @@ export const DEFAULT_LOGGED_OUT_PREFERENCES: UsePreferencesQueryResponse = {
   bskyAppState: {
     queuedNudges: [],
     activeProgressGuide: undefined,
+    nuxs: [],
   },
 }