import {useEffect, useMemo, useState} from 'react' import {useWindowDimensions, View} from 'react-native' import Animated, { FadeIn, FadeOut, LayoutAnimationConfig, LinearTransition, SlideInRight, SlideOutLeft, } from 'react-native-reanimated' import {ComAtprotoServerCreateAppPassword} from '@atproto/api' import {msg, Trans} from '@lingui/macro' import {useLingui} from '@lingui/react' import {useMutation} from '@tanstack/react-query' import {isWeb} from '#/platform/detection' import {useAppPasswordCreateMutation} from '#/state/queries/app-passwords' import {atoms as a, native, useTheme} from '#/alf' import {Admonition} from '#/components/Admonition' import {Button, ButtonIcon, ButtonText} from '#/components/Button' import * as Dialog from '#/components/Dialog' import * as TextInput from '#/components/forms/TextField' import * as Toggle from '#/components/forms/Toggle' import {ChevronRight_Stroke2_Corner0_Rounded as ChevronRight} from '#/components/icons/Chevron' import {SquareBehindSquare4_Stroke2_Corner0_Rounded as CopyIcon} from '#/components/icons/SquareBehindSquare4' import {Text} from '#/components/Typography' import {CopyButton} from './CopyButton' export function AddAppPasswordDialog({ control, passwords, }: { control: Dialog.DialogControlProps passwords: string[] }) { const {height} = useWindowDimensions() return ( ) } function CreateDialogInner({passwords}: {passwords: string[]}) { const control = Dialog.useDialogContext() const t = useTheme() const {_} = useLingui() const autogeneratedName = useRandomName() const [name, setName] = useState('') const [privileged, setPrivileged] = useState(false) const { mutateAsync: actuallyCreateAppPassword, error: apiError, data, } = useAppPasswordCreateMutation() const regexFailError = useMemo( () => new DisplayableError( _( msg`App password names can only contain letters, numbers, spaces, dashes, and underscores`, ), ), [_], ) const { mutate: createAppPassword, error: validationError, isPending, } = useMutation< ComAtprotoServerCreateAppPassword.AppPassword, Error | DisplayableError >({ mutationFn: async () => { const chosenName = name.trim() || autogeneratedName if (chosenName.length < 4) { throw new DisplayableError( _(msg`App password names must be at least 4 characters long`), ) } if (passwords.find(p => p === chosenName)) { throw new DisplayableError(_(msg`App password name must be unique`)) } return await actuallyCreateAppPassword({name: chosenName, privileged}) }, }) const [hasBeenCopied, setHasBeenCopied] = useState(false) useEffect(() => { if (hasBeenCopied) { const timeout = setTimeout(() => setHasBeenCopied(false), 100) return () => clearTimeout(timeout) } }, [hasBeenCopied]) const error = validationError || (!name.match(/^[a-zA-Z0-9-_ ]*$/) && regexFailError) return ( {!data ? ( Add App Password Please enter a unique name for this app password or use our randomly generated one. createAppPassword()} blurOnSubmit autoCorrect={false} autoComplete="off" autoCapitalize="none" autoFocus /> {error instanceof DisplayableError && ( {error.message} )} Allow access to your direct messages {!!apiError || (error && !(error instanceof DisplayableError) && ( Failed to create app password. Please try again. ))} ) : ( Here is your app password! Use this to sign in to the other app along with your handle. {data.password} For security reasons, you won't be able to view this again. If you lose this app password, you'll need to generate a new one. )} ) } class DisplayableError extends Error { constructor(message: string) { super(message) this.name = 'DisplayableError' } } function useRandomName() { return useState( () => shadesOfBlue[Math.floor(Math.random() * shadesOfBlue.length)], )[0] } const shadesOfBlue: string[] = [ 'AliceBlue', 'Aqua', 'Aquamarine', 'Azure', 'BabyBlue', 'Blue', 'BlueViolet', 'CadetBlue', 'CornflowerBlue', 'Cyan', 'DarkBlue', 'DarkCyan', 'DarkSlateBlue', 'DeepSkyBlue', 'DodgerBlue', 'ElectricBlue', 'LightBlue', 'LightCyan', 'LightSkyBlue', 'LightSteelBlue', 'MediumAquaMarine', 'MediumBlue', 'MediumSlateBlue', 'MidnightBlue', 'Navy', 'PowderBlue', 'RoyalBlue', 'SkyBlue', 'SlateBlue', 'SteelBlue', 'Teal', 'Turquoise', ]