]> Untitled Git - lemmy-ui.git/blobdiff - src/shared/components/post/post.tsx
Use canonical URLs (#1883)
[lemmy-ui.git] / src / shared / components / post / post.tsx
index 25fd1b3f18215d58aba3aa1dfb9600f43346de9e..eeb713a9cef6538bc483776b3cbb0dbf7313b6b5 100644 (file)
@@ -1,5 +1,30 @@
+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,
@@ -51,39 +76,16 @@ import {
   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,
-  debounce,
-  editComment,
-  editWith,
-  enableDownvotes,
-  enableNsfw,
-  getCommentIdFromProps,
-  getCommentParentId,
-  getDepthFromComment,
-  getIdFromProps,
-  isBrowser,
-  isImage,
-  myAuth,
-  restoreScrollPosition,
-  saveScrollPosition,
-  setIsoData,
-  setupTippy,
-  toast,
-  updateCommunityBlock,
-  updatePersonBlock,
-} from "../../utils";
+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";
@@ -93,6 +95,11 @@ import { PostListing } from "./post-listing";
 
 const commentsShownInterval = 15;
 
+type PostData = RouteDataResponse<{
+  postRes: GetPostResponse;
+  commentsRes: GetCommentsResponse;
+}>;
+
 interface PostState {
   postId?: number;
   commentId?: number;
@@ -110,7 +117,7 @@ interface PostState {
 }
 
 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" },
@@ -169,7 +176,7 @@ export class Post extends Component<any, PostState> {
 
     // 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,
@@ -220,13 +227,12 @@ export class Post extends Component<any, PostState> {
     }
   }
 
-  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;
@@ -252,10 +258,10 @@ export class Post extends Component<any, PostState> {
       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() {
@@ -343,10 +349,11 @@ export class Post extends Component<any, PostState> {
         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}
               />
@@ -390,10 +397,10 @@ export class Post extends Component<any, PostState> {
               />
               <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
@@ -410,8 +417,10 @@ export class Post extends Component<any, PostState> {
                 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>
         );
       }
@@ -419,79 +428,102 @@ export class Post extends Component<any, PostState> {
   }
 
   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>
       </>
@@ -580,17 +612,17 @@ export class Post extends Component<any, PostState> {
           {!!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>
               )}
             </>
@@ -824,14 +856,14 @@ export class Post extends Component<any, PostState> {
   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"));
     }
   }
 
@@ -970,7 +1002,7 @@ export class Post extends Component<any, PostState> {
 
   purgeItem(purgeRes: RequestState<PurgeItemResponse>) {
     if (purgeRes.state == "success") {
-      toast(i18n.t("purge_success"));
+      toast(I18NextService.i18n.t("purge_success"));
       this.context.router.history.push(`/`);
     }
   }