An HTML-only Bluesky frontend

minor formatting changes

+73 -72
+60 -53
actor.js
··· 1 1 import { agent } from "./main.js"; 2 - import { getFacets, getDescriptionFacets } from "./facets.js"; 2 + import { getFacets } from "./facets.js"; 3 3 4 4 const DateTimeFormat = new Intl.DateTimeFormat("en-US", { 5 5 dateStyle: "short", ··· 29 29 30 30 actor.username = actor.displayName ? actor.displayName : actor.handle; 31 31 actor.avatar = actor.avatar ? actor.avatar : "/static/avatar.jpg"; 32 + actor.description = actor.description ? `<tr><td colspan="2"><p>${await getFacets(actor.description).then((res) => res.replaceAll("\n", "<br>"))}</p></td></tr><tr><td colspan="2">&nbsp;</td></tr>` : ""; 32 33 33 34 return ` 34 35 <head> ··· 48 49 </h1> 49 50 </td> 50 51 </tr> 51 - ${ 52 - actor.description 53 - ? `<tr> 54 - <td colspan="2"> 55 - <p>${await getDescriptionFacets(actor.description).then((res) => res.replaceAll("\n", "<br>"))}</p> 56 - </td> 57 - </tr> 58 - <tr> 59 - <td colspan="2">&nbsp;</td> 60 - </tr>` 61 - : `` 62 - } 52 + ${actor.description} 63 53 <tr> 64 54 <td colspan="2"> 65 55 <a href="./followers/"><b>${actor.followersCount}</b> followers</a> ··· 87 77 for (const post of feed) { 88 78 if (post.reply) { 89 79 const reply = post.reply.parent ? post.reply.parent : post.reply.root; 90 - const author = reply.author; 91 80 if (reply.notFound || reply.blocked) { 92 81 feedList.push(` 93 - <tr> 94 - <td> 95 - <table> 96 - <tr><td> 97 - Post was deleted. 98 - </td></tr> 99 - </table> 100 - </td> 101 - </tr> 82 + <tr><td> 83 + <table> 84 + <tr><td> 85 + Post not found. 86 + </td></tr> 87 + </table> 88 + </td></tr> 102 89 `); 103 90 } else { 104 91 feedList.push(` 105 92 <tr> 106 93 <td> 107 94 <table> 108 - <tr><td><b>${reply.author.displayName ? reply.author.displayName : reply.author.handle}</b> (<a href="/profile/${reply.author.handle !== "handle.invalid" ? reply.author.handle : reply.author.did}/">@${reply.author.handle}</a>) &middot; ${DateTimeFormat.format(new Date(reply.record.createdAt))}</td></tr> 109 - <tr><td>${await getFacets(reply.record.text).then((res) => res.replaceAll("\n", "<br>"))}</td></tr> 110 - <tr><td><b>${reply.replyCount}</b> replies &middot; <b>${reply.repostCount}</b> reposts &middot; <b>${reply.likeCount}</b> likes</td></tr> 95 + <tr><td> 96 + <b>${reply.author.displayName ? reply.author.displayName : reply.author.handle}</b> (<a href="/profile/${reply.author.handle !== "handle.invalid" ? reply.author.handle : reply.author.did}/">@${reply.author.handle}</a>) &middot; ${DateTimeFormat.format(new Date(reply.record.createdAt))} 97 + </td></tr> 98 + <tr><td> 99 + <p>${await getFacets(reply.record.text).then((res) => res.replaceAll("\n", "<br>"))}</p> 100 + </td></tr> 101 + <tr><td> 102 + <b>${reply.replyCount}</b> replies &middot; 103 + <b>${reply.repostCount}</b> reposts &middot; 104 + <b>${reply.likeCount}</b> likes 105 + </td></tr> 111 106 </table> 112 107 </td> 113 108 </tr> ··· 119 114 const author = post.post.author; 120 115 121 116 feedList.push(` 122 - <tr> 123 - <td> 124 - ${post.reply ? `<blockquote>` : ``} 125 - <table> 117 + <tr><td> 118 + ${post.reply ? `<blockquote>` : ``} 119 + <table> 126 120 ${actor.did !== author.did ? `<tr><td><i>Reposted by ${actor.displayName ? actor.displayName : actor.handle}</i></td></tr>` : ``} 127 - <tr><td><b>${author.displayName ? author.displayName : author.handle}</b> (<a href="/profile/${author.handle !== "handle.invalid" ? author.handle : author.did }/">@${author.handle}</a>) &middot; ${DateTimeFormat.format(new Date(record.createdAt))}</td></tr> 128 - <tr><td><p>${await getFacets(record.text).then((res) => res.replaceAll("\n", "<br>"))}</p></td></tr> 121 + <tr><td> 122 + <b>${author.displayName ? author.displayName : author.handle}</b> (<a href="/profile/${author.handle !== "handle.invalid" ? author.handle : author.did }/">@${author.handle}</a>) 123 + &middot; 124 + ${DateTimeFormat.format(new Date(record.createdAt))} 125 + </td></tr> 129 126 <tr><td> 130 - ${/*post.post.record.embed ? `<pre>${JSON.stringify(post.post.record.embed, null, 2)}</pre>` : */``} 127 + <p>${await getFacets(record.text).then((res) => res.replaceAll("\n", "<br>"))}</p> 131 128 </td></tr> 132 - <tr><td><b>${post.post.replyCount}</b> replies &middot; <b>${post.post.repostCount}</b> reposts &middot; <b>${post.post.likeCount}</b> likes</td></tr> 133 - </table> 134 - ${post.reply ? `</blockquote><hr>` : `<hr>`} 135 - </td> 136 - </tr> 129 + <tr><td> 130 + ${/*post.post.record.embed ? `<pre>${JSON.stringify(post.post.record.embed, null, 2)}</pre>` : */``} 131 + </td></tr> 132 + <tr><td> 133 + <b>${post.post.replyCount}</b> replies &middot; 134 + <b>${post.post.repostCount}</b> reposts &middot; 135 + <b>${post.post.likeCount}</b> likes 136 + </td></tr> 137 + </table> 138 + ${post.reply ? `</blockquote><hr>` : `<hr>`} 139 + </td></tr> 137 140 `); 138 141 } 139 142 140 143 if (cursor) { 141 144 feedList.push(` 142 - <tr> 143 - <td><br><a href="?cursor=${cursor}">Next page</a></td> 144 - </tr> 145 + <tr><td> 146 + <br> 147 + <a href="?cursor=${cursor}">Next page</a> 148 + </td></tr> 145 149 `); 146 150 } 147 151 ··· 166 170 const followersList = []; 167 171 for (const follower of followers) { 168 172 followersList.push(` 169 - <tr> 170 - <td><b>${follower.displayName ? follower.displayName : follower.handle}</b> (<a href="/profile/${follower.handle !== "handle.invalid" ? follower.handle : follower.did}/">@${follower.handle}</a>)</td> 171 - </tr>`); 173 + <tr><td> 174 + <b>${follower.displayName ? follower.displayName : follower.handle}</b> (<a href="/profile/${follower.handle !== "handle.invalid" ? follower.handle : follower.did}/">@${follower.handle}</a>) 175 + </td></tr>`); 172 176 } 173 177 174 178 if (cursor) { 175 179 followersList.push(` 176 - <tr> 177 - <td><br><a href="?cursor=${cursor}">Next page</a></td> 178 - </tr> 180 + <tr><td> 181 + <br> 182 + <a href="?cursor=${cursor}">Next page</a> 183 + </td></tr> 179 184 `); 180 185 } 181 186 ··· 205 210 const followsList = []; 206 211 for (const follow of follows) { 207 212 followsList.push(` 208 - <tr> 209 - <td><b>${follow.displayName ? follow.displayName : follow.handle}</b> (<a href="/profile/${follow.handle !== "handle.invalid" ? follow.handle : follow.did}/">@${follow.handle}</a>)</td> 210 - </tr>`); 213 + <tr><td> 214 + <b>${follow.displayName ? follow.displayName : follow.handle}</b> (<a href="/profile/${follow.handle !== "handle.invalid" ? follow.handle : follow.did}/">@${follow.handle}</a>) 215 + </td> 216 + </tr>`); 211 217 } 212 218 213 219 if (cursor) { 214 220 followsList.push(` 215 - <tr> 216 - <td><br><a href="?cursor=${cursor}">Next page</a></td> 217 - </tr> 221 + <tr><td> 222 + <br> 223 + <a href="?cursor=${cursor}">Next page</a> 224 + </td></tr> 218 225 `); 219 226 } 220 227
+1 -1
deno.json
··· 1 1 { 2 2 "tasks": { 3 - "dev": "deno run -A --watch main.js", 3 + "dev": "deno run -A --watch --quiet main.js", 4 4 "clean": "deno fmt && deno lint" 5 5 } 6 6 }
+12 -18
main.js
··· 30 30 31 31 try { 32 32 // PROFILE 33 - const profilePattern = new URLPattern({ pathname: "/profile/:actor/" }); 33 + const profilePattern = new URLPattern({ pathname: "/profile/:identifier/:page?/" }); 34 34 if (profilePattern.test(url)) { 35 - const actorName = profilePattern.exec(url)?.pathname.groups.actor; 36 - const actor = new Actor(actorName); 35 + const identifier = profilePattern.exec(url)?.pathname.groups.identifier, 36 + page = profilePattern.exec(url)?.pathname.groups.page; 37 + 38 + // TODO: move API calls out 39 + 40 + const actor = new Actor(identifier); 37 41 const cursor = query.get("cursor"); 38 - if (cursor) return new Response(await actor.HTML(cursor), html_headers); 39 - else return new Response(await actor.HTML(), html_headers); 40 - } 41 42 42 - // RAW PROFILE 43 - const rawProfilePattern = new URLPattern({ 44 - pathname: "/raw/profile/:actor/", 45 - }); 46 - if (rawProfilePattern.test(url)) { 47 - const actorName = rawProfilePattern.exec(url)?.pathname.groups.actor; 48 - const actor = new Actor(actorName); 43 + // TODO: move pages here 49 44 50 - return new Response(await actor.Raw(), json_headers); 45 + if (cursor) return new Response(await actor.HTML(cursor), html_headers); 46 + else return new Response(await actor.HTML(), html_headers); 51 47 } 52 48 53 - // FOLLOWERS, FOLLOWING 54 - const pageProfilePattern = new URLPattern({ 55 - pathname: "/profile/:actor/:page/", 56 - }); 49 + /* 57 50 if (pageProfilePattern.test(url)) { 58 51 const actorName = pageProfilePattern.exec(url)?.pathname.groups.actor; 59 52 const pageName = pageProfilePattern.exec(url)?.pathname.groups.page; ··· 72 65 } else return new Response(await actor.Follows(), html_headers); 73 66 } 74 67 } 68 + */ 75 69 } catch (error) { 76 70 return new Response( 77 71 `<head><meta name="color-scheme" content="light dark"></head>\n${error}`,