Live video on the AT Protocol
79
fork

Configure Feed

Select the types of activity you want to include in your feed.

at eli/detox-testing-github 186 lines 5.5 kB view raw
1import { useNavigation } from "@react-navigation/native"; 2import { CircleHelp } from "@tamagui/lucide-icons"; 3import { useToastController } from "@tamagui/toast"; 4import Loading from "components/loading/loading"; 5import NameColorPicker from "components/name-color-picker/name-color-picker"; 6import { 7 login, 8 logout, 9 selectChatProfile, 10 selectIsReady, 11 selectLogin, 12 selectUserProfile, 13} from "features/bluesky/blueskySlice"; 14import { useEffect, useState } from "react"; 15import { KeyboardAvoidingView, Linking, Pressable } from "react-native"; 16import { useAppDispatch, useAppSelector } from "store/hooks"; 17import { 18 Button, 19 Form, 20 Input, 21 Spinner, 22 Text, 23 View, 24 XStack, 25 YStack, 26} from "tamagui"; 27 28export default function Login() { 29 const dispatch = useAppDispatch(); 30 const chatProfile = useAppSelector(selectChatProfile); 31 const userProfile = useAppSelector(selectUserProfile); 32 const loginState = useAppSelector(selectLogin); 33 const [handle, setHandle] = useState(""); 34 const isReady = useAppSelector(selectIsReady); 35 const toast = useToastController(); 36 const navigation = useNavigation(); 37 38 const submit = () => { 39 let clean = handle; 40 if (handle.startsWith("@")) clean = handle.slice(1); 41 dispatch(login(clean)); 42 }; 43 const onEnterPress = (e: any) => { 44 if (e.nativeEvent.key === "Enter") { 45 submit(); 46 } 47 }; 48 49 useEffect(() => { 50 if (loginState?.error) { 51 toast.show("Login error", { 52 message: loginState.error, 53 }); 54 } 55 }, [loginState?.error]); 56 57 if (!isReady) { 58 return ( 59 <View f={1} jc="center" ai="stretch" gap="$3"> 60 <Loading /> 61 </View> 62 ); 63 } 64 65 let rgb = 66 chatProfile.profile?.color && 67 `rgb(${chatProfile.profile?.color?.red},${chatProfile.profile?.color?.green},${chatProfile.profile?.color?.blue})`; 68 69 if (userProfile) { 70 navigation.setOptions({ title: `Account` }); 71 return ( 72 <View f={1} jc="center" ai="stretch" gap="$3"> 73 <Text textAlign="center" fontSize="$8"> 74 Hey, <Text color={rgb || "#bd6e86"}>@{userProfile.handle}</Text>. 75 </Text> 76 <View flexDirection="row" gap="$2" justifyContent="center"> 77 <Button 78 onPress={() => dispatch(logout())} 79 maxWidth={300} 80 textAlign="center" 81 marginHorizontal="auto" 82 flexBasis={250} 83 > 84 Log out 85 </Button> 86 </View> 87 <NameColorPicker 88 buttonProps={{ 89 textAlign: "center", 90 flexBasis: 250, 91 maxWidth: 300, 92 marginHorizontal: "auto", 93 }} 94 /> 95 </View> 96 ); 97 } 98 99 return ( 100 <KeyboardAvoidingView style={{ flex: 1 }} behavior="padding"> 101 <Form flex={1} onSubmit={submit}> 102 <View 103 f={1} 104 jc="center" 105 ai="center" 106 padding="$4" 107 width="100%" 108 marginHorizontal="auto" 109 > 110 <YStack 111 px="$6" 112 py="$6" 113 br="$4" 114 backgroundColor="$color2" 115 width="100%" 116 maxWidth={600} 117 gap="$2" 118 > 119 <Text fontSize="$9" fontWeight="200"> 120 Log in 121 </Text> 122 <View flexWrap="wrap" flexDirection="row" gap="$1.5"> 123 <Text color="$color11"> 124 Sign in using your handle on the AT Protocol 125 </Text> 126 <Pressable 127 onPress={() => { 128 const u = new URL( 129 "https://atproto.academy/docs/Authentication/why", 130 ); 131 Linking.openURL(u.toString()); 132 }} 133 > 134 <CircleHelp size={18} color="lightskyblue" /> 135 </Pressable> 136 <Text color="$color11">(e.g. your Bluesky handle)</Text> 137 </View> 138 <YStack gap="$2" py="$4"> 139 <Text htmlFor="pdsUrl" color="$color11"> 140 Handle 141 </Text> 142 <Input 143 id="pdsUrl" 144 value={handle} 145 onChangeText={(text) => 146 setHandle( 147 text 148 .toLowerCase() 149 // copying from bsky.app often includes some RTL/LTR characters 150 .replace(/[\u202A\u202C\u200E\u200F\u2066-\u2069]/g, "") 151 .trim(), 152 ) 153 } 154 backgroundColor="$color2" 155 onSubmitEditing={onEnterPress} 156 autoCapitalize="none" 157 autoCorrect={false} 158 keyboardType="url" 159 /> 160 </YStack> 161 <XStack justifyContent="space-between"> 162 <Button 163 onPress={() => navigation.navigate("Signup")} 164 backgroundColor="$gray3" 165 color="$color" 166 > 167 Sign Up 168 </Button> 169 <Form.Trigger asChild> 170 <Button 171 px="$6" 172 // @ts-expect-error Not in the type definition but required for web. 173 type="submit" 174 backgroundColor="$accentColor" 175 disabled={loginState.loading} 176 > 177 <Text>{loginState.loading ? <Spinner /> : `Log in`}</Text> 178 </Button> 179 </Form.Trigger> 180 </XStack> 181 </YStack> 182 </View> 183 </Form> 184 </KeyboardAvoidingView> 185 ); 186}