Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adds a loading skeleton to posts, comments and trending communities #2311

Merged
merged 17 commits into from Mar 27, 2024
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion lemmy-translations
31 changes: 31 additions & 0 deletions src/assets/css/loading-skeleton.css
rodrigo-fm marked this conversation as resolved.
Show resolved Hide resolved
@@ -0,0 +1,31 @@
.skeleton-pulse {
animation: skeleton-loading 1.2s linear infinite alternate;
}

.skeleton-pulse[data-bs-theme="dark"] {
background-color: #5a5a5a;
}

.skeleton-pulse[data-bs-theme="light"] {
background-color: #dadada;
}

.trending-community-icon-skeleton {
border-radius: 100%;
width: 1.5rem;
height: 1.5rem;
}

.post-line-loading-skeleton {
height: 1.5rem;
margin-bottom: 4px;
}
@keyframes skeleton-loading {
0% {
opacity: 100%;
}

100% {
opacity: 35%;
}
}
1 change: 1 addition & 0 deletions src/shared/components/app/styles.scss
Expand Up @@ -4,3 +4,4 @@
@import "../../../../node_modules/tippy.js/dist/tippy.css";
@import "../../../../node_modules/bootstrap/dist/css/bootstrap-utilities.min.css";
@import "../../../assets/css/main.css";
@import "../../../assets/css/loading-skeleton.css"
192 changes: 192 additions & 0 deletions src/shared/components/common/loading-skeleton.tsx
@@ -0,0 +1,192 @@
import { setIsoData } from "@utils/app";
import dataBsTheme from "@utils/browser/data-bs-theme";
import { Component } from "inferno";

interface LoadingSkeletonProps {
itemCount?: number;
}

interface LoadingSkeletonItemProps {
theme: string;
dessalines marked this conversation as resolved.
Show resolved Hide resolved
}

export class PostsLoadingSkeleton extends Component<LoadingSkeletonProps, any> {
private theme: string = dataBsTheme(setIsoData(this.context).site_res);
dessalines marked this conversation as resolved.
Show resolved Hide resolved

render() {
return Array.from({ length: this.props.itemCount ?? 10 }, (_, index) => (
<PostsLoadingSkeletonItem key={index} theme={this.theme} />
));
}
}

class PostThumbnailLoadingSkeleton extends Component<
LoadingSkeletonItemProps,
any
> {
render() {
return (
<div
className="thumbnail rounded d-flex justify-content-center skeleton-pulse"
data-bs-theme={this.props.theme}
></div>
);
}
}

class PostsLoadingSkeletonItem extends Component<
LoadingSkeletonItemProps,
any
> {
render() {
return (
<>
{/* mobile */}
<div className="d-block d-sm-none">
<div className="col flex-grow-1 mt-2">
<div className="row">
<div className="col flex-grow-1">
<div
className="col-12 post-line-loading-skeleton skeleton-pulse"
data-bs-theme={this.props.theme}
></div>
<div
className="col-8 post-line-loading-skeleton skeleton-pulse"
data-bs-theme={this.props.theme}
></div>
<div
className="col-6 post-line-loading-skeleton skeleton-pulse"
data-bs-theme={this.props.theme}
></div>
</div>
<div className="col flex-grow-0">
<PostThumbnailLoadingSkeleton theme={this.props.theme} />
</div>
</div>
</div>
</div>
{/* desktop */}
<div className="d-none d-sm-block">
<div className="col flex-grow-1 mt-2">
<div className="row">
<div className="col flex-grow-0">
<PostThumbnailLoadingSkeleton theme={this.props.theme} />
</div>
<div className="col flex-grow-1">
<div
className="col-12 post-line-loading-skeleton skeleton-pulse"
data-bs-theme={this.props.theme}
></div>
<div
className="col-8 post-line-loading-skeleton skeleton-pulse"
data-bs-theme={this.props.theme}
></div>
<div
className="col-4 post-line-loading-skeleton skeleton-pulse"
data-bs-theme={this.props.theme}
></div>
</div>
</div>
</div>
</div>
</>
);
}
}

export class TrendingCommunitiesLoadingSkeleton extends Component<
LoadingSkeletonProps,
any
> {
private theme: string = dataBsTheme(setIsoData(this.context).site_res);

render() {
return (
<div className="mb-2">
{Array.from({ length: this.props.itemCount ?? 10 }, (_, index) => (
<TrendingCommunitiesLoadingSkeletonItem
key={index}
theme={this.theme}
/>
))}
</div>
);
}
}

class TrendingCommunitiesLoadingSkeletonItem extends Component<
LoadingSkeletonItemProps,
any
> {
render() {
return (
<div
className="col flex-grow-1 mt-2"
style={{
rodrigo-fm marked this conversation as resolved.
Show resolved Hide resolved
"padding-left": "calc(var(--bs-gutter-x) *0.5)",
"padding-right": "calc(var(--bs-gutter-x) * 1)",
}}
>
<div className="row">
<div className="col flex-grow-0" style={{ "padding-right": "0px" }}>
<div
className="trending-community-icon-skeleton d-flex skeleton-pulse"
data-bs-theme={this.props.theme}
></div>
</div>
<div className="col flex-grow-1" style={{ "padding-right": "0px" }}>
<div
className="col-12 post-line-loading-skeleton skeleton-pulse"
data-bs-theme={this.props.theme}
></div>
</div>
</div>
</div>
);
}
}

export class CommentsLoadingSkeleton extends Component<
LoadingSkeletonProps,
any
> {
private theme: string = dataBsTheme(setIsoData(this.context).site_res);

render() {
return Array.from({ length: this.props.itemCount ?? 10 }, (_, index) => (
<CommentsLoadingSkeletonItem key={index} theme={this.theme} />
));
}
}

class CommentsLoadingSkeletonItem extends Component<
LoadingSkeletonItemProps,
any
> {
render() {
return (
<div className="col flex-grow-1 my-2 p-2">
<div className="row">
<div className="col flex-grow-1">
<div
className="col-6 post-line-loading-skeleton skeleton-pulse"
data-bs-theme={this.props.theme}
></div>
<div
className="col-12 post-line-loading-skeleton skeleton-pulse"
data-bs-theme={this.props.theme}
></div>
<div
className="col-7 post-line-loading-skeleton skeleton-pulse"
data-bs-theme={this.props.theme}
></div>
<div
className="col-4 post-line-loading-skeleton skeleton-pulse"
data-bs-theme={this.props.theme}
></div>
</div>
</div>
</div>
);
}
}
16 changes: 6 additions & 10 deletions src/shared/components/community/community.tsx
Expand Up @@ -101,6 +101,10 @@ import { PostListings } from "../post/post-listings";
import { CommunityLink } from "./community-link";
import { PaginatorCursor } from "../common/paginator-cursor";
import { getHttpBaseInternal } from "../../utils/env";
import {
CommentsLoadingSkeleton,
PostsLoadingSkeleton,
} from "../common/loading-skeleton";

type CommunityData = RouteDataResponse<{
communityRes: GetCommunityResponse;
Expand Down Expand Up @@ -417,11 +421,7 @@ export class Community extends Component<
if (dataType === DataType.Post) {
switch (this.state.postsRes.state) {
case "loading":
return (
<h5>
<Spinner large />
</h5>
);
return <PostsLoadingSkeleton />;
case "success":
return (
<PostListings
Expand Down Expand Up @@ -454,11 +454,7 @@ export class Community extends Component<
} else {
switch (this.state.commentsRes.state) {
case "loading":
return (
<h5>
<Spinner large />
</h5>
);
return <CommentsLoadingSkeleton />;
case "success":
return (
<CommentNodes
Expand Down
25 changes: 9 additions & 16 deletions src/shared/components/home/home.tsx
Expand Up @@ -92,14 +92,19 @@ import { toast } from "../../toast";
import { CommentNodes } from "../comment/comment-nodes";
import { DataTypeSelect } from "../common/data-type-select";
import { HtmlTags } from "../common/html-tags";
import { Icon, Spinner } from "../common/icon";
import { Icon } from "../common/icon";
import { ListingTypeSelect } from "../common/listing-type-select";
import { SortSelect } from "../common/sort-select";
import { CommunityLink } from "../community/community-link";
import { PostListings } from "../post/post-listings";
import { SiteSidebar } from "./site-sidebar";
import { PaginatorCursor } from "../common/paginator-cursor";
import { getHttpBaseInternal } from "../../utils/env";
import {
CommentsLoadingSkeleton,
PostsLoadingSkeleton,
TrendingCommunitiesLoadingSkeleton,
} from "../common/loading-skeleton";

interface HomeState {
postsRes: RequestState<GetPostsResponse>;
Expand Down Expand Up @@ -511,11 +516,7 @@ export class Home extends Component<any, HomeState> {
trendingCommunities() {
switch (this.state.trendingCommunitiesRes?.state) {
case "loading":
return (
<h5>
<Spinner large />
</h5>
);
return <TrendingCommunitiesLoadingSkeleton itemCount={5} />;
case "success": {
const trending = this.state.trendingCommunitiesRes.data.communities;
return (
Expand Down Expand Up @@ -684,11 +685,7 @@ export class Home extends Component<any, HomeState> {
case "empty":
return <div style="min-height: 20000px;"></div>;
case "loading":
return (
<h5>
<Spinner large />
</h5>
);
return <PostsLoadingSkeleton />;
case "success": {
const posts = this.state.postsRes.data.posts;
return (
Expand Down Expand Up @@ -724,11 +721,7 @@ export class Home extends Component<any, HomeState> {
} else {
switch (this.state.commentsRes.state) {
case "loading":
return (
<h5>
<Spinner large />
</h5>
);
return <CommentsLoadingSkeleton />;
case "success": {
const comments = this.state.commentsRes.data.comments;
return (
Expand Down
25 changes: 5 additions & 20 deletions src/shared/components/person/inbox.tsx
Expand Up @@ -80,6 +80,7 @@ import { Icon, Spinner } from "../common/icon";
import { Paginator } from "../common/paginator";
import { PrivateMessage } from "../private_message/private-message";
import { getHttpBaseInternal } from "../../utils/env";
import { CommentsLoadingSkeleton } from "../common/loading-skeleton";

enum UnreadOrAll {
Unread,
Expand Down Expand Up @@ -573,11 +574,7 @@ export class Inbox extends Component<any, InboxState> {
this.state.mentionsRes.state === "loading" ||
this.state.messagesRes.state === "loading"
) {
return (
<h1 className="h4">
<Spinner large />
</h1>
);
return <CommentsLoadingSkeleton />;
} else {
return (
<div>{this.buildCombined().map(r => this.renderReplyType(r))}</div>
Expand All @@ -588,11 +585,7 @@ export class Inbox extends Component<any, InboxState> {
replies() {
switch (this.state.repliesRes.state) {
case "loading":
return (
<h1 className="h4">
<Spinner large />
</h1>
);
return <CommentsLoadingSkeleton />;
case "success": {
const replies = this.state.repliesRes.data.replies;
return (
Expand Down Expand Up @@ -635,11 +628,7 @@ export class Inbox extends Component<any, InboxState> {
mentions() {
switch (this.state.mentionsRes.state) {
case "loading":
return (
<h1 className="h4">
<Spinner large />
</h1>
);
return <CommentsLoadingSkeleton />;
case "success": {
const mentions = this.state.mentionsRes.data.mentions;
return (
Expand Down Expand Up @@ -685,11 +674,7 @@ export class Inbox extends Component<any, InboxState> {
messages() {
switch (this.state.messagesRes.state) {
case "loading":
return (
<h1 className="h4">
<Spinner large />
</h1>
);
return <CommentsLoadingSkeleton />;
case "success": {
const messages = this.state.messagesRes.data.private_messages;
return (
Expand Down