diff options
author | Samuel Newman <mozzius@protonmail.com> | 2025-08-08 01:01:01 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2025-08-07 17:01:01 -0500 |
commit | 93c4719a2140070b33f69dd0f12b4de2619a25a6 (patch) | |
tree | 0396daa890b1023097385748f2194b16d1fa6fa4 /src/state/queries/handle-availability.ts | |
parent | f708e884107c75559759b22901c87e57d5b979da (diff) | |
download | voidsky-93c4719a2140070b33f69dd0f12b4de2619a25a6.tar.zst |
Check handle as you type (#8601)
* check handle as you type * metrics * add metric types * fix overflow * only check reserved handles for bsky.social, fix test * change validation check name * tweak input * move ghosttext component to textfield * tweak styles to try and match latest * add suggestions * improvements, metrics * share logic between typeahead and next button * Apply suggestions from code review Co-authored-by: surfdude29 <149612116+surfdude29@users.noreply.github.com> * update checks, disable button if unavailable * convert to lowercase * fix bug with checkHandleAvailability * add gate * move files around to make clearer * fix bad import * Fix flashing next button * Enable for TF --------- Co-authored-by: surfdude29 <149612116+surfdude29@users.noreply.github.com> Co-authored-by: Hailey <me@haileyok.com> Co-authored-by: Eric Bailey <git@esb.lol>
Diffstat (limited to 'src/state/queries/handle-availability.ts')
-rw-r--r-- | src/state/queries/handle-availability.ts | 126 |
1 files changed, 126 insertions, 0 deletions
diff --git a/src/state/queries/handle-availability.ts b/src/state/queries/handle-availability.ts new file mode 100644 index 000000000..9391f5d09 --- /dev/null +++ b/src/state/queries/handle-availability.ts @@ -0,0 +1,126 @@ +import {Agent, ComAtprotoTempCheckHandleAvailability} from '@atproto/api' +import {useQuery} from '@tanstack/react-query' + +import { + BSKY_SERVICE, + BSKY_SERVICE_DID, + PUBLIC_BSKY_SERVICE, +} from '#/lib/constants' +import {createFullHandle} from '#/lib/strings/handles' +import {logger} from '#/logger' +import {useDebouncedValue} from '#/components/live/utils' +import * as bsky from '#/types/bsky' + +export const RQKEY_handleAvailability = ( + handle: string, + domain: string, + serviceDid: string, +) => ['handle-availability', {handle, domain, serviceDid}] + +export function useHandleAvailabilityQuery( + { + username, + serviceDomain, + serviceDid, + enabled, + birthDate, + email, + }: { + username: string + serviceDomain: string + serviceDid: string + enabled: boolean + birthDate?: string + email?: string + }, + debounceDelayMs = 500, +) { + const name = username.trim() + const debouncedHandle = useDebouncedValue(name, debounceDelayMs) + + return { + debouncedUsername: debouncedHandle, + enabled: enabled && name === debouncedHandle, + query: useQuery({ + enabled: enabled && name === debouncedHandle, + queryKey: RQKEY_handleAvailability( + debouncedHandle, + serviceDomain, + serviceDid, + ), + queryFn: async () => { + const handle = createFullHandle(name, serviceDomain) + return await checkHandleAvailability(handle, serviceDid, { + email, + birthDate, + typeahead: true, + }) + }, + }), + } +} + +export async function checkHandleAvailability( + handle: string, + serviceDid: string, + { + email, + birthDate, + typeahead, + }: { + email?: string + birthDate?: string + typeahead?: boolean + }, +) { + if (serviceDid === BSKY_SERVICE_DID) { + const agent = new Agent({service: BSKY_SERVICE}) + // entryway has a special API for handle availability + const {data} = await agent.com.atproto.temp.checkHandleAvailability({ + handle, + birthDate, + email, + }) + + if ( + bsky.dangerousIsType<ComAtprotoTempCheckHandleAvailability.ResultAvailable>( + data.result, + ComAtprotoTempCheckHandleAvailability.isResultAvailable, + ) + ) { + logger.metric('signup:handleAvailable', {typeahead}, {statsig: true}) + + return {available: true} as const + } else if ( + bsky.dangerousIsType<ComAtprotoTempCheckHandleAvailability.ResultUnavailable>( + data.result, + ComAtprotoTempCheckHandleAvailability.isResultUnavailable, + ) + ) { + logger.metric('signup:handleTaken', {typeahead}, {statsig: true}) + return { + available: false, + suggestions: data.result.suggestions, + } as const + } else { + throw new Error( + `Unexpected result of \`checkHandleAvailability\`: ${JSON.stringify(data.result)}`, + ) + } + } else { + // 3rd party PDSes won't have this API so just try and resolve the handle + const agent = new Agent({service: PUBLIC_BSKY_SERVICE}) + try { + const res = await agent.resolveHandle({ + handle, + }) + + if (res.data.did) { + logger.metric('signup:handleTaken', {typeahead}, {statsig: true}) + return {available: false} as const + } + } catch {} + logger.metric('signup:handleAvailable', {typeahead}, {statsig: true}) + return {available: true} as const + } +} |