Compare commits

...

4 Commits

Author SHA1 Message Date
Eric Bailey 2ce22fa3f2 Sketch cleanup 2025-06-20 14:02:45 -05:00
Eric Bailey 6c689a3c7c Update compression file 2025-06-20 11:50:55 -05:00
Eric Bailey 6fcae251fe Remove cleanup 2025-06-20 11:32:44 -05:00
Eric Bailey b8c804fe84 WIP 2025-06-20 11:32:17 -05:00
5 changed files with 105 additions and 2 deletions
+55
View File
@@ -0,0 +1,55 @@
/*
* This module provides functions to pack and unpack flags into a compact
* binary format for storage in our NUX system. Given the max length of 300
* characters, and a 6-bit encoding scheme, we can store up to 1800 flags in a
* single string.
*/
const TRUTHY = '1'
const FALSY = '0'
/**
* Total number of characters that flags can be packed. This corresponds to the
* max-length of a NUX.
*/
const MAX_FLAGS = 300
/**
* Length of binary string chunks used to encode flags.
*/
const CHAR_CODE_LENGTH = 6
export function pack(flags: Record<string, boolean>) {
const values = Object.values(flags)
if (values.length > MAX_FLAGS) {
throw new Error(`Too many flags. Maximum supported is ${MAX_FLAGS}`)
}
/*
* Splits `compactBinaryNotation` string into 6 char chunks of 0s and 1s.
*/
const compactBinaryNotation = values
.map(value => (value ? TRUTHY : FALSY))
.join('')
const matcher = new RegExp(`.{1,${CHAR_CODE_LENGTH}}`, 'g')
const encoded = (compactBinaryNotation.match(matcher) || [])
.map(chunk =>
String.fromCharCode(parseInt(chunk.padEnd(CHAR_CODE_LENGTH, '0'), 2)),
)
.join('')
return encoded
}
export function unpack<K extends string>(encoded: string, keys: K[]) {
const compactBinaryNotation = encoded
.split('')
.map(char => char.charCodeAt(0).toString(2).padEnd(CHAR_CODE_LENGTH, '0'))
.join('')
return keys.reduce((obj, key, i) => {
obj[key] = compactBinaryNotation[i] === TRUTHY
return obj
}, {} as Record<K, boolean>)
}
+12
View File
@@ -0,0 +1,12 @@
/**
* NEVER CHANGE THE ORDER OF THIS MAP, the compression step depends on the
* other of the keys!
*/
export const flags = {
newThreadgate: false,
activitySubscriptionAlert: false,
} as const
export type Flag = keyof typeof flags
export const flagKeys = Object.keys(flags) as (keyof typeof flags)[]
+37
View File
@@ -0,0 +1,37 @@
import {useCallback, useMemo} from 'react'
import {pack, unpack} from '#/state/queries/nudges/compression'
import {type Flag, flagKeys, flags} from '#/state/queries/nudges/flags'
export function useNudges() {
const data = pack(flags) // TODO fetch from API
return useMemo(() => {
return data ? unpack(data, flagKeys) : flags
}, [data])
}
export function useNudge(flag: Flag) {
const nudges = useNudges()
const complete = useCallback(() => {
const next = {...nudges}
next[flag] = true
pack(next) // TODO write to API
}, [flag, nudges])
const reset = useCallback(() => {
const next = {...nudges}
next[flag] = false
pack(next) // TODO write to API
}, [flag, nudges])
return useMemo(
() => ({
nudge: nudges[flag],
complete,
reset,
}),
[flag, nudges, complete, reset],
)
}
-1
View File
@@ -7,7 +7,6 @@ export enum Nux {
ExploreInterestsCard = 'ExploreInterestsCard',
InitialVerificationAnnouncement = 'InitialVerificationAnnouncement',
}
export const nuxNames = new Set(Object.values(Nux))
export type AppNux = BaseNux<
+1 -1
View File
@@ -1,6 +1,6 @@
import {useMutation, useQueryClient} from '@tanstack/react-query'
import {AppNux, Nux} from '#/state/queries/nuxs/definitions'
import {type AppNux, type Nux} from '#/state/queries/nuxs/definitions'
import {parseAppNux, serializeAppNux} from '#/state/queries/nuxs/util'
import {
preferencesQueryKey,