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 9 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
155 changes: 155 additions & 0 deletions src/shared/components/common/loading-skeleton.tsx
@@ -0,0 +1,155 @@
import { Component } from "inferno";

interface LoadingSkeletonProps {
itemCount?: number;
}

interface LoadingSkeletonLineProps {
size: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12;
dessalines marked this conversation as resolved.
Show resolved Hide resolved
}

class LoadingSkeletonLine extends Component<LoadingSkeletonLineProps, any> {
render() {
const className = "placeholder placeholder-lg col-" + this.props.size;
return (
<p className="placeholder-glow" style={{ margin: "0" }}>
rodrigo-fm marked this conversation as resolved.
Show resolved Hide resolved
<span className={className} style={{ height: "1.3rem" }}></span>
</p>
);
}
}

export class PostsLoadingSkeleton extends Component<LoadingSkeletonProps, any> {
render() {
return Array.from({ length: this.props.itemCount ?? 10 }, (_, index) => (
<PostsLoadingSkeletonItem key={index} />
));
}
}

class PostThumbnailLoadingSkeleton extends Component<any, any> {
render() {
return (
<div className="thumbnail rounded d-flex justify-content-center placeholder-glow">
<span
className="placeholder placeholder-lg"
style={{ height: "100%", width: "100%", "border-radius": "0.5rem" }}
rodrigo-fm marked this conversation as resolved.
Show resolved Hide resolved
></span>
</div>
);
}
}

class PostsLoadingSkeletonItem extends Component<any, 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">
<LoadingSkeletonLine size={12} />
<LoadingSkeletonLine size={8} />
<LoadingSkeletonLine size={6} />
</div>
<div className="col flex-grow-0">
<PostThumbnailLoadingSkeleton />
</div>
</div>
</div>
</div>
{/* desktop */}
<div className="d-none d-sm-block my-3">
<div className="col flex-grow-1">
<div className="row">
<div className="col flex-grow-0">
<PostThumbnailLoadingSkeleton />
</div>
<div className="col flex-grow-1">
<LoadingSkeletonLine size={12} />
<LoadingSkeletonLine size={8} />
<LoadingSkeletonLine size={4} />
</div>
</div>
</div>
</div>
</>
);
}
rodrigo-fm marked this conversation as resolved.
Show resolved Hide resolved
}

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

class TrendingCommunitiesLoadingSkeletonItem extends Component<any, 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="d-flex placeholder-glow"
style={{ width: "1.5rem", height: "1.5rem" }}
rodrigo-fm marked this conversation as resolved.
Show resolved Hide resolved
rodrigo-fm marked this conversation as resolved.
Show resolved Hide resolved
>
<span
className="placeholder placeholder-lg"
style={{
rodrigo-fm marked this conversation as resolved.
Show resolved Hide resolved
height: "100%",
width: "100%",
"border-radius": "100%",
}}
></span>
</div>
</div>
<div className="col flex-grow-1" style={{ "padding-right": "0px" }}>
<LoadingSkeletonLine size={12} />
</div>
</div>
</div>
);
}
}

export class CommentsLoadingSkeleton extends Component<any, any> {
render() {
return Array.from({ length: this.props.itemCount ?? 10 }, (_, index) => (
<CommentsLoadingSkeletonItem key={index} />
));
}
}

class CommentsLoadingSkeletonItem extends Component<any, any> {
render() {
return (
<div className="col flex-grow-1 my-2 p-2">
<div className="row">
<div className="col flex-grow-1">
<LoadingSkeletonLine size={6} />
<LoadingSkeletonLine size={12} />
<LoadingSkeletonLine size={7} />
<LoadingSkeletonLine size={4} />
</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