hacking
This commit is contained in:
@@ -13,5 +13,6 @@ export type Gate =
|
|||||||
| 'suggested_feeds_interstitial'
|
| 'suggested_feeds_interstitial'
|
||||||
| 'suggested_follows_interstitial'
|
| 'suggested_follows_interstitial'
|
||||||
| 'ungroup_follow_backs'
|
| 'ungroup_follow_backs'
|
||||||
|
| 'use_oauth'
|
||||||
| 'videos'
|
| 'videos'
|
||||||
| 'small_avi_thumb'
|
| 'small_avi_thumb'
|
||||||
|
|||||||
@@ -10,6 +10,12 @@ import {
|
|||||||
ComAtprotoServerCreateSession,
|
ComAtprotoServerCreateSession,
|
||||||
ComAtprotoServerDescribeServer,
|
ComAtprotoServerDescribeServer,
|
||||||
} from '@atproto/api'
|
} from '@atproto/api'
|
||||||
|
import {
|
||||||
|
AuthorizeOptions,
|
||||||
|
BrowserOAuthClient,
|
||||||
|
LoginContinuedInParentWindowError,
|
||||||
|
OAuthAgent,
|
||||||
|
} from '@atproto/oauth-client-browser'
|
||||||
import {msg, Trans} from '@lingui/macro'
|
import {msg, Trans} from '@lingui/macro'
|
||||||
import {useLingui} from '@lingui/react'
|
import {useLingui} from '@lingui/react'
|
||||||
|
|
||||||
@@ -21,6 +27,7 @@ import {logger} from '#/logger'
|
|||||||
import {useSessionApi} from '#/state/session'
|
import {useSessionApi} from '#/state/session'
|
||||||
import {useLoggedOutViewControls} from '#/state/shell/logged-out'
|
import {useLoggedOutViewControls} from '#/state/shell/logged-out'
|
||||||
import {useRequestNotificationsPermission} from 'lib/notifications/notifications'
|
import {useRequestNotificationsPermission} from 'lib/notifications/notifications'
|
||||||
|
import {useGate} from 'lib/statsig/statsig'
|
||||||
import {useSetHasCheckedForStarterPack} from 'state/preferences/used-starter-packs'
|
import {useSetHasCheckedForStarterPack} from 'state/preferences/used-starter-packs'
|
||||||
import {atoms as a, useTheme} from '#/alf'
|
import {atoms as a, useTheme} from '#/alf'
|
||||||
import {Button, ButtonIcon, ButtonText} from '#/components/Button'
|
import {Button, ButtonIcon, ButtonText} from '#/components/Button'
|
||||||
@@ -36,6 +43,106 @@ import {FormContainer} from './FormContainer'
|
|||||||
|
|
||||||
type ServiceDescription = ComAtprotoServerDescribeServer.OutputSchema
|
type ServiceDescription = ComAtprotoServerDescribeServer.OutputSchema
|
||||||
|
|
||||||
|
const client = new BrowserOAuthClient({
|
||||||
|
plcDirectoryUrl: 'https://plc.directory',
|
||||||
|
handleResolver: 'https://bsky.social',
|
||||||
|
})
|
||||||
|
|
||||||
|
export function useOAuth(client: BrowserOAuthClient) {
|
||||||
|
const [agent, setAgent] = useState<null | OAuthAgent>(null)
|
||||||
|
const [loading, setLoading] = useState(true)
|
||||||
|
|
||||||
|
const clientRef = useRef<typeof client>()
|
||||||
|
React.useEffect(() => {
|
||||||
|
// In strict mode, we don't want to reinitialize the client if it's the same
|
||||||
|
if (clientRef.current === client) return
|
||||||
|
clientRef.current = client
|
||||||
|
|
||||||
|
setLoading(true)
|
||||||
|
setAgent(null)
|
||||||
|
|
||||||
|
client
|
||||||
|
.init()
|
||||||
|
.then(async r => {
|
||||||
|
if (clientRef.current !== client) return
|
||||||
|
|
||||||
|
setAgent(r?.agent || null)
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
console.error('Failed to init:', err)
|
||||||
|
|
||||||
|
if (clientRef.current !== client) return
|
||||||
|
if (err instanceof LoginContinuedInParentWindowError) return
|
||||||
|
|
||||||
|
setAgent(null)
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
if (clientRef.current !== client) return
|
||||||
|
|
||||||
|
setLoading(false)
|
||||||
|
})
|
||||||
|
}, [client])
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
if (!agent) return
|
||||||
|
|
||||||
|
const clear = ({detail}: {detail: {sub: string}}) => {
|
||||||
|
if (detail.sub === agent.sub) {
|
||||||
|
setAgent(null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
client.addEventListener('deleted', clear)
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
client.removeEventListener('deleted', clear)
|
||||||
|
}
|
||||||
|
}, [client, agent])
|
||||||
|
|
||||||
|
const signOut = React.useCallback(async () => {
|
||||||
|
if (!agent) return
|
||||||
|
|
||||||
|
setAgent(null)
|
||||||
|
setLoading(true)
|
||||||
|
|
||||||
|
try {
|
||||||
|
await agent.signOut()
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Failed to clear credentials', err)
|
||||||
|
throw err
|
||||||
|
} finally {
|
||||||
|
setLoading(false)
|
||||||
|
}
|
||||||
|
}, [agent])
|
||||||
|
|
||||||
|
const signIn = React.useCallback(
|
||||||
|
async (input: string, options?: AuthorizeOptions) => {
|
||||||
|
// if (agent) return
|
||||||
|
|
||||||
|
setLoading(true)
|
||||||
|
|
||||||
|
try {
|
||||||
|
const agent = await client.signIn(input, options)
|
||||||
|
setAgent(agent)
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Failed to login', err)
|
||||||
|
throw err
|
||||||
|
} finally {
|
||||||
|
setLoading(false)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[client],
|
||||||
|
)
|
||||||
|
|
||||||
|
return {
|
||||||
|
agent,
|
||||||
|
loading,
|
||||||
|
signedIn: agent != null,
|
||||||
|
signIn,
|
||||||
|
signOut,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export const LoginForm = ({
|
export const LoginForm = ({
|
||||||
error,
|
error,
|
||||||
serviceUrl,
|
serviceUrl,
|
||||||
@@ -73,11 +180,20 @@ export const LoginForm = ({
|
|||||||
const {setShowLoggedOut} = useLoggedOutViewControls()
|
const {setShowLoggedOut} = useLoggedOutViewControls()
|
||||||
const setHasCheckedForStarterPack = useSetHasCheckedForStarterPack()
|
const setHasCheckedForStarterPack = useSetHasCheckedForStarterPack()
|
||||||
|
|
||||||
|
const gate = useGate()
|
||||||
|
const oauthEnabled = __DEV__ || gate('use_oauth')
|
||||||
|
const oauth = useOAuth(client)
|
||||||
|
const [handle, setHandle] = React.useState('')
|
||||||
|
|
||||||
const onPressSelectService = React.useCallback(() => {
|
const onPressSelectService = React.useCallback(() => {
|
||||||
Keyboard.dismiss()
|
Keyboard.dismiss()
|
||||||
track('Signin:PressedSelectService')
|
track('Signin:PressedSelectService')
|
||||||
}, [track])
|
}, [track])
|
||||||
|
|
||||||
|
const onPressNextOauth = async () => {
|
||||||
|
await oauth.signIn(handle)
|
||||||
|
}
|
||||||
|
|
||||||
const onPressNext = async () => {
|
const onPressNext = async () => {
|
||||||
if (isProcessing) return
|
if (isProcessing) return
|
||||||
Keyboard.dismiss()
|
Keyboard.dismiss()
|
||||||
@@ -175,6 +291,40 @@ export const LoginForm = ({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<FormContainer testID="loginForm" titleText={<Trans>Sign in</Trans>}>
|
<FormContainer testID="loginForm" titleText={<Trans>Sign in</Trans>}>
|
||||||
|
{oauthEnabled ? (
|
||||||
|
<>
|
||||||
|
<TextField.LabelText>
|
||||||
|
<Trans>Handle</Trans>
|
||||||
|
</TextField.LabelText>
|
||||||
|
<View style={[a.gap_sm]}>
|
||||||
|
<TextField.Root>
|
||||||
|
<TextField.Icon icon={At} />
|
||||||
|
<TextField.Input
|
||||||
|
testID="loginUsernameInput"
|
||||||
|
label={_(msg`alice.test`)}
|
||||||
|
autoCapitalize="none"
|
||||||
|
autoFocus
|
||||||
|
autoCorrect={false}
|
||||||
|
autoComplete="username"
|
||||||
|
returnKeyType="next"
|
||||||
|
textContentType="username"
|
||||||
|
defaultValue={initialHandle || ''}
|
||||||
|
onChangeText={v => {
|
||||||
|
setHandle(v)
|
||||||
|
checkIsReady()
|
||||||
|
}}
|
||||||
|
onSubmitEditing={() => {
|
||||||
|
// @TODO submit
|
||||||
|
}}
|
||||||
|
blurOnSubmit={false} // prevents flickering due to onSubmitEditing going to next field
|
||||||
|
editable={!isProcessing}
|
||||||
|
accessibilityHint={_(msg`Input the handle for your account`)}
|
||||||
|
/>
|
||||||
|
</TextField.Root>
|
||||||
|
</View>
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
<View>
|
<View>
|
||||||
<TextField.LabelText>
|
<TextField.LabelText>
|
||||||
<Trans>Hosting provider</Trans>
|
<Trans>Hosting provider</Trans>
|
||||||
@@ -260,6 +410,8 @@ export const LoginForm = ({
|
|||||||
</TextField.Root>
|
</TextField.Root>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
{isAuthFactorTokenNeeded && (
|
{isAuthFactorTokenNeeded && (
|
||||||
<View>
|
<View>
|
||||||
<TextField.LabelText>
|
<TextField.LabelText>
|
||||||
@@ -325,7 +477,7 @@ export const LoginForm = ({
|
|||||||
<Trans>Connecting...</Trans>
|
<Trans>Connecting...</Trans>
|
||||||
</Text>
|
</Text>
|
||||||
</>
|
</>
|
||||||
) : isReady ? (
|
) : isReady || oauthEnabled ? (
|
||||||
<Button
|
<Button
|
||||||
testID="loginNextButton"
|
testID="loginNextButton"
|
||||||
label={_(msg`Next`)}
|
label={_(msg`Next`)}
|
||||||
@@ -333,7 +485,7 @@ export const LoginForm = ({
|
|||||||
variant="solid"
|
variant="solid"
|
||||||
color="primary"
|
color="primary"
|
||||||
size="medium"
|
size="medium"
|
||||||
onPress={onPressNext}>
|
onPress={oauthEnabled ? onPressNextOauth : onPressNext}>
|
||||||
<ButtonText>
|
<ButtonText>
|
||||||
<Trans>Next</Trans>
|
<Trans>Next</Trans>
|
||||||
</ButtonText>
|
</ButtonText>
|
||||||
|
|||||||
Reference in New Issue
Block a user