diff --git a/src/state/queries/nudges/compression.ts b/src/state/queries/nudges/compression.ts new file mode 100644 index 0000000000..bc88b03c0b --- /dev/null +++ b/src/state/queries/nudges/compression.ts @@ -0,0 +1,26 @@ +const MAX_FLAGS = 300 +const FLAGS_PER_CHAR = 6 + +export function pack(flags: Record) { + const values = Object.values(flags) + if (values.length > MAX_FLAGS) { + throw new Error(`Too many flags. Maximum supported is ${MAX_FLAGS}`) + } + const compacted = values.map(value => value ? '1' : '0').join('') + // split into chunks of 6 values + const encoded = (compacted.match(/.{1,6}/g) || []) + .map(chunk => String.fromCharCode(parseInt(chunk.padEnd(6, '0'), 2))) + .join('') + return encoded +} + +export function unpack(encoded: string, keys: K[]) { + const binary = encoded.split('') + .map(char => char.charCodeAt(0).toString(2).padEnd(6, '0')) + .join('') + return keys.reduce((obj, key, i) => { + obj[key] = binary[i] === '1' + return obj + }, {} as Record) +} + diff --git a/src/state/queries/nudges/flags.ts b/src/state/queries/nudges/flags.ts new file mode 100644 index 0000000000..99c2a17d13 --- /dev/null +++ b/src/state/queries/nudges/flags.ts @@ -0,0 +1,21 @@ +export enum Group { + Wayfinding = 'wayfinding', + Composer = 'composer', +} + +export type Flags = { + 'wayfinding:settings': boolean + 'wayfinding:settings:moderation': boolean + 'composer:threadgate_20250204': boolean +} + +export type Flag = keyof Flags + +// existing users? +export const flags: Flags = { + 'wayfinding:settings': true, + 'wayfinding:settings:moderation': true, + 'composer:threadgate_20250204': false, +} + +export const flagKeys = Object.keys(flags) as (keyof typeof flags)[] diff --git a/src/state/queries/nudges/index.ts b/src/state/queries/nudges/index.ts new file mode 100644 index 0000000000..8b53501f6a --- /dev/null +++ b/src/state/queries/nudges/index.ts @@ -0,0 +1,36 @@ +import {useMemo, useCallback} from 'react' + +import {flags, flagKeys, Flag} from '#/state/queries/nudges/flags' +import {pack, unpack} from '#/state/queries/nudges/compression' + +const TEST = pack(flags) + +export function useNudges() { + const data = TEST // TODO + return useMemo(() => { + return data ? unpack(data, flagKeys) : flags + }, [data]) +} + +export function useNudgesGroup() { + // TODO +} + +export function useNudge(flag: Flag) { + const nudges = useNudges() + const nudge = nudges[flag] + const complete = useCallback(() => { + const next = {...nudges} + next[flag] = true + const data = pack(next) + // TODO update + }, [nudge]) + const reset = useCallback(() => { + const next = {...nudges} + next[flag] = false + const data = pack(next) + // TODO update + }, [nudge]) + return useMemo(() => ({ + }), [nudge, complete, reset]) +} diff --git a/src/state/queries/nuxs/definitions.ts b/src/state/queries/nuxs/definitions.ts index a44ffa4c54..772aeeaaff 100644 --- a/src/state/queries/nuxs/definitions.ts +++ b/src/state/queries/nuxs/definitions.ts @@ -7,7 +7,6 @@ export enum Nux { ExploreInterestsCard = 'ExploreInterestsCard', InitialVerificationAnnouncement = 'InitialVerificationAnnouncement', } - export const nuxNames = new Set(Object.values(Nux)) export type AppNux = BaseNux< @@ -25,6 +24,14 @@ export type AppNux = BaseNux< } > +/** + * Nuxes that have been retired and should be cleared from storage. + */ +export enum RetiredNux { + NeueTypography = 'NeueTypography', +} +export const retiredNuxNames = Object.values(RetiredNux) + export const NuxSchemas: Record | undefined> = { [Nux.NeueTypography]: undefined, [Nux.ExploreInterestsCard]: undefined, diff --git a/src/state/queries/nuxs/index.ts b/src/state/queries/nuxs/index.ts index 6ad59c7a46..ebfa6983c6 100644 --- a/src/state/queries/nuxs/index.ts +++ b/src/state/queries/nuxs/index.ts @@ -1,6 +1,6 @@ import {useMutation, useQueryClient} from '@tanstack/react-query' -import {AppNux, Nux} from '#/state/queries/nuxs/definitions' +import {AppNux, Nux, retiredNuxNames} from '#/state/queries/nuxs/definitions' import {parseAppNux, serializeAppNux} from '#/state/queries/nuxs/util' import { preferencesQueryKey, @@ -93,6 +93,8 @@ export function useSaveNux() { await queryClient.invalidateQueries({ queryKey: preferencesQueryKey, }) + // do not await, just cleaning up + agent.bskyAppRemoveNuxs(retiredNuxNames).catch(() => {}) }, }) }