[READ-ONLY] Mirror of https://github.com/flo-bit/bluesky-website-generator.
bluesky-website-generator.vercel.app
4.2 kB
170 lines
1<script lang="ts">
2 import {
3 Badge,
4 Button,
5 Heading,
6 Input,
7 Subheading,
8 Tabs,
9 ThemeToggle,
10 toast,
11 Toaster
12 } from '@fuxui/base';
13 import { BlueskyLogin, blueskyPostToPostData, UserProfile, type PostData } from '@fuxui/social';
14 import { editingState } from './state.svelte.js';
15 import Editor from '$lib/Editor.svelte';
16 import Posts from '$lib/Posts.svelte';
17 import Links from '$lib/Links.svelte';
18 import { onMount } from 'svelte';
19 import { AtpBaseClient } from '@atproto/api';
20 import type { FeedViewPost } from '@atproto/api/dist/client/types/app/bsky/feed/defs.js';
21 import Navbar from '$lib/Navbar.svelte';
22
23 let value = $state('');
24
25 let { data } = $props();
26 let form = $state<HTMLFormElement | null>(null);
27
28 async function deploy() {
29 const res = await fetch('/api/cloudflare', {
30 method: 'POST',
31 headers: { 'Content-Type': 'application/json' },
32 body: JSON.stringify({
33 projectName: data.user.handle.replace(/\./g, '-'),
34 domain: value + '.polijn.com',
35 profileHandle: data.user.handle
36 })
37 });
38
39 // const data = await res.json();
40 // if (!res.ok) {
41 // alert('Error: ' + data.error);
42 // } else {
43 // alert('Success!);
44 // }
45 }
46 onMount(async () => {
47 editingState.links = JSON.parse(localStorage.getItem('links') ?? '[]');
48
49 if (!data.user) return;
50
51 const posts = await getPostsOfUser({ actor: data.user.did });
52 console.log(posts);
53
54 blueskyPosts = posts.feed.map((post) => {
55 console.log(post);
56 return blueskyPostToPostData(post.post);
57 });
58 });
59
60 export async function getPostsOfUser({
61 actor,
62 cursor,
63 limit = 20
64 }: {
65 actor: string;
66 cursor?: string;
67 limit?: number;
68 }) {
69 let agent = new AtpBaseClient({ service: 'https://api.bsky.app' });
70 const perLimit = limit > 100 ? 100 : limit;
71 const posts: FeedViewPost[] = [];
72 do {
73 const { data } = await agent.app.bsky.feed.getAuthorFeed({
74 actor,
75 cursor,
76 limit: perLimit,
77 filter: 'posts_no_replies'
78 });
79 posts.push(...data.feed);
80 cursor = data.cursor;
81 } while (cursor && posts.length < limit);
82 return { feed: posts, cursor };
83 }
84
85 let blueskyPosts: PostData[] = $state([]);
86</script>
87
88<div class="mx-auto max-w-2xl py-16">
89 {#if !data.user}
90 <Heading class="mt-16 mb-8">Login with Bluesky to create your website</Heading>
91 <BlueskyLogin
92 login={async (handle) => {
93 // create and submit form with handle
94
95 if (!form) {
96 console.error('Form not found');
97 return false;
98 }
99
100 // set handle
101 const input = form.querySelector('input[name="handle"]') as HTMLInputElement;
102 if (!input) {
103 return false;
104 }
105 input.value = handle;
106 form.submit();
107
108 return true;
109 }}
110 />
111
112 <form method="POST" action="/?/login" class="hidden" bind:this={form}>
113 <input type="hidden" name="handle" value="handle" />
114 </form>
115 {:else}
116 <UserProfile class="" profile={{ ...data.user, description: undefined }} />
117
118 <div class="-mt-8">
119 <Tabs
120 active={editingState.active}
121 items={[
122 {
123 name: 'about',
124 onclick: () => (editingState.active = 'about')
125 },
126 {
127 name: 'links',
128 onclick: () => (editingState.active = 'links')
129 },
130 {
131 name: 'feed',
132 onclick: () => (editingState.active = 'feed')
133 }
134 ]}
135 />
136
137 <div class="px-4">
138 {#if editingState.active === 'about'}
139 <div
140 class="focus-within:outline-accent-400 hover:not-focus-within:bg-base-200/40 dark:hover:not-focus-within:bg-base-900/20 group focus-within:bg-accent-500/5 relative mt-4 rounded-2xl px-2 py-0.5 focus-within:outline"
141 >
142 <Editor />
143 <Badge
144 variant="secondary"
145 class="absolute top-2 right-2 opacity-60 group-focus-within:hidden"
146 >
147 Select to edit
148 </Badge>
149 </div>
150 {:else if editingState.active === 'links'}
151 <div>
152 <Links links={editingState.links} />
153 </div>
154 {:else if editingState.active === 'feed'}
155 <Posts posts={blueskyPosts} />
156 {/if}
157 </div>
158
159 <form method="POST" action="/?/logout" class="fixed right-2 bottom-2">
160 <Button variant="secondary" type="submit">Logout</Button>
161 </form>
162 </div>
163 {/if}
164</div>
165
166<Toaster />
167
168{#if data.user}
169 <Navbar />
170{/if}