Personal Site
0
fork

Configure Feed

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

Remove tab system and make it only bluesky feed

this is bc i only post there and the other apis make me go insaine
i have had Enough of apis
i cannot be fucked anymore lmfao

vielle.dev 88baeb2a d52be025

verified
+101 -257
-63
src/components/home/feeds/Bluesky.astro
··· 1 - --- 2 - import { socials } from "/site-config"; 3 - import { is } from "@atcute/lexicons"; 4 - import { AppBskyFeedPost } from "@atcute/bluesky"; 5 - import BskyPost, { type Author } from "./BskyPost.astro"; 6 - import { client } from "./atproto"; 7 - 8 - const { ok, data } = await client.get("app.bsky.feed.getAuthorFeed", { 9 - params: { 10 - actor: socials.atproto.did, 11 - limit: 100, 12 - }, 13 - }); 14 - 15 - if (!ok) { 16 - // error occoured 17 - console.error(data.error, data.message); 18 - 19 - return "Error 500: Internal server error fetching this feed. Try refreshing the page"; 20 - } 21 - 22 - let posts: { record: AppBskyFeedPost.Main; author: Author }[] = data.feed 23 - // make sure it was by me (exclude reposts) 24 - .filter((x) => x.post.author.did === socials.atproto.did) 25 - // makes sure its not a reply 26 - .filter((x) => !x.reply) 27 - // this list now includes original posts & quote skeets 28 - .map((x) => { 29 - return { 30 - record: is(AppBskyFeedPost.mainSchema, x.post.record) 31 - ? x.post.record 32 - // never type bc it gets filtered out 33 - : undefined as never, 34 - author: { 35 - did: x.post.author.did, 36 - displayName: x.post.author.displayName, 37 - handle: x.post.author.handle, 38 - avatar: x.post.author.avatar, 39 - }, 40 - }; 41 - }) 42 - .filter((x) => x.record); 43 - --- 44 - 45 - <h1>Bsky</h1> 46 - <ul> 47 - { 48 - posts.map((x) => ( 49 - <li> 50 - <BskyPost post={x.record} author={x.author} /> 51 - </li> 52 - )) 53 - } 54 - </ul> 55 - 56 - <style> 57 - ul { 58 - padding-inline: 2em; 59 - li { 60 - margin-block-end: 1em; 61 - } 62 - } 63 - </style>
src/components/home/feeds/BskyPost.astro src/components/home/feeds/Post.astro
+89
src/components/home/feeds/Feed.astro
··· 1 + --- 2 + import boxLR from "/assets/box-lr.png"; 3 + import { socials } from "/site-config"; 4 + import { is } from "@atcute/lexicons"; 5 + import { AppBskyFeedPost } from "@atcute/bluesky"; 6 + import Post, { type Author } from "./Post.astro"; 7 + import { client } from "./atproto"; 8 + 9 + const { ok, data } = await client.get("app.bsky.feed.getAuthorFeed", { 10 + params: { 11 + actor: socials.atproto.did, 12 + limit: 100, 13 + }, 14 + }); 15 + 16 + if (!ok) { 17 + // error occoured 18 + console.error(data.error, data.message); 19 + 20 + return "Error 500: Internal server error fetching this feed. Try refreshing the page"; 21 + } 22 + 23 + let posts: { record: AppBskyFeedPost.Main; author: Author }[] = data.feed 24 + // make sure it was by me (exclude reposts) 25 + .filter((x) => x.post.author.did === socials.atproto.did) 26 + // makes sure its not a reply 27 + .filter((x) => !x.reply) 28 + // this list now includes original posts & quote skeets 29 + .map((x) => { 30 + return { 31 + record: is(AppBskyFeedPost.mainSchema, x.post.record) 32 + ? x.post.record 33 + : // never type bc it gets filtered out 34 + (undefined as never), 35 + author: { 36 + did: x.post.author.did, 37 + displayName: x.post.author.displayName, 38 + handle: x.post.author.handle, 39 + avatar: x.post.author.avatar, 40 + }, 41 + }; 42 + }) 43 + .filter((x) => x.record); 44 + --- 45 + 46 + <section class="feed" style={`--box-lr-png: url("${boxLR.src}");`}> 47 + <div class="feed-contents"> 48 + <h2>Bluesky 🦋</h2> 49 + <ul> 50 + { 51 + posts.map((x) => ( 52 + <li> 53 + <Post post={x.record} author={x.author} /> 54 + </li> 55 + )) 56 + } 57 + </ul> 58 + 59 + <style> 60 + ul { 61 + padding-inline: 2em; 62 + li { 63 + margin-block-end: 1em; 64 + } 65 + } 66 + </style> 67 + </div> 68 + </section> 69 + 70 + <style> 71 + /* wrapper */ 72 + .feed { 73 + height: calc(100vh - 40px - 2rem * 1.5); 74 + @media (max-width: 90ch) { 75 + height: 600px; 76 + } 77 + 78 + z-index: 0; 79 + 80 + border-image: var(--box-lr-png) 0 10 fill / 0 20px / 10000px 20px round; 81 + } 82 + 83 + /* tab contents */ 84 + .feed-contents { 85 + grid-area: contents; 86 + overflow-y: auto; 87 + min-height: 100%; 88 + } 89 + </style>
-177
src/components/home/feeds/Feeds.astro
··· 1 - --- 2 - import Bluesky from "./Bluesky.astro"; 3 - import Tangled from "./Tangled.astro"; 4 - import Github from "./Github.astro"; 5 - 6 - import IconBluesky from "/assets/icons/Bluesky.svg"; 7 - import IconGit from "/assets/icons/Git.svg"; 8 - import IconGithub from "/assets/icons/GitHub.svg"; 9 - import boxLR from "/assets/box-lr.png"; 10 - 11 - const tabs: { 12 - id: string; 13 - label: string; 14 - icon?: ((_props: astroHTML.JSX.SVGAttributes) => any) | string; 15 - content: (_props: Record<string, any>) => any; 16 - }[] = [ 17 - { id: "bsky", label: "Bluesky", icon: IconBluesky, content: Bluesky }, 18 - { id: "tangled", label: "Tangled", icon: IconGit, content: Tangled }, 19 - { id: "gh", label: "Github", icon: IconGithub, content: Github }, 20 - ]; 21 - --- 22 - 23 - <section class="feeds" style={`--box-lr-png: url("${boxLR.src}");`}> 24 - <!-- tabindex -1 disables tabbing onto this element 25 - tabbing is disabled because when the element overflows its focusable 26 - to scrub across children etc. which is a logical default 27 - but in this case is annoying as scrubbing is already possible via the tabs 28 - --> 29 - <div 30 - id="tab-container" 31 - tabindex="-1" 32 - role="tablist" 33 - aria-label="Social media feed" 34 - > 35 - { 36 - tabs.map((tab, i) => ( 37 - <div role="tab" aria-controls={"tab-contents-" + tab.id}> 38 - <label> 39 - <input 40 - type="radio" 41 - name="social-tab" 42 - id={"tab-" + tab.id} 43 - checked={i === 0} 44 - /> 45 - {tab.icon && 46 - (typeof tab.icon === "string" ? ( 47 - <img src={tab.icon} alt="" /> 48 - ) : ( 49 - <tab.icon /> 50 - ))} 51 - <span class="label">{tab.label}</span> 52 - </label> 53 - </div> 54 - )) 55 - } 56 - </div> 57 - 58 - <div class="feed-contents"> 59 - { 60 - tabs.map(({ id, content: Content }) => ( 61 - <div 62 - class="content" 63 - id={"tab-contents-" + id} 64 - aria-labelledby={"tab-" + id} 65 - role="tabpanel" 66 - > 67 - <Content /> 68 - </div> 69 - )) 70 - } 71 - </div> 72 - </section> 73 - 74 - <!-- god i want /ref/ combinators so fuckin bad 75 - this could be so clean 76 - `#tab-container :checked /data-tab-for/ .tab` --> 77 - <style 78 - set:html={tabs 79 - .map( 80 - ({ id }) => 81 - `#tab-container:has([aria-controls="tab-contents-${id}"] :checked) + .feed-contents #tab-contents-${id} { display: var(--display, block); }`, 82 - ) 83 - .join("\n")} 84 - ></style> 85 - 86 - <style> 87 - /* wrapper */ 88 - .feeds { 89 - display: grid; 90 - grid-template: "menu" auto "contents" 1fr / 1fr; 91 - /* calculate header height and reduce the height accordingly */ 92 - /* screen height - margin&padding - h1 text */ 93 - height: calc(100vh - 40px - 2rem * 1.5); 94 - @media (max-width: 90ch) { 95 - height: 600px; 96 - } 97 - 98 - z-index: 0; 99 - 100 - border-image-source: var(--box-lr-png); 101 - border-image-slice: 0 10 fill; 102 - border-image-width: 0 20px; 103 - border-image-outset: 10000px 20px; 104 - border-image-repeat: round; 105 - } 106 - 107 - /* tab menu */ 108 - #tab-container { 109 - display: flex; 110 - flex-direction: row; 111 - gap: 10px; 112 - overflow-x: auto; 113 - scrollbar-width: none; 114 - 115 - grid-area: menu; 116 - 117 - border-bottom: 2px solid green; 118 - 119 - label { 120 - display: grid; 121 - grid-template: "icon text" 1fr / auto auto; 122 - flex-direction: row; 123 - gap: 2.5px; 124 - margin: 2.5px; 125 - padding: 2.5px; 126 - 127 - position: relative; 128 - padding-bottom: 5px; 129 - &::after { 130 - content: ""; 131 - width: 100%; 132 - height: 5px; 133 - display: block; 134 - 135 - position: absolute; 136 - bottom: 0; 137 - left: 0; 138 - } 139 - 140 - &:has(:checked)::after { 141 - background-color: blue; 142 - } 143 - 144 - &:focus-within { 145 - outline: 1px solid red; 146 - } 147 - 148 - img, 149 - svg { 150 - width: 24px; 151 - height: 24px; 152 - grid-area: icon; 153 - } 154 - 155 - .label { 156 - grid-area: text; 157 - } 158 - 159 - input { 160 - /* visually hide it but dont remove from dom/accessibility tree for focus */ 161 - opacity: 0; 162 - grid-area: icon; 163 - } 164 - } 165 - } 166 - 167 - /* tab contents */ 168 - .feed-contents { 169 - grid-area: contents; 170 - overflow-y: auto; 171 - min-height: 100%; 172 - } 173 - 174 - .feed-contents .content { 175 - display: none; 176 - } 177 - </style>
-5
src/components/home/feeds/Github.astro
··· 1 - --- 2 - 3 - --- 4 - 5 - <h3>gh</h3>
-5
src/components/home/feeds/Tangled.astro
··· 1 - --- 2 - 3 - --- 4 - 5 - <h4>tangled.sh</h4>
+5
src/components/home/feeds/atproto.ts
··· 5 5 PlcDidDocumentResolver, 6 6 WebDidDocumentResolver, 7 7 } from "@atcute/identity-resolver"; 8 + import { socials } from "/site-config"; 8 9 9 10 export const client = new Client({ 10 11 handler: simpleFetchHandler({ service: "https://public.api.bsky.app" }), 12 + }); 13 + 14 + export const pds = new Client({ 15 + handler: simpleFetchHandler({ service: socials.atproto.pds }), 11 16 }); 12 17 13 18 export const docResolver = new CompositeDidDocumentResolver({
+7 -7
src/pages/index.astro
··· 1 1 --- 2 2 import Base from "/components/Base.astro"; 3 - import Feeds from "/components/home/feeds/Feeds.astro"; 3 + import Feed from "/components/home/feeds/Feed.astro"; 4 4 import Landing from "/components/home/Landing.astro"; 5 5 import NowPlaying from "/components/home/playing/NowPlaying.astro"; 6 6 // start the sdk setup while other components and requests are ongoing ··· 17 17 <!-- now playing --> 18 18 <NowPlaying /> 19 19 <!-- feeds --> 20 - <Feeds /> 20 + <Feed /> 21 21 <!-- blog --> 22 22 <section class="blog">blog</section> 23 23 </main> ··· 40 40 main { 41 41 display: grid; 42 42 grid-template: 43 - ". landing feeds blog ." min-content 44 - ". playing feeds blog ." 1fr 43 + ". landing feed blog ." min-content 44 + ". playing feed blog ." 1fr 45 45 / auto minmax(25ch, 50ch) minmax(25ch, 50ch) minmax(25ch, 50ch) auto; 46 46 max-width: 100%; 47 47 gap: 20px; ··· 50 50 grid-template: 51 51 ". landing ." min-content 52 52 "playing playing playing" min-content 53 - ". feeds ." 1fr 53 + ". feed ." 1fr 54 54 / auto minmax(30ch, 50ch) auto; 55 55 56 56 & :global(.blog) { ··· 66 66 grid-area: playing; 67 67 } 68 68 69 - & :global(.feeds) { 70 - grid-area: feeds; 69 + & :global(.feed) { 70 + grid-area: feed; 71 71 } 72 72 73 73 & :global(.blog) {