improvements, metrics

This commit is contained in:
Samuel Newman
2025-08-04 14:42:17 +03:00
parent 8f88fd5b2f
commit fc96b8f8a0
3 changed files with 104 additions and 79 deletions
+1
View File
@@ -70,6 +70,7 @@ export type MetricEvents = {
'signup:handleTaken': {typeahead?: boolean} 'signup:handleTaken': {typeahead?: boolean}
'signup:handleReserved': {typeahead?: boolean} 'signup:handleReserved': {typeahead?: boolean}
'signup:handleAvailable': {typeahead?: boolean} 'signup:handleAvailable': {typeahead?: boolean}
'signup:handleSuggestionSelected': {method: string}
'signin:hostingProviderPressed': { 'signin:hostingProviderPressed': {
hostingProviderDidChange: boolean hostingProviderDidChange: boolean
} }
+41 -20
View File
@@ -1,10 +1,10 @@
import {useState} from 'react' import {useState} from 'react'
import {View} from 'react-native' import {View} from 'react-native'
import Animated, { import Animated, {
Easing,
FadeIn, FadeIn,
FadeInDown, FadeInDown,
FadeOut, FadeOut,
FadeOutDown,
LayoutAnimationConfig, LayoutAnimationConfig,
LinearTransition, LinearTransition,
} from 'react-native-reanimated' } from 'react-native-reanimated'
@@ -42,7 +42,10 @@ export function StepHandle() {
const validCheck = validateServiceHandle(draftValue, state.userDomain) const validCheck = validateServiceHandle(draftValue, state.userDomain)
const {data: isHandleAvailable, isLoading} = useHandleAvailabilityQuery({ const {
enabled: queryEnabled,
query: {data: isHandleAvailable, isPending},
} = useHandleAvailabilityQuery({
username: draftValue, username: draftValue,
serviceDid: state.serviceDescription?.did ?? 'UNKNOWN', serviceDid: state.serviceDescription?.did ?? 'UNKNOWN',
serviceDomain: state.userDomain, serviceDomain: state.userDomain,
@@ -121,7 +124,10 @@ export function StepHandle() {
} }
const textFieldInvalid = const textFieldInvalid =
(!isLoading && isHandleAvailable && !isHandleAvailable.available) || (!isPending &&
queryEnabled &&
isHandleAvailable &&
!isHandleAvailable.available) ||
!validCheck.frontLengthNotTooLong || !validCheck.frontLengthNotTooLong ||
!validCheck.handleChars || !validCheck.handleChars ||
!validCheck.hyphenStartOrEnd || !validCheck.hyphenStartOrEnd ||
@@ -129,7 +135,7 @@ export function StepHandle() {
return ( return (
<ScreenTransition> <ScreenTransition>
<View style={[a.gap_sm, a.pt_lg]}> <View style={[a.gap_sm, a.pt_lg, a.z_10]}>
<View> <View>
<TextField.Root isInvalid={textFieldInvalid}> <TextField.Root isInvalid={textFieldInvalid}>
<TextField.Icon icon={AtIcon} /> <TextField.Icon icon={AtIcon} />
@@ -142,7 +148,7 @@ export function StepHandle() {
setDraftValue(val) setDraftValue(val)
}} }}
label={state.userDomain} label={state.userDomain}
defaultValue={draftValue} value={draftValue}
keyboardType="ascii-capable" // fix for iOS replacing -- with — keyboardType="ascii-capable" // fix for iOS replacing -- with —
autoCapitalize="none" autoCapitalize="none"
autoCorrect={false} autoCorrect={false}
@@ -166,7 +172,8 @@ export function StepHandle() {
<RequirementText>{state.error}</RequirementText> <RequirementText>{state.error}</RequirementText>
</Requirement> </Requirement>
)} )}
{isHandleAvailable && {queryEnabled &&
isHandleAvailable &&
!isHandleAvailable.available && !isHandleAvailable.available &&
validCheck.overall && ( validCheck.overall && (
<> <>
@@ -189,7 +196,9 @@ export function StepHandle() {
state.userDomain.length * -1, state.userDomain.length * -1,
), ),
) )
// TODO: add logging logger.metric('signup:handleSuggestionSelected', {
method: suggestion.method,
})
}} }}
/> />
)} )}
@@ -225,12 +234,14 @@ export function StepHandle() {
</View> </View>
</LayoutAnimationConfig> </LayoutAnimationConfig>
</View> </View>
<BackNextButtons <Animated.View layout={native(LinearTransition)}>
isLoading={isNextLoading} <BackNextButtons
isNextDisabled={!validCheck.overall || !!state.error} isLoading={isNextLoading}
onBackPress={onBackPress} isNextDisabled={!validCheck.overall || !!state.error}
onNextPress={onNextPress} onBackPress={onBackPress}
/> onNextPress={onNextPress}
/>
</Animated.View>
</ScreenTransition> </ScreenTransition>
) )
} }
@@ -270,33 +281,43 @@ function HandleSuggestions({
return ( return (
<Animated.View <Animated.View
entering={FadeInDown} entering={native(FadeInDown.easing(Easing.out(Easing.exp)))}
exiting={FadeOutDown} exiting={native(FadeOut)}
style={[ style={[
a.flex_1, a.flex_1,
a.border, a.border,
a.rounded_sm, a.rounded_sm,
a.shadow_md, t.atoms.shadow_sm,
t.atoms.bg, t.atoms.bg,
t.atoms.border_contrast_low, t.atoms.border_contrast_low,
a.mt_xs,
a.z_50,
a.w_full,
a.zoom_fade_in,
]}> ]}>
{suggestions.map((suggestion, index) => ( {suggestions.map((suggestion, index) => (
<Button <Button
label={_(msg`Select ${suggestion.handle}`)} label={_(msg`Select ${suggestion.handle}`)}
key={index} key={index}
onPress={() => onSelect(suggestion)} onPress={() => onSelect(suggestion)}
hoverStyle={[t.atoms.bg_contrast_25]}
style={[ style={[
a.w_full, a.w_full,
a.flex_row, a.flex_row,
a.align_center, a.align_center,
a.justify_between, a.justify_between,
a.px_md, a.p_md,
a.py_lg,
a.border_b, a.border_b,
t.atoms.border_contrast_low, t.atoms.border_contrast_low,
index === 0 && {borderTopEndRadius: borderRadius.sm}, index === 0 && {
borderTopStartRadius: borderRadius.sm,
borderTopEndRadius: borderRadius.sm,
},
index === suggestions.length - 1 && [ index === suggestions.length - 1 && [
{borderBottomEndRadius: borderRadius.sm}, {
borderBottomStartRadius: borderRadius.sm,
borderBottomEndRadius: borderRadius.sm,
},
a.border_b_0, a.border_b_0,
], ],
]}> ]}>
+62 -59
View File
@@ -16,7 +16,7 @@ export const RQKEY_handleAvailability = (
handle: string, handle: string,
domain: string, domain: string,
serviceDid: string, serviceDid: string,
) => ['handle-availability', handle, domain, serviceDid] ) => ['handle-availability', {handle, domain, serviceDid}]
export function useHandleAvailabilityQuery( export function useHandleAvailabilityQuery(
{ {
@@ -46,68 +46,71 @@ export function useHandleAvailabilityQuery(
} }
}, [serviceDid]) }, [serviceDid])
return useQuery({ return {
enabled: enabled && name === debouncedHandle, enabled: enabled && name === debouncedHandle,
queryKey: RQKEY_handleAvailability( query: useQuery({
debouncedHandle, enabled: enabled && name === debouncedHandle,
serviceDomain, queryKey: RQKEY_handleAvailability(
serviceDid, debouncedHandle,
), serviceDomain,
queryFn: async () => { serviceDid,
const handle = createFullHandle(name, serviceDomain) ),
if (serviceDid === BSKY_SERVICE_DID) { queryFn: async () => {
// entryway has a special API for handle availability const handle = createFullHandle(name, serviceDomain)
const {data} = await agent.com.atproto.temp.checkHandleAvailability({ if (serviceDid === BSKY_SERVICE_DID) {
handle, // entryway has a special API for handle availability
birthDate, const {data} = await agent.com.atproto.temp.checkHandleAvailability({
email,
})
if (
bsky.dangerousIsType<ComAtprotoTempCheckHandleAvailability.ResultAvailable>(
data.result,
ComAtprotoTempCheckHandleAvailability.isResultAvailable,
)
) {
return {available: true} as const
} else if (
bsky.dangerousIsType<ComAtprotoTempCheckHandleAvailability.ResultUnavailable>(
data.result,
ComAtprotoTempCheckHandleAvailability.isResultUnavailable,
)
) {
return {
available: false,
suggestions: data.result.suggestions,
} as const
} else {
throw new Error(
`Unexpected result of \`checkHandleAvailability\`: ${JSON.stringify(data.result)}`,
)
}
} else {
// 3rd party services won't have this API so just try and resolve the handle
try {
const res = await agent.resolveHandle({
handle, handle,
birthDate,
email,
}) })
if (res.data.did) { if (
logger.metric( bsky.dangerousIsType<ComAtprotoTempCheckHandleAvailability.ResultAvailable>(
'signup:handleReserved', data.result,
{typeahead: true}, ComAtprotoTempCheckHandleAvailability.isResultAvailable,
{statsig: true}, )
) {
return {available: true} as const
} else if (
bsky.dangerousIsType<ComAtprotoTempCheckHandleAvailability.ResultUnavailable>(
data.result,
ComAtprotoTempCheckHandleAvailability.isResultUnavailable,
)
) {
return {
available: false,
suggestions: data.result.suggestions,
} as const
} else {
throw new Error(
`Unexpected result of \`checkHandleAvailability\`: ${JSON.stringify(data.result)}`,
) )
return {available: false} as const
} }
} catch {} } else {
logger.metric( // 3rd party services won't have this API so just try and resolve the handle
'signup:handleAvailable', try {
{typeahead: true}, const res = await agent.resolveHandle({
{statsig: true}, handle,
) })
return {available: true} as const
} if (res.data.did) {
}, logger.metric(
}) 'signup:handleReserved',
{typeahead: true},
{statsig: true},
)
return {available: false} as const
}
} catch {}
logger.metric(
'signup:handleAvailable',
{typeahead: true},
{statsig: true},
)
return {available: true} as const
}
},
}),
}
} }