import React, {useState, useEffect} from 'react' import { ActivityIndicator, KeyboardAvoidingView, StyleSheet, Text, TextInput, TouchableOpacity, View, useWindowDimensions, } from 'react-native' import Svg, {Circle, Line, Text as SvgText} from 'react-native-svg' import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome' import * as EmailValidator from 'email-validator' import {observer} from 'mobx-react-lite' import {Picker} from '../com/util/Picker' import {s, colors} from '../lib/styles' import {useStores} from '../../state' import {ServiceDescription} from '../../state/models/session' enum ScreenState { SigninOrCreateAccount, Signin, CreateAccount, } const Logo = () => { return ( B ) } const SigninOrCreateAccount = ({ onPressSignin, onPressCreateAccount, }: { onPressSignin: () => void onPressCreateAccount: () => void }) => { const winDim = useWindowDimensions() const halfWidth = winDim.width / 2 return ( <> Bluesky [ private beta ] Create a new account or Sign in ) } const Signin = ({onPressBack}: {onPressBack: () => void}) => { const store = useStores() const [isProcessing, setIsProcessing] = useState(false) const [error, setError] = useState('') const [handle, setHandle] = useState('') const [password, setPassword] = useState('') const onPressNext = async () => { setError('') setIsProcessing(true) try { await store.session.login({ service: 'http://localhost:2583/', handle, password, }) } catch (e: any) { const errMsg = e.toString() console.log(e) setIsProcessing(false) if (errMsg.includes('Authentication Required')) { setError('Invalid username or password') } else if (errMsg.includes('Network request failed')) { setError( 'Unable to contact your service. Please check your Internet connection.', ) } else { setError(errMsg.replace(/^Error:/, '')) } } } return ( Sign in {error ? ( {error} ) : undefined} Back {isProcessing ? ( ) : ( Next )} ) } const CreateAccount = ({onPressBack}: {onPressBack: () => void}) => { const store = useStores() const [isProcessing, setIsProcessing] = useState(false) const [error, setError] = useState('') const [serviceDescription, setServiceDescription] = useState< ServiceDescription | undefined >(undefined) const [userDomain, setUserDomain] = useState('') const [inviteCode, setInviteCode] = useState('') const [email, setEmail] = useState('') const [password, setPassword] = useState('') const [handle, setHandle] = useState('') useEffect(() => { let aborted = false if (serviceDescription || error) { return } store.session.describeService('http://localhost:2583/').then( desc => { if (aborted) return setServiceDescription(desc) setUserDomain(desc.availableUserDomains[0]) }, err => { if (aborted) return console.error(err) setError( 'Unable to contact your service. Please check your Internet connection.', ) }, ) return () => { aborted = true } }, []) const onPressNext = async () => { if (!email) { return setError('Please enter your email.') } if (!EmailValidator.validate(email)) { return setError('Your email appears to be invalid.') } if (!password) { return setError('Please choose your password.') } if (!handle) { return setError('Please choose your username.') } setError('') setIsProcessing(true) try { await store.session.createAccount({ service: 'http://localhost:2583/', email, handle: `${handle}.${userDomain}`, password, inviteCode, }) } catch (e: any) { const errMsg = e.toString() console.log(e) setIsProcessing(false) // if (errMsg.includes('Authentication Required')) { // setError('Invalid username or password') // } else if (errMsg.includes('Network request failed')) { // setError( // 'Unable to contact your service. Please check your Internet connection.', // ) // } else { setError(errMsg.replace(/^Error:/, '')) // } } } const InitialLoadView = () => ( <> {error ? ( <> {error} Back ) : ( )} ) return ( {serviceDescription ? ( <> {error ? ( {error} ) : undefined} Create a new account {serviceDescription?.inviteCodeRequired ? ( ) : undefined} Choose your username setHandle(cleanUsername(v))} editable={!isProcessing} /> {serviceDescription.availableUserDomains.length > 1 && ( ({ label: `.${d}`, value: d, }))} onChange={itemValue => setUserDomain(itemValue)} enabled={!isProcessing} /> )} Your full username will be{' '} @{handle}.{userDomain} Back {isProcessing ? ( ) : ( Next )} ) : ( )} ) } function cleanUsername(v: string): string { v = v.trim() if (v.length > 63) { v = v.slice(0, 63) } return v.toLowerCase().replace(/[^a-z0-9-]/g, '') } export const Login = observer( (/*{navigation}: RootTabsScreenProps<'Login'>*/) => { // const store = useStores() const [screenState, setScreenState] = useState( ScreenState.SigninOrCreateAccount, ) return ( {screenState === ScreenState.SigninOrCreateAccount ? ( setScreenState(ScreenState.Signin)} onPressCreateAccount={() => setScreenState(ScreenState.CreateAccount) } /> ) : undefined} {screenState === ScreenState.Signin ? ( setScreenState(ScreenState.SigninOrCreateAccount) } /> ) : undefined} {screenState === ScreenState.CreateAccount ? ( setScreenState(ScreenState.SigninOrCreateAccount) } /> ) : undefined} ) }, ) const styles = StyleSheet.create({ outer: { flex: 1, }, hero: { flex: 2, justifyContent: 'center', }, logoHero: { paddingTop: 30, paddingBottom: 40, }, logo: { flexDirection: 'row', justifyContent: 'center', }, title: { textAlign: 'center', color: colors.white, fontSize: 68, fontWeight: 'bold', }, subtitle: { textAlign: 'center', color: colors.white, fontSize: 18, }, btn: { borderWidth: 1, borderColor: colors.white, borderRadius: 10, paddingVertical: 16, marginBottom: 20, marginHorizontal: 20, backgroundColor: colors.blue3, }, btnLabel: { textAlign: 'center', color: colors.white, fontSize: 18, fontWeight: 'bold', }, or: { marginBottom: 20, }, orLine: { position: 'absolute', top: 10, }, orLabel: { textAlign: 'center', color: colors.white, fontSize: 16, }, group: { borderWidth: 1, borderColor: colors.white, borderRadius: 10, marginBottom: 20, marginHorizontal: 20, backgroundColor: colors.blue3, }, groupTitle: { paddingVertical: 8, paddingHorizontal: 12, }, groupContent: { borderTopWidth: 1, borderTopColor: colors.blue1, flexDirection: 'row', alignItems: 'center', }, groupContentIcon: { color: 'white', marginLeft: 10, }, textInput: { flex: 1, width: '100%', backgroundColor: colors.blue3, color: colors.white, paddingVertical: 10, paddingHorizontal: 12, fontSize: 18, borderRadius: 10, }, picker: { flex: 1, width: '100%', backgroundColor: colors.blue3, color: colors.white, paddingVertical: 10, paddingHorizontal: 12, fontSize: 18, borderRadius: 10, }, pickerLabel: { color: colors.white, fontSize: 18, }, pickerIcon: { color: colors.white, }, error: { borderTopWidth: 1, borderTopColor: colors.blue1, flexDirection: 'row', alignItems: 'center', marginTop: 5, backgroundColor: colors.blue2, paddingHorizontal: 8, paddingVertical: 5, }, errorFloating: { borderWidth: 1, borderColor: colors.blue1, marginBottom: 20, marginHorizontal: 20, borderRadius: 8, }, errorIcon: { borderWidth: 1, borderColor: colors.white, color: colors.white, borderRadius: 30, width: 16, height: 16, alignItems: 'center', justifyContent: 'center', marginRight: 5, }, })