+import {
+ buildCommentsTree,
+ commentsToFlatNodes,
+ editComment,
+ editWith,
+ enableDownvotes,
+ enableNsfw,
+ getCommentIdFromProps,
+ getCommentParentId,
+ getDepthFromComment,
+ getIdFromProps,
+ myAuth,
+ setIsoData,
+ updateCommunityBlock,
+ updatePersonBlock,
+} from "@utils/app";
+import {
+ isBrowser,
+ restoreScrollPosition,
+ saveScrollPosition,
+} from "@utils/browser";
+import { debounce, randomStr } from "@utils/helpers";
+import { isImage } from "@utils/media";
+import { RouteDataResponse } from "@utils/types";
import autosize from "autosize";
-import { Component, createRef, linkEvent, RefObject } from "inferno";
+import classNames from "classnames";
+import { Component, RefObject, createRef, linkEvent } from "inferno";
import {
AddAdmin,
AddModToCommunity,
SavePost,
TransferCommunity,
} from "lemmy-js-client";
-import { i18n } from "../../i18next";
+import { commentTreeMaxDepth } from "../../config";
import {
CommentNodeI,
CommentViewType,
InitialFetchRequest,
} from "../../interfaces";
-import { UserService } from "../../services";
-import { FirstLoadService } from "../../services/FirstLoadService";
+import { FirstLoadService, I18NextService, UserService } from "../../services";
import { HttpService, RequestState } from "../../services/HttpService";
-import {
- buildCommentsTree,
- commentsToFlatNodes,
- commentTreeMaxDepth,
- editComment,
- editWith,
- enableDownvotes,
- enableNsfw,
- getCommentIdFromProps,
- getCommentParentId,
- getDepthFromComment,
- getIdFromProps,
- isImage,
- myAuth,
- restoreScrollPosition,
- saveScrollPosition,
- setIsoData,
- setupTippy,
- toast,
- updateCommunityBlock,
- updatePersonBlock,
-} from "../../utils";
-import { isBrowser } from "../../utils/browser/is-browser";
-import { debounce } from "../../utils/helpers/debounce";
+import { setupTippy } from "../../tippy";
+import { toast } from "../../toast";
import { CommentForm } from "../comment/comment-form";
import { CommentNodes } from "../comment/comment-nodes";
import { HtmlTags } from "../common/html-tags";
const commentsShownInterval = 15;
+type PostData = RouteDataResponse<{
+ postRes: GetPostResponse;
+ commentsRes: GetCommentsResponse;
+}>;
+
interface PostState {
postId?: number;
commentId?: number;
}
export class Post extends Component<any, PostState> {
- private isoData = setIsoData(this.context);
+ private isoData = setIsoData<PostData>(this.context);
private commentScrollDebounced: () => void;
state: PostState = {
postRes: { state: "empty" },
// Only fetch the data if coming from another route
if (FirstLoadService.isFirstLoad) {
- const [postRes, commentsRes] = this.isoData.routeData;
+ const { commentsRes, postRes } = this.isoData.routeData;
this.state = {
...this.state,
}
}
- static fetchInitialData({
- auth,
+ static async fetchInitialData({
client,
path,
- }: InitialFetchRequest): Promise<any>[] {
+ auth,
+ }: InitialFetchRequest): Promise<PostData> {
const pathSplit = path.split("/");
- const promises: Promise<RequestState<any>>[] = [];
const pathType = pathSplit.at(1);
const id = pathSplit.at(2) ? Number(pathSplit.at(2)) : undefined;
commentsForm.parent_id = id;
}
- promises.push(client.getPost(postForm));
- promises.push(client.getComments(commentsForm));
-
- return promises;
+ return {
+ postRes: await client.getPost(postForm),
+ commentsRes: await client.getComments(commentsForm),
+ };
}
componentWillUnmount() {
const res = this.state.postRes.data;
return (
<div className="row">
- <div className="col-12 col-md-8 mb-3">
+ <main className="col-12 col-md-8 col-lg-9 mb-3">
<HtmlTags
title={this.documentTitle}
path={this.context.router.route.match.url}
+ canonicalPath={res.post_view.post.ap_id}
image={this.imageTag}
description={res.post_view.post.body}
/>
/>
<div className="d-block d-md-none">
<button
- className="btn btn-secondary d-inline-block mb-2 mr-3"
+ className="btn btn-secondary d-inline-block mb-2 me-3"
onClick={linkEvent(this, this.handleShowSidebarMobile)}
>
- {i18n.t("sidebar")}{" "}
+ {I18NextService.i18n.t("sidebar")}{" "}
<Icon
icon={
this.state.showSidebarMobile
this.commentsTree()}
{this.state.commentViewType == CommentViewType.Flat &&
this.commentsFlat()}
- </div>
- <div className="d-none d-md-block col-md-4">{this.sidebar()}</div>
+ </main>
+ <aside className="d-none d-md-block col-md-4 col-lg-3">
+ {this.sidebar()}
+ </aside>
</div>
);
}
}
render() {
- return <div className="container-lg">{this.renderPostRes()}</div>;
+ return <div className="post container-lg">{this.renderPostRes()}</div>;
}
sortRadios() {
+ const radioId =
+ this.state.postRes.state === "success"
+ ? this.state.postRes.data.post_view.post.id
+ : randomStr();
+
return (
<>
- <div className="btn-group btn-group-toggle flex-wrap mr-3 mb-2">
+ <div
+ className="btn-group btn-group-toggle flex-wrap me-3 mb-2"
+ role="group"
+ >
+ <input
+ id={`${radioId}-hot`}
+ type="radio"
+ className="btn-check"
+ value={"Hot"}
+ checked={this.state.commentSort === "Hot"}
+ onChange={linkEvent(this, this.handleCommentSortChange)}
+ />
<label
- className={`btn btn-outline-secondary pointer ${
- this.state.commentSort === "Hot" && "active"
- }`}
+ htmlFor={`${radioId}-hot`}
+ className={classNames("btn btn-outline-secondary pointer", {
+ active: this.state.commentSort === "Hot",
+ })}
>
- {i18n.t("hot")}
- <input
- type="radio"
- value={"Hot"}
- checked={this.state.commentSort === "Hot"}
- onChange={linkEvent(this, this.handleCommentSortChange)}
- />
+ {I18NextService.i18n.t("hot")}
</label>
+ <input
+ id={`${radioId}-top`}
+ type="radio"
+ className="btn-check"
+ value={"Top"}
+ checked={this.state.commentSort === "Top"}
+ onChange={linkEvent(this, this.handleCommentSortChange)}
+ />
<label
- className={`btn btn-outline-secondary pointer ${
- this.state.commentSort === "Top" && "active"
- }`}
+ htmlFor={`${radioId}-top`}
+ className={classNames("btn btn-outline-secondary pointer", {
+ active: this.state.commentSort === "Top",
+ })}
>
- {i18n.t("top")}
- <input
- type="radio"
- value={"Top"}
- checked={this.state.commentSort === "Top"}
- onChange={linkEvent(this, this.handleCommentSortChange)}
- />
+ {I18NextService.i18n.t("top")}
</label>
+ <input
+ id={`${radioId}-new`}
+ type="radio"
+ className="btn-check"
+ value={"New"}
+ checked={this.state.commentSort === "New"}
+ onChange={linkEvent(this, this.handleCommentSortChange)}
+ />
<label
- className={`btn btn-outline-secondary pointer ${
- this.state.commentSort === "New" && "active"
- }`}
+ htmlFor={`${radioId}-new`}
+ className={classNames("btn btn-outline-secondary pointer", {
+ active: this.state.commentSort === "New",
+ })}
>
- {i18n.t("new")}
- <input
- type="radio"
- value={"New"}
- checked={this.state.commentSort === "New"}
- onChange={linkEvent(this, this.handleCommentSortChange)}
- />
+ {I18NextService.i18n.t("new")}
</label>
+ <input
+ id={`${radioId}-old`}
+ type="radio"
+ className="btn-check"
+ value={"Old"}
+ checked={this.state.commentSort === "Old"}
+ onChange={linkEvent(this, this.handleCommentSortChange)}
+ />
<label
- className={`btn btn-outline-secondary pointer ${
- this.state.commentSort === "Old" && "active"
- }`}
+ htmlFor={`${radioId}-old`}
+ className={classNames("btn btn-outline-secondary pointer", {
+ active: this.state.commentSort === "Old",
+ })}
>
- {i18n.t("old")}
- <input
- type="radio"
- value={"Old"}
- checked={this.state.commentSort === "Old"}
- onChange={linkEvent(this, this.handleCommentSortChange)}
- />
+ {I18NextService.i18n.t("old")}
</label>
</div>
- <div className="btn-group btn-group-toggle flex-wrap mb-2">
+ <div className="btn-group btn-group-toggle flex-wrap mb-2" role="group">
+ <input
+ id={`${radioId}-chat`}
+ type="radio"
+ className="btn-check"
+ value={CommentViewType.Flat}
+ checked={this.state.commentViewType === CommentViewType.Flat}
+ onChange={linkEvent(this, this.handleCommentViewTypeChange)}
+ />
<label
- className={`btn btn-outline-secondary pointer ${
- this.state.commentViewType === CommentViewType.Flat && "active"
- }`}
+ htmlFor={`${radioId}-chat`}
+ className={classNames("btn btn-outline-secondary pointer", {
+ active: this.state.commentViewType === CommentViewType.Flat,
+ })}
>
- {i18n.t("chat")}
- <input
- type="radio"
- value={CommentViewType.Flat}
- checked={this.state.commentViewType === CommentViewType.Flat}
- onChange={linkEvent(this, this.handleCommentViewTypeChange)}
- />
+ {I18NextService.i18n.t("chat")}
</label>
</div>
</>
{!!this.state.commentId && (
<>
<button
- className="pl-0 d-block btn btn-link text-muted"
+ className="ps-0 d-block btn btn-link text-muted"
onClick={linkEvent(this, this.handleViewPost)}
>
- {i18n.t("view_all_comments")} ➔
+ {I18NextService.i18n.t("view_all_comments")} ➔
</button>
{showContextButton && (
<button
- className="pl-0 d-block btn btn-link text-muted"
+ className="ps-0 d-block btn btn-link text-muted"
onClick={linkEvent(this, this.handleViewContext)}
>
- {i18n.t("show_context")} ➔
+ {I18NextService.i18n.t("show_context")} ➔
</button>
)}
</>
async handleCommentReport(form: CreateCommentReport) {
const reportRes = await HttpService.client.createCommentReport(form);
if (reportRes.state == "success") {
- toast(i18n.t("report_created"));
+ toast(I18NextService.i18n.t("report_created"));
}
}
async handlePostReport(form: CreatePostReport) {
const reportRes = await HttpService.client.createPostReport(form);
if (reportRes.state == "success") {
- toast(i18n.t("report_created"));
+ toast(I18NextService.i18n.t("report_created"));
}
}
purgeItem(purgeRes: RequestState<PurgeItemResponse>) {
if (purgeRes.state == "success") {
- toast(i18n.t("purge_success"));
+ toast(I18NextService.i18n.t("purge_success"));
this.context.router.history.push(`/`);
}
}