[READ-ONLY] Mirror of https://github.com/flo-bit/bluesky-website-generator. bluesky-website-generator.vercel.app
0

Configure Feed

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

bluesky-website-generator / src / routes / +page.svelte
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}