import Button from '@components/Button' import Icon from '@components/Icon' import browserPackage from '@helpers/browserPackage' import { redirectUri, useAppsMutation } from '@utils/queryHooks/apps' import { useInstanceQuery } from '@utils/queryHooks/instance' import { checkInstanceFeature, getInstances } from '@utils/slices/instancesSlice' import { StyleConstants } from '@utils/styles/constants' import { useTheme } from '@utils/styles/ThemeManager' import * as AuthSession from 'expo-auth-session' import * as WebBrowser from 'expo-web-browser' import { debounce } from 'lodash' import React, { RefObject, useCallback, useState } from 'react' import { Trans, useTranslation } from 'react-i18next' import { Alert, Image, KeyboardAvoidingView, Platform, TextInput, View } from 'react-native' import { ScrollView } from 'react-native-gesture-handler' import { useSelector } from 'react-redux' import { Placeholder } from 'rn-placeholder' import InstanceInfo from './Info' import CustomText from '../Text' import { useNavigation } from '@react-navigation/native' import { TabMeStackNavigationProp } from '@utils/navigation/navigators' import queryClient from '@helpers/queryClient' import { useAppDispatch } from '@root/store' import addInstance from '@utils/slices/instances/add' export interface Props { scrollViewRef?: RefObject disableHeaderImage?: boolean goBack?: boolean } const ComponentInstance: React.FC = ({ scrollViewRef, disableHeaderImage, goBack = false }) => { const { t } = useTranslation('componentInstance') const { colors, mode } = useTheme() const navigation = useNavigation>() const [domain, setDomain] = useState('') const dispatch = useAppDispatch() const instances = useSelector(getInstances, () => true) const instanceQuery = useInstanceQuery({ domain, options: { enabled: !!domain, retry: false } }) const deprecateAuthFollow = useSelector(checkInstanceFeature('deprecate_auth_follow')) const appsMutation = useAppsMutation({ retry: false, onSuccess: async (data, variables) => { const clientId = data.client_id const clientSecret = data.client_secret const discovery = { authorizationEndpoint: `https://${domain}/oauth/authorize` } const request = new AuthSession.AuthRequest({ clientId, clientSecret, scopes: deprecateAuthFollow ? ['read', 'write', 'push'] : ['read', 'write', 'follow', 'push'], redirectUri }) await request.makeAuthUrlAsync(discovery) const promptResult = await request.promptAsync(discovery) if (promptResult?.type === 'success') { const { accessToken } = await AuthSession.exchangeCodeAsync( { clientId, clientSecret, scopes: ['read', 'write', 'follow', 'push'], redirectUri, code: promptResult.params.code, extraParams: { grant_type: 'authorization_code' } }, { tokenEndpoint: `https://${variables.domain}/oauth/token` } ) queryClient.clear() dispatch( addInstance({ domain, token: accessToken, instance: instanceQuery.data!, appData: { clientId, clientSecret } }) ) goBack && navigation.goBack() } } }) const processUpdate = useCallback(() => { if (domain) { if (instances && instances.filter(instance => instance.url === domain).length) { Alert.alert(t('update.alert.title'), t('update.alert.message'), [ { text: t('common:buttons.cancel'), style: 'cancel' }, { text: t('common:buttons.continue'), onPress: () => appsMutation.mutate({ domain }) } ]) } else { appsMutation.mutate({ domain }) } } }, [domain]) return ( {!disableHeaderImage ? ( ) : null} setDomain(text.replace(/^http(s)?\:\/\//i, '')), 1000, { trailing: true })} autoCapitalize='none' clearButtonMode='never' keyboardType='url' textContentType='URL' onSubmitEditing={({ nativeEvent: { text } }) => { if ( text === domain && instanceQuery.isSuccess && instanceQuery.data && instanceQuery.data.uri ) { processUpdate() } }} placeholder={' ' + t('server.textInput.placeholder')} placeholderTextColor={colors.secondary} returnKeyType='go' keyboardAppearance={mode} {...(scrollViewRef && { onFocus: () => setTimeout(() => scrollViewRef.current?.scrollTo({ y: 0, animated: true }), 150) })} autoCorrect={false} spellCheck={false} />