import React, {useState} from 'react' import {StyleSheet, TextInput, View, TouchableOpacity} from 'react-native' import {Text} from '../util/text/Text' import {Button} from '../util/forms/Button' import {s} from 'lib/styles' import {usePalette} from 'lib/hooks/usePalette' import {isNative} from 'platform/detection' import { FontAwesomeIcon, FontAwesomeIconStyle, } from '@fortawesome/react-native-fontawesome' import Clipboard from '@react-native-clipboard/clipboard' import * as Toast from '../util/Toast' import {logger} from '#/logger' import {Trans, msg} from '@lingui/macro' import {useLingui} from '@lingui/react' import {useModalControls} from '#/state/modals' import { useAppPasswordsQuery, useAppPasswordCreateMutation, } from '#/state/queries/app-passwords' export const snapPoints = ['70%'] 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', ] export function Component({}: {}) { const pal = usePalette('default') const {_} = useLingui() const {closeModal} = useModalControls() const {data: passwords} = useAppPasswordsQuery() const {mutateAsync: mutateAppPassword, isPending} = useAppPasswordCreateMutation() const [name, setName] = useState( shadesOfBlue[Math.floor(Math.random() * shadesOfBlue.length)], ) const [appPassword, setAppPassword] = useState() const [wasCopied, setWasCopied] = useState(false) const onCopy = React.useCallback(() => { if (appPassword) { Clipboard.setString(appPassword) Toast.show(_(msg`Copied to clipboard`)) setWasCopied(true) } }, [appPassword, _]) const onDone = React.useCallback(() => { closeModal() }, [closeModal]) const createAppPassword = async () => { // if name is all whitespace, we don't allow it if (!name || !name.trim()) { Toast.show( _( msg`Please enter a name for your app password. All spaces is not allowed.`, ), 'times', ) return } // if name is too short (under 4 chars), we don't allow it if (name.length < 4) { Toast.show( _(msg`App Password names must be at least 4 characters long.`), 'times', ) return } if (passwords?.find(p => p.name === name)) { Toast.show(_(msg`This name is already in use`), 'times') return } try { const newPassword = await mutateAppPassword({name}) if (newPassword) { setAppPassword(newPassword.password) } else { Toast.show(_(msg`Failed to create app password.`), 'times') // TODO: better error handling (?) } } catch (e) { Toast.show(_(msg`Failed to create app password.`), 'times') logger.error('Failed to create app password', {message: e}) } } const _onChangeText = (text: string) => { // sanitize input // we only all alphanumeric characters, spaces, dashes, and underscores // if the user enters anything else, we ignore it and shake the input container // also, it cannot start with a space if (text.match(/^[a-zA-Z0-9-_ ]*$/)) { setName(text) } else { Toast.show( _( msg`App Password names can only contain letters, numbers, spaces, dashes, and underscores.`, ), ) } } return ( {!appPassword ? ( Please enter a unique name for this App Password or use our randomly generated one. ) : ( Here is your app password. Use this to sign into the other app along with your handle. )} {!appPassword ? ( ) : ( {appPassword} {wasCopied ? ( Copied ) : ( )} )} {appPassword ? ( For security reasons, you won't be able to view this again. If you lose this password, you'll need to generate a new one. ) : ( Can only contain letters, numbers, spaces, dashes, and underscores. Must be at least 4 characters long, but no more than 32 characters long. )}