this repo has no description
at main 5.5 kB view raw
1import { Image } from "expo-image"; 2import { Alert, Button, StyleSheet, TextInput } from "react-native"; 3import { Agent } from "@atproto/api"; 4import { TID } from "@atproto/common-web"; 5 6import { HelloWave } from "@/components/HelloWave"; 7import ParallaxScrollView from "@/components/ParallaxScrollView"; 8import { ThemedText } from "@/components/ThemedText"; 9import { ThemedView } from "@/components/ThemedView"; 10import { useState } from "react"; 11import { ExpoOAuthClient } from "expo-atproto-auth"; 12import { OAuthSession } from "@atproto/oauth-client"; 13import { WebBrowserResultType } from "expo-web-browser"; 14 15const client = new ExpoOAuthClient({ 16 clientMetadata: { 17 client_id: "https://hailey.at/oauth-client-metadata.json", 18 client_name: "Hailey's Atproto App", 19 client_uri: "https://hailey.at", 20 redirect_uris: ["at.hailey:/auth/callback"], 21 scope: "atproto transition:generic", 22 token_endpoint_auth_method: "none", 23 response_types: ["code"], 24 grant_types: ["authorization_code", "refresh_token"], 25 application_type: "native", 26 dpop_bound_access_tokens: true, 27 }, 28 handleResolver: "https://bsky.social", 29}); 30 31export default function HomeScreen() { 32 const [handle, setHandle] = useState<string>(""); 33 const [agent, setAgent] = useState<Agent | null>(null); 34 const [isProcessing, setIsProcessing] = useState(false); 35 const [postTitle, setPostTitle] = useState<string>(""); 36 const [postText, setPostText] = useState<string>(""); 37 const [name, setName] = useState<string>(); 38 39 const onSignInPress = async () => { 40 let res: 41 | { 42 status: WebBrowserResultType; 43 } 44 | { 45 status: "error"; 46 error: unknown; 47 } 48 | { 49 status: "success"; 50 session: OAuthSession; 51 }; 52 53 setIsProcessing(true); 54 try { 55 res = await client.signIn(handle); 56 } catch (e) { 57 console.log(e); 58 return; 59 } finally { 60 setIsProcessing(false); 61 } 62 63 if (res.status === "error") { 64 Alert.alert("Authentication Error", (res.error as any).toString()); 65 } else if (res.status === "success") { 66 const newAgent = new Agent(res.session); 67 setAgent(newAgent); 68 69 const pres = await newAgent.getProfile({ actor: newAgent.did! }); 70 setName(pres.data.displayName); 71 } 72 }; 73 74 const onCreatePost = async () => { 75 const entry: WhtwndBlogEntryRecord = { 76 $type: "com.whtwnd.blog.entry", 77 createdAt: new Date().toISOString(), 78 title: postTitle, 79 content: postText, 80 }; 81 82 setIsProcessing(true); 83 try { 84 await agent!.com.atproto.repo.createRecord({ 85 repo: agent!.did!, 86 collection: "com.whtwnd.blog.entry", 87 record: entry, 88 rkey: TID.nextStr(), 89 }); 90 } catch (e) { 91 console.log(e); 92 } finally { 93 setIsProcessing(false); 94 } 95 96 Alert.alert("Post Created!", "Your post has been published!!!"); 97 }; 98 99 return ( 100 <ParallaxScrollView 101 headerBackgroundColor={{ light: "#A1CEDC", dark: "#1D3D47" }} 102 headerImage={ 103 <Image 104 source={require("@/assets/images/partial-react-logo.png")} 105 style={styles.reactLogo} 106 /> 107 } 108 > 109 <ThemedView style={styles.titleContainer}> 110 <ThemedText type="title">Welcome!</ThemedText> 111 <HelloWave /> 112 </ThemedView> 113 <ThemedView style={styles.stepContainer}> 114 {agent === null ? ( 115 <> 116 <ThemedText>Sign into your account to get started!</ThemedText> 117 <TextInput 118 onChangeText={setHandle} 119 value={handle} 120 placeholder="Handle or DID" 121 style={styles.textInput} 122 autoCapitalize="none" 123 autoCorrect={false} 124 autoComplete="username" 125 /> 126 <Button 127 title="Sign In" 128 onPress={onSignInPress} 129 disabled={isProcessing} 130 /> 131 </> 132 ) : ( 133 <> 134 <ThemedText> 135 Welcome, {name}. Go ahead and share your thoughts. 136 </ThemedText> 137 <TextInput 138 style={styles.textInput} 139 onChangeText={setPostTitle} 140 value={postTitle} 141 placeholder="Post Title" 142 /> 143 <TextInput 144 onChangeText={setPostText} 145 value={postText} 146 placeholder="Post Content" 147 multiline 148 numberOfLines={10} 149 style={styles.multilineTextInput} 150 /> 151 <Button 152 title="Create Post" 153 onPress={onCreatePost} 154 disabled={isProcessing} 155 /> 156 </> 157 )} 158 </ThemedView> 159 </ParallaxScrollView> 160 ); 161} 162 163const styles = StyleSheet.create({ 164 titleContainer: { 165 flexDirection: "row", 166 alignItems: "center", 167 gap: 8, 168 }, 169 stepContainer: { 170 gap: 8, 171 marginBottom: 8, 172 }, 173 reactLogo: { 174 height: 178, 175 width: 290, 176 bottom: 0, 177 left: 0, 178 position: "absolute", 179 }, 180 textInput: { 181 height: 50, 182 borderWidth: 1, 183 borderColor: "grey", 184 }, 185 multilineTextInput: { 186 borderWidth: 1, 187 borderColor: "grey", 188 }, 189}); 190 191interface WhtwndBlogEntryRecord { 192 $type: "com.whtwnd.blog.entry"; 193 content?: string; 194 createdAt: string; 195 theme?: string; 196 title: string; 197 ogp?: { 198 height: number | null; 199 url: string | null; 200 width: number | null; 201 }; 202}