[READ-ONLY] Mirror of https://github.com/improsocial/impro An extensible Bluesky client for web impro.social
6

Configure Feed

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

Add feed generator list item skeleton

Grace Kind (May 31, 2026, 12:14 AM -0500) c2a3d981 2821cdae

+96 -3
+1 -1
package.json
··· 1 1 { 2 2 "name": "impro", 3 - "version": "0.14.125", 3 + "version": "0.14.126", 4 4 "type": "module", 5 5 "scripts": { 6 6 "start": "rm -rf build && NODE_ENV=development eleventy --serve",
+27
src/css/style.css
··· 4382 4382 height: 16px; 4383 4383 } 4384 4384 4385 + .feeds-list-item-skeleton { 4386 + pointer-events: none; 4387 + } 4388 + 4389 + .feeds-list-item-skeleton-avatar { 4390 + width: 30px; 4391 + height: 30px; 4392 + border-radius: var(--list-avatar-border-radius); 4393 + background-color: var(--skeleton-color); 4394 + } 4395 + 4396 + .feeds-list-item-skeleton-title { 4397 + height: 14px; 4398 + width: 45%; 4399 + border-radius: var(--skeleton-border-radius); 4400 + background-color: var(--skeleton-color); 4401 + margin-bottom: 6px; 4402 + margin-top: 2px; 4403 + } 4404 + 4405 + .feeds-list-item-skeleton-creator { 4406 + height: 12px; 4407 + width: 30%; 4408 + border-radius: var(--skeleton-border-radius); 4409 + background-color: var(--skeleton-color); 4410 + } 4411 + 4385 4412 .post-composer { 4386 4413 border: none; 4387 4414 border-radius: var(--sheet-border-radius) var(--sheet-border-radius) 0 0;
+18
src/js/templates/feedGeneratorListItemSkeleton.template.js
··· 1 + import { html } from "/js/lib/lit-html.js"; 2 + 3 + export function feedGeneratorListItemSkeletonTemplate() { 4 + return html` 5 + <div 6 + class="feeds-list-item feeds-list-item-skeleton" 7 + data-testid="feed-generator-list-item-skeleton" 8 + > 9 + <div class="feeds-list-item-avatar"> 10 + <div class="feeds-list-item-skeleton-avatar skeleton-animate"></div> 11 + </div> 12 + <div class="feeds-list-item-content"> 13 + <div class="feeds-list-item-skeleton-title skeleton-animate"></div> 14 + <div class="feeds-list-item-skeleton-creator skeleton-animate"></div> 15 + </div> 16 + </div> 17 + `; 18 + }
+4 -1
src/js/views/feeds.view.js
··· 5 5 import { mainLayoutTemplate } from "/js/templates/mainLayout.template.js"; 6 6 import { headerTemplate } from "/js/templates/header.template.js"; 7 7 import { feedGeneratorListItemTemplate } from "/js/templates/feedGeneratorListItem.template.js"; 8 + import { feedGeneratorListItemSkeletonTemplate } from "/js/templates/feedGeneratorListItemSkeleton.template.js"; 8 9 9 10 class FeedsView extends View { 10 11 async render({ ··· 69 70 ` 70 71 : feedGeneratorListItemTemplate({ feedGenerator }), 71 72 ) 72 - : html`<div class="loading-spinner"></div>`} 73 + : Array.from({ length: 5 }).map(() => 74 + feedGeneratorListItemSkeletonTemplate(), 75 + )} 73 76 </div> 74 77 `, 75 78 })}
+4 -1
src/js/views/profile.view.js
··· 17 17 import { showToast } from "/js/toasts.js"; 18 18 import { tabBarTemplate } from "/js/templates/tabBar.template.js"; 19 19 import { feedGeneratorListItemTemplate } from "/js/templates/feedGeneratorListItem.template.js"; 20 + import { feedGeneratorListItemSkeletonTemplate } from "/js/templates/feedGeneratorListItemSkeleton.template.js"; 20 21 import "/js/components/edit-profile-dialog.js"; 21 22 22 23 class ProfileView extends View { ··· 171 172 function actorFeedsTemplate({ actorFeeds, onLoadMore }) { 172 173 if (!actorFeeds) { 173 174 return html`<div class="feeds-list"> 174 - <div class="loading-spinner"></div> 175 + ${Array.from({ length: 5 }).map(() => 176 + feedGeneratorListItemSkeletonTemplate(), 177 + )} 175 178 </div>`; 176 179 } 177 180 if (actorFeeds.feeds.length === 0) {
+42
tests/unit/specs/templates/feedGeneratorListItemSkeleton.template.test.js
··· 1 + import { TestSuite } from "../../testSuite.js"; 2 + import { assert } from "../../testHelpers.js"; 3 + import { feedGeneratorListItemSkeletonTemplate } from "/js/templates/feedGeneratorListItemSkeleton.template.js"; 4 + import { render } from "/js/lib/lit-html.js"; 5 + 6 + const t = new TestSuite("feedGeneratorListItemSkeletonTemplate"); 7 + 8 + t.describe("feedGeneratorListItemSkeletonTemplate", (it) => { 9 + it("should render the skeleton element", () => { 10 + const container = document.createElement("div"); 11 + render(feedGeneratorListItemSkeletonTemplate(), container); 12 + assert( 13 + container.querySelector( 14 + "[data-testid='feed-generator-list-item-skeleton']", 15 + ) !== null, 16 + ); 17 + }); 18 + 19 + it("should render the avatar placeholder", () => { 20 + const container = document.createElement("div"); 21 + render(feedGeneratorListItemSkeletonTemplate(), container); 22 + assert( 23 + container.querySelector(".feeds-list-item-skeleton-avatar") !== null, 24 + ); 25 + }); 26 + 27 + it("should render the title placeholder", () => { 28 + const container = document.createElement("div"); 29 + render(feedGeneratorListItemSkeletonTemplate(), container); 30 + assert(container.querySelector(".feeds-list-item-skeleton-title") !== null); 31 + }); 32 + 33 + it("should render the creator placeholder", () => { 34 + const container = document.createElement("div"); 35 + render(feedGeneratorListItemSkeletonTemplate(), container); 36 + assert( 37 + container.querySelector(".feeds-list-item-skeleton-creator") !== null, 38 + ); 39 + }); 40 + }); 41 + 42 + await t.run();