Track starter pack opt-out actions

This commit is contained in:
vineyardbovines
2026-09-02 14:33:11 -04:00
parent 004e489592
commit 84444be047
4 changed files with 23 additions and 7 deletions
+4
View File
@@ -715,6 +715,10 @@ export type Events = {
count: number count: number
} }
'starterPack:delete': {} 'starterPack:delete': {}
'starterPack:optOut': {
starterPack: string
action: 'optOut' | 'undo'
}
'starterPack:create': { 'starterPack:create': {
setName: boolean setName: boolean
setDescription: boolean setDescription: boolean
@@ -552,6 +552,12 @@ function OverflowMenu({
const {mutate: setReferenceListOptOut, isPending: isOptOutPending} = const {mutate: setReferenceListOptOut, isPending: isOptOutPending} =
useReferenceListOptOutMutation({ useReferenceListOptOutMutation({
starterPack, starterPack,
onSuccess: action => {
ax.metric('starterPack:optOut', {
starterPack: starterPack.uri,
action,
})
},
onError: error => { onError: error => {
logger.error('Failed to update starter pack opt-out', { logger.error('Failed to update starter pack opt-out', {
safeMessage: error, safeMessage: error,
@@ -34,7 +34,7 @@ const createdOptOut =
const indexedOptOut = const indexedOptOut =
'at://did:plc:viewer/app.bsky.graph.referencelistoptout/indexed' 'at://did:plc:viewer/app.bsky.graph.referencelistoptout/indexed'
function setup() { function setup({onSuccess = jest.fn()} = {}) {
const queryClient = new QueryClient({ const queryClient = new QueryClient({
defaultOptions: { defaultOptions: {
queries: {gcTime: Infinity, retry: false}, queries: {gcTime: Infinity, retry: false},
@@ -57,11 +57,11 @@ function setup() {
<QueryClientProvider client={queryClient}>{children}</QueryClientProvider> <QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
) )
const hook = renderHook( const hook = renderHook(
() => useReferenceListOptOutMutation({starterPack, onError}), () => useReferenceListOptOutMutation({starterPack, onError, onSuccess}),
{wrapper}, {wrapper},
) )
return {appviewClient, hook, onError, pdsClient, queryClient} return {appviewClient, hook, onError, onSuccess, pdsClient, queryClient}
} }
beforeEach(() => { beforeEach(() => {
@@ -76,7 +76,7 @@ beforeAll(() => {
describe('useReferenceListOptOutMutation', () => { describe('useReferenceListOptOutMutation', () => {
it('keeps the successful PDS write optimistic when AppView has not caught up', async () => { it('keeps the successful PDS write optimistic when AppView has not caught up', async () => {
const {hook, pdsClient, queryClient} = setup() const {hook, onSuccess, pdsClient, queryClient} = setup()
pdsClient.create.mockResolvedValue({uri: createdOptOut}) pdsClient.create.mockResolvedValue({uri: createdOptOut})
jest.mocked(until).mockResolvedValue(false) jest.mocked(until).mockResolvedValue(false)
@@ -92,6 +92,7 @@ describe('useReferenceListOptOutMutation', () => {
queryClient.getQueryData<app.bsky.graph.defs.StarterPackView>(queryKey) queryClient.getQueryData<app.bsky.graph.defs.StarterPackView>(queryKey)
?.list?.viewer?.referenceListOptOut, ?.list?.viewer?.referenceListOptOut,
).toBe(createdOptOut) ).toBe(createdOptOut)
expect(onSuccess).toHaveBeenCalledWith('optOut')
}) })
it('uses the indexed viewer-state URI when AppView reports a duplicate', async () => { it('uses the indexed viewer-state URI when AppView reports a duplicate', async () => {
@@ -121,7 +122,7 @@ describe('useReferenceListOptOutMutation', () => {
}) })
it('deletes the viewer-state record URI when undoing', async () => { it('deletes the viewer-state record URI when undoing', async () => {
const {hook, pdsClient, queryClient} = setup() const {hook, onSuccess, pdsClient, queryClient} = setup()
queryClient.setQueryData(queryKey, { queryClient.setQueryData(queryKey, {
...starterPack, ...starterPack,
list: { list: {
@@ -148,10 +149,11 @@ describe('useReferenceListOptOutMutation', () => {
queryClient.getQueryData<app.bsky.graph.defs.StarterPackView>(queryKey) queryClient.getQueryData<app.bsky.graph.defs.StarterPackView>(queryKey)
?.list?.viewer?.referenceListOptOut, ?.list?.viewer?.referenceListOptOut,
).toBeUndefined() ).toBeUndefined()
expect(onSuccess).toHaveBeenCalledWith('undo')
}) })
it('restores viewer state and surfaces PDS write failures', async () => { it('restores viewer state and surfaces PDS write failures', async () => {
const {hook, onError, pdsClient, queryClient} = setup() const {hook, onError, onSuccess, pdsClient, queryClient} = setup()
const error = new Error('write failed') const error = new Error('write failed')
pdsClient.create.mockRejectedValue(error) pdsClient.create.mockRejectedValue(error)
@@ -162,6 +164,7 @@ describe('useReferenceListOptOutMutation', () => {
}) })
await waitFor(() => expect(onError).toHaveBeenCalledWith(error)) await waitFor(() => expect(onError).toHaveBeenCalledWith(error))
expect(onSuccess).not.toHaveBeenCalled()
expect( expect(
queryClient.getQueryData<app.bsky.graph.defs.StarterPackView>(queryKey) queryClient.getQueryData<app.bsky.graph.defs.StarterPackView>(queryKey)
?.list?.viewer?.referenceListOptOut, ?.list?.viewer?.referenceListOptOut,
+4 -1
View File
@@ -74,9 +74,11 @@ export function useStarterPackQuery({
export function useReferenceListOptOutMutation({ export function useReferenceListOptOutMutation({
starterPack, starterPack,
onError, onError,
onSuccess,
}: { }: {
starterPack: app.bsky.graph.defs.StarterPackView starterPack: app.bsky.graph.defs.StarterPackView
onError: (error: Error) => void onError: (error: Error) => void
onSuccess?: (action: 'optOut' | 'undo') => void
}) { }) {
const queryClient = useQueryClient() const queryClient = useQueryClient()
const appviewClient = useAppviewClient() const appviewClient = useAppviewClient()
@@ -171,7 +173,7 @@ export function useReferenceListOptOutMutation({
) )
return {previous} return {previous}
}, },
onSuccess: ({referenceListOptOut}) => { onSuccess: ({referenceListOptOut}, variables) => {
queryClient.setQueryData<app.bsky.graph.defs.StarterPackView>( queryClient.setQueryData<app.bsky.graph.defs.StarterPackView>(
queryKey, queryKey,
current => current =>
@@ -192,6 +194,7 @@ export function useReferenceListOptOutMutation({
queryClient, queryClient,
uri: starterPack.list!.uri, uri: starterPack.list!.uri,
}) })
onSuccess?.(variables.referenceListOptOut ? 'undo' : 'optOut')
}, },
onError: (error, _, context) => { onError: (error, _, context) => {
if (context?.previous) { if (context?.previous) {