this repo has no description
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}