]> Untitled Git - lemmy-ui.git/commitdiff
Comment Tree paging (#726)
authorDessalines <dessalines@users.noreply.github.com>
Sat, 30 Jul 2022 13:28:08 +0000 (09:28 -0400)
committerGitHub <noreply@github.com>
Sat, 30 Jul 2022 13:28:08 +0000 (15:28 +0200)
* Updating translations.

* Forgot to add comment-sort-select

* Upgrading deps

19 files changed:
lemmy-translations
package.json
src/shared/components/comment/comment-form.tsx
src/shared/components/comment/comment-node.tsx
src/shared/components/comment/comment-nodes.tsx
src/shared/components/comment/comment-report.tsx
src/shared/components/common/comment-sort-select.tsx [new file with mode: 0644]
src/shared/components/common/sort-select.tsx
src/shared/components/community/community.tsx
src/shared/components/home/home.tsx
src/shared/components/person/inbox.tsx
src/shared/components/person/person-details.tsx
src/shared/components/post/post-listing.tsx
src/shared/components/post/post.tsx
src/shared/components/search.tsx
src/shared/interfaces.ts
src/shared/routes.ts
src/shared/utils.ts
yarn.lock

index 7c1b691af63845a2fe2f8219b4620b8db3c9c3ba..7c3945745dcd07774b19453803f7f14ab80ab3d3 160000 (submodule)
@@ -1 +1 @@
-Subproject commit 7c1b691af63845a2fe2f8219b4620b8db3c9c3ba
+Subproject commit 7c3945745dcd07774b19453803f7f14ab80ab3d3
index 4213e892ee4c17b74c939462be199ef6558c5dbc..31e9c8780738ff5db7f0c7db80517fc459f8cd6c 100644 (file)
@@ -77,7 +77,7 @@
     "eslint-plugin-prettier": "^4.2.1",
     "husky": "^8.0.1",
     "import-sort-style-module": "^6.0.0",
-    "lemmy-js-client": "0.17.0-rc.38",
+    "lemmy-js-client": "0.17.0-rc.39",
     "lint-staged": "^13.0.3",
     "mini-css-extract-plugin": "^2.6.1",
     "node-fetch": "^2.6.1",
index 7abf39b70a2fcbb76560b828f7d19339529be493..64841e788687b8805704dd22257f47f47cfe3750 100644 (file)
@@ -3,6 +3,7 @@ import { Component } from "inferno";
 import { T } from "inferno-i18next-dess";
 import { Link } from "inferno-router";
 import {
+  CommentNode as CommentNodeI,
   CommentResponse,
   CreateComment,
   EditComment,
@@ -12,7 +13,6 @@ import {
 } from "lemmy-js-client";
 import { Subscription } from "rxjs";
 import { i18n } from "../../i18next";
-import { CommentNode as CommentNodeI } from "../../interfaces";
 import { UserService, WebSocketService } from "../../services";
 import {
   auth,
index 27e209ee9d2d5ea40f96122b944684cec5dd63ad..d4bd1fe51de9ca7ea83414adc01f0c51f300fe13 100644 (file)
@@ -8,12 +8,16 @@ import {
   BanFromCommunity,
   BanPerson,
   BlockPerson,
+  CommentNode as CommentNodeI,
+  CommentReplyView,
   CommentView,
   CommunityModeratorView,
   CreateCommentLike,
   CreateCommentReport,
   DeleteComment,
-  MarkCommentAsRead,
+  GetComments,
+  ListingType,
+  MarkCommentReplyAsRead,
   MarkPersonMentionAsRead,
   PersonMentionView,
   PersonViewSafe,
@@ -26,11 +30,7 @@ import {
 } from "lemmy-js-client";
 import moment from "moment";
 import { i18n } from "../../i18next";
-import {
-  BanType,
-  CommentNode as CommentNodeI,
-  PurgeType,
-} from "../../interfaces";
+import { BanType, CommentViewType, PurgeType } from "../../interfaces";
 import { UserService, WebSocketService } from "../../services";
 import {
   amCommunityCreator,
@@ -38,6 +38,7 @@ import {
   canAdmin,
   canMod,
   colorList,
+  commentTreeMaxDepth,
   futureDaysToUnixTime,
   isAdmin,
   isBanned,
@@ -82,7 +83,6 @@ interface CommentNodeState {
   score: number;
   upvotes: number;
   downvotes: number;
-  borderColor: string;
   readLoading: boolean;
   saveLoading: boolean;
 }
@@ -99,6 +99,7 @@ interface CommentNodeProps {
   showContext?: boolean;
   showCommunity?: boolean;
   enableDownvotes: boolean;
+  viewType: CommentViewType;
 }
 
 export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
@@ -129,9 +130,6 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
     score: this.props.node.comment_view.counts.score,
     upvotes: this.props.node.comment_view.counts.upvotes,
     downvotes: this.props.node.comment_view.counts.downvotes,
-    borderColor: this.props.node.depth
-      ? colorList[this.props.node.depth % colorList.length]
-      : colorList[0],
     readLoading: false,
     saveLoading: false,
   };
@@ -181,10 +179,23 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
       cv.creator.id
     );
 
+    let borderColor = this.props.node.depth
+      ? colorList[(this.props.node.depth - 1) % colorList.length]
+      : colorList[0];
+    let moreRepliesBorderColor = this.props.node.depth
+      ? colorList[this.props.node.depth % colorList.length]
+      : colorList[0];
+
+    let showMoreChildren =
+      this.props.viewType == CommentViewType.Tree &&
+      !this.state.collapsed &&
+      node.children.length == 0 &&
+      node.comment_view.counts.child_count > 0;
+
     return (
       <div
         className={`comment ${
-          cv.comment.parent_id.isSome() && !this.props.noIndent ? "ml-1" : ""
+          this.props.node.depth && !this.props.noIndent ? "ml-1" : ""
         }`}
       >
         <div
@@ -194,14 +205,12 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
           } ${this.isCommentNew ? "mark" : ""}`}
           style={
             !this.props.noIndent &&
-            cv.comment.parent_id.isSome() &&
-            `border-left: 2px ${this.state.borderColor} solid !important`
+            this.props.node.depth &&
+            `border-left: 2px ${borderColor} solid !important`
           }
         >
           <div
-            class={`${
-              !this.props.noIndent && cv.comment.parent_id.isSome() && "ml-2"
-            }`}
+            class={`${!this.props.noIndent && this.props.node.depth && "ml-2"}`}
           >
             <div class="d-flex flex-wrap align-items-center text-muted small">
               <span class="mr-2">
@@ -262,7 +271,7 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
                 <>
                   <a
                     className={`unselectable pointer ${this.scoreColor}`}
-                    onClick={linkEvent(node, this.handleCommentUpvote)}
+                    onClick={this.handleCommentUpvote}
                     data-tippy-content={this.pointsTippy}
                   >
                     <span
@@ -314,12 +323,12 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
                       class="btn btn-link btn-animate text-muted"
                       onClick={linkEvent(this, this.handleMarkRead)}
                       data-tippy-content={
-                        this.commentOrMentionRead
+                        this.commentReplyOrMentionRead
                           ? i18n.t("mark_as_unread")
                           : i18n.t("mark_as_read")
                       }
                       aria-label={
-                        this.commentOrMentionRead
+                        this.commentReplyOrMentionRead
                           ? i18n.t("mark_as_unread")
                           : i18n.t("mark_as_read")
                       }
@@ -330,7 +339,7 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
                         <Icon
                           icon="check"
                           classes={`icon-inline ${
-                            this.commentOrMentionRead && "text-success"
+                            this.commentReplyOrMentionRead && "text-success"
                           }`}
                         />
                       )}
@@ -345,7 +354,7 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
                               ? "text-info"
                               : "text-muted"
                           }`}
-                          onClick={linkEvent(node, this.handleCommentUpvote)}
+                          onClick={this.handleCommentUpvote}
                           data-tippy-content={i18n.t("upvote")}
                           aria-label={i18n.t("upvote")}
                         >
@@ -364,10 +373,7 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
                                 ? "text-danger"
                                 : "text-muted"
                             }`}
-                            onClick={linkEvent(
-                              node,
-                              this.handleCommentDownvote
-                            )}
+                            onClick={this.handleCommentDownvote}
                             data-tippy-content={i18n.t("downvote")}
                             aria-label={i18n.t("downvote")}
                           >
@@ -772,6 +778,25 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
             )}
           </div>
         </div>
+        {showMoreChildren && (
+          <div
+            className={`details ml-1 comment-node py-2 ${
+              !this.props.noBorder ? "border-top border-light" : ""
+            }`}
+            style={`border-left: 2px ${moreRepliesBorderColor} solid !important`}
+          >
+            <button
+              class="btn btn-link text-muted"
+              onClick={linkEvent(this, this.handleFetchChildren)}
+            >
+              {i18n.t("x_more_replies", {
+                count: node.comment_view.counts.child_count,
+                formattedCount: numToSI(node.comment_view.counts.child_count),
+              })}{" "}
+              ➔
+            </button>
+          </div>
+        )}
         {/* end of details */}
         {this.state.showRemoveDialog && (
           <form
@@ -931,7 +956,7 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
             focus
           />
         )}
-        {!this.state.collapsed && node.children && (
+        {!this.state.collapsed && node.children.length > 0 && (
           <CommentNodes
             nodes={node.children}
             locked={this.props.locked}
@@ -939,6 +964,7 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
             admins={this.props.admins}
             maxCommentsShown={None}
             enableDownvotes={this.props.enableDownvotes}
+            viewType={this.props.viewType}
           />
         )}
         {/* A collapsed clearfix */}
@@ -947,11 +973,16 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
     );
   }
 
-  get commentOrMentionRead() {
+  get commentReplyOrMentionRead(): boolean {
     let cv = this.props.node.comment_view;
-    return this.isPersonMentionType(cv)
-      ? cv.person_mention.read
-      : cv.comment.read;
+
+    if (this.isPersonMentionType(cv)) {
+      return cv.person_mention.read;
+    } else if (this.isCommentReplyType(cv)) {
+      return cv.comment_reply.read;
+    } else {
+      return false;
+    }
   }
 
   linkBtn(small = false) {
@@ -968,7 +999,7 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
       <>
         <Link
           className={classnames}
-          to={`/post/${cv.post.id}/comment/${cv.comment.id}`}
+          to={`/comment/${cv.comment.id}`}
           title={title}
         >
           <Icon icon="link" classes="icon-inline" />
@@ -1061,7 +1092,7 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
     this.setState(this.state);
   }
 
-  handleCommentUpvote(i: CommentNodeI, event: any) {
+  handleCommentUpvote(event: any) {
     event.preventDefault();
     let myVote = this.state.my_vote.unwrapOr(0);
     let newVote = myVote == 1 ? 0 : 1;
@@ -1081,17 +1112,16 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
     this.state.my_vote = Some(newVote);
 
     let form = new CreateCommentLike({
-      comment_id: i.comment_view.comment.id,
+      comment_id: this.props.node.comment_view.comment.id,
       score: newVote,
       auth: auth().unwrap(),
     });
-
     WebSocketService.Instance.send(wsClient.likeComment(form));
     this.setState(this.state);
     setupTippy();
   }
 
-  handleCommentDownvote(i: CommentNodeI, event: any) {
+  handleCommentDownvote(event: any) {
     event.preventDefault();
     let myVote = this.state.my_vote.unwrapOr(0);
     let newVote = myVote == -1 ? 0 : -1;
@@ -1111,7 +1141,7 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
     this.state.my_vote = Some(newVote);
 
     let form = new CreateCommentLike({
-      comment_id: i.comment_view.comment.id,
+      comment_id: this.props.node.comment_view.comment.id,
       score: newVote,
       auth: auth().unwrap(),
     });
@@ -1175,11 +1205,17 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
   }
 
   isPersonMentionType(
-    item: CommentView | PersonMentionView
+    item: CommentView | PersonMentionView | CommentReplyView
   ): item is PersonMentionView {
     return (item as PersonMentionView).person_mention?.id !== undefined;
   }
 
+  isCommentReplyType(
+    item: CommentView | PersonMentionView | CommentReplyView
+  ): item is CommentReplyView {
+    return (item as CommentReplyView).comment_reply?.id !== undefined;
+  }
+
   handleMarkRead(i: CommentNode) {
     if (i.isPersonMentionType(i.props.node.comment_view)) {
       let form = new MarkPersonMentionAsRead({
@@ -1188,13 +1224,13 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
         auth: auth().unwrap(),
       });
       WebSocketService.Instance.send(wsClient.markPersonMentionAsRead(form));
-    } else {
-      let form = new MarkCommentAsRead({
-        comment_id: i.props.node.comment_view.comment.id,
-        read: !i.props.node.comment_view.comment.read,
+    } else if (i.isCommentReplyType(i.props.node.comment_view)) {
+      let form = new MarkCommentReplyAsRead({
+        comment_reply_id: i.props.node.comment_view.comment_reply.id,
+        read: !i.props.node.comment_view.comment_reply.read,
         auth: auth().unwrap(),
       });
-      WebSocketService.Instance.send(wsClient.markCommentAsRead(form));
+      WebSocketService.Instance.send(wsClient.markCommentReplyAsRead(form));
     }
 
     i.state.readLoading = true;
@@ -1419,6 +1455,24 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
     setupTippy();
   }
 
+  handleFetchChildren(i: CommentNode) {
+    let form = new GetComments({
+      post_id: Some(i.props.node.comment_view.post.id),
+      parent_id: Some(i.props.node.comment_view.comment.id),
+      max_depth: Some(commentTreeMaxDepth),
+      page: None,
+      sort: None,
+      limit: Some(999),
+      type_: Some(ListingType.All),
+      community_name: None,
+      community_id: None,
+      saved_only: Some(false),
+      auth: auth(false).ok(),
+    });
+
+    WebSocketService.Instance.send(wsClient.getComments(form));
+  }
+
   get scoreColor() {
     if (this.state.my_vote.unwrapOr(0) == 1) {
       return "text-info";
index 62167ec800ae4cfbd066e5ecc6f0f8ed6487acf3..f9484c2d4497e15a1d8d18a9beaf8ee1435702de 100644 (file)
@@ -1,7 +1,11 @@
 import { Option } from "@sniptt/monads";
 import { Component } from "inferno";
-import { CommunityModeratorView, PersonViewSafe } from "lemmy-js-client";
-import { CommentNode as CommentNodeI } from "../../interfaces";
+import {
+  CommentNode as CommentNodeI,
+  CommunityModeratorView,
+  PersonViewSafe,
+} from "lemmy-js-client";
+import { CommentViewType } from "../../interfaces";
 import { CommentNode } from "./comment-node";
 
 interface CommentNodesProps {
@@ -17,6 +21,7 @@ interface CommentNodesProps {
   showContext?: boolean;
   showCommunity?: boolean;
   enableDownvotes?: boolean;
+  viewType: CommentViewType;
 }
 
 export class CommentNodes extends Component<CommentNodesProps, any> {
@@ -45,6 +50,7 @@ export class CommentNodes extends Component<CommentNodesProps, any> {
             showContext={this.props.showContext}
             showCommunity={this.props.showCommunity}
             enableDownvotes={this.props.enableDownvotes}
+            viewType={this.props.viewType}
           />
         ))}
       </div>
index 0a6522631b2e7b66d36eda0c3c59adaa6c32b79f..a2d2b10c19008a70299f9f1f09d8ea8efe6444f0 100644 (file)
@@ -2,13 +2,14 @@ import { None } from "@sniptt/monads";
 import { Component, linkEvent } from "inferno";
 import { T } from "inferno-i18next-dess";
 import {
+  CommentNode as CommentNodeI,
   CommentReportView,
   CommentView,
   ResolveCommentReport,
   SubscribedType,
 } from "lemmy-js-client";
 import { i18n } from "../../i18next";
-import { CommentNode as CommentNodeI } from "../../interfaces";
+import { CommentViewType } from "../../interfaces";
 import { WebSocketService } from "../../services";
 import { auth, wsClient } from "../../utils";
 import { Icon } from "../common/icon";
@@ -44,18 +45,20 @@ export class CommentReport extends Component<CommentReportProps, any> {
       subscribed: SubscribedType.NotSubscribed,
       saved: false,
       creator_blocked: false,
-      recipient: None,
       my_vote: r.my_vote,
     };
 
     let node: CommentNodeI = {
       comment_view,
+      children: [],
+      depth: 0,
     };
 
     return (
       <div>
         <CommentNode
           node={node}
+          viewType={CommentViewType.Flat}
           moderators={None}
           admins={None}
           enableDownvotes={true}
diff --git a/src/shared/components/common/comment-sort-select.tsx b/src/shared/components/common/comment-sort-select.tsx
new file mode 100644 (file)
index 0000000..b87f266
--- /dev/null
@@ -0,0 +1,70 @@
+import { Component, linkEvent } from "inferno";
+import { CommentSortType } from "lemmy-js-client";
+import { i18n } from "../../i18next";
+import { randomStr, relTags, sortingHelpUrl } from "../../utils";
+import { Icon } from "./icon";
+
+interface CommentSortSelectProps {
+  sort: CommentSortType;
+  onChange?(val: CommentSortType): any;
+}
+
+interface CommentSortSelectState {
+  sort: CommentSortType;
+}
+
+export class CommentSortSelect extends Component<
+  CommentSortSelectProps,
+  CommentSortSelectState
+> {
+  private id = `sort-select-${randomStr()}`;
+  private emptyState: CommentSortSelectState = {
+    sort: this.props.sort,
+  };
+
+  constructor(props: any, context: any) {
+    super(props, context);
+    this.state = this.emptyState;
+  }
+
+  static getDerivedStateFromProps(props: any): CommentSortSelectState {
+    return {
+      sort: props.sort,
+    };
+  }
+
+  render() {
+    return (
+      <>
+        <select
+          id={this.id}
+          name={this.id}
+          value={this.state.sort}
+          onChange={linkEvent(this, this.handleSortChange)}
+          class="custom-select w-auto mr-2 mb-2"
+          aria-label={i18n.t("sort_type")}
+        >
+          <option disabled aria-hidden="true">
+            {i18n.t("sort_type")}
+          </option>
+          <option value={CommentSortType.Hot}>{i18n.t("hot")}</option>,
+          <option value={CommentSortType.Top}>{i18n.t("top")}</option>,
+          <option value={CommentSortType.New}>{i18n.t("new")}</option>
+          <option value={CommentSortType.Old}>{i18n.t("old")}</option>
+        </select>
+        <a
+          className="text-muted"
+          href={sortingHelpUrl}
+          rel={relTags}
+          title={i18n.t("sorting_help")}
+        >
+          <Icon icon="help-circle" classes="icon-inline" />
+        </a>
+      </>
+    );
+  }
+
+  handleSortChange(i: CommentSortSelect, event: any) {
+    i.props.onChange(event.target.value);
+  }
+}
index 11880647642ffd2a94e143671b755669174cafa4..3f815f5b0eb6e728ff9fea9ad01544fd59fd4505 100644 (file)
@@ -51,6 +51,7 @@ export class SortSelect extends Component<SortSelectProps, SortSelectState> {
             <option value={SortType.Active}>{i18n.t("active")}</option>,
           ]}
           <option value={SortType.New}>{i18n.t("new")}</option>
+          <option value={SortType.Old}>{i18n.t("old")}</option>
           {!this.props.hideMostComments && [
             <option value={SortType.MostComments}>
               {i18n.t("most_comments")}
index c5afe0efe33e848e6842c62de3be1d8f79cf9f85..4e8d949c4ca1b99f6119eff73d61dc1e97303445 100644 (file)
@@ -29,7 +29,11 @@ import {
 } from "lemmy-js-client";
 import { Subscription } from "rxjs";
 import { i18n } from "../../i18next";
-import { DataType, InitialFetchRequest } from "../../interfaces";
+import {
+  CommentViewType,
+  DataType,
+  InitialFetchRequest,
+} from "../../interfaces";
 import { UserService, WebSocketService } from "../../services";
 import {
   auth,
@@ -46,6 +50,7 @@ import {
   getPageFromProps,
   getSortTypeFromProps,
   notifyPost,
+  postToCommentSortType,
   relTags,
   restoreScrollPosition,
   saveCommentRes,
@@ -233,9 +238,12 @@ export class Community extends Component<any, State> {
         community_id: None,
         page,
         limit: Some(fetchLimit),
-        sort,
+        max_depth: None,
+        sort: sort.map(postToCommentSortType),
         type_: Some(ListingType.All),
         saved_only: Some(false),
+        post_id: None,
+        parent_id: None,
         auth: req.auth,
       });
       promises.push(Promise.resolve());
@@ -389,6 +397,7 @@ export class Community extends Component<any, State> {
     ) : (
       <CommentNodes
         nodes={commentsToFlatNodes(this.state.comments)}
+        viewType={CommentViewType.Flat}
         noIndent
         showContext
         enableDownvotes={enableDownvotes(this.state.siteRes)}
@@ -499,11 +508,14 @@ export class Community extends Component<any, State> {
       let form = new GetComments({
         page: Some(this.state.page),
         limit: Some(fetchLimit),
-        sort: Some(this.state.sort),
+        max_depth: None,
+        sort: Some(postToCommentSortType(this.state.sort)),
         type_: Some(ListingType.All),
         community_name: Some(this.state.communityName),
         community_id: None,
         saved_only: Some(false),
+        post_id: None,
+        parent_id: None,
         auth: auth(false).ok(),
       });
       WebSocketService.Instance.send(wsClient.getComments(form));
index 6618034216b60d410ee73c4e8ce1159e93a57208..53559df9cbe5bc93defd43edc0f7d5a93cc888f2 100644 (file)
@@ -30,7 +30,11 @@ import {
 } from "lemmy-js-client";
 import { Subscription } from "rxjs";
 import { i18n } from "../../i18next";
-import { DataType, InitialFetchRequest } from "../../interfaces";
+import {
+  CommentViewType,
+  DataType,
+  InitialFetchRequest,
+} from "../../interfaces";
 import { UserService, WebSocketService } from "../../services";
 import {
   auth,
@@ -48,6 +52,7 @@ import {
   getSortTypeFromProps,
   isBrowser,
   notifyPost,
+  postToCommentSortType,
   relTags,
   restoreScrollPosition,
   saveCommentRes,
@@ -263,9 +268,12 @@ export class Home extends Component<any, HomeState> {
         community_name: None,
         page,
         limit: Some(fetchLimit),
-        sort,
+        max_depth: None,
+        sort: sort.map(postToCommentSortType),
         type_,
         saved_only: Some(false),
+        post_id: None,
+        parent_id: None,
         auth: req.auth,
       });
       promises.push(Promise.resolve());
@@ -565,6 +573,7 @@ export class Home extends Component<any, HomeState> {
     ) : (
       <CommentNodes
         nodes={commentsToFlatNodes(this.state.comments)}
+        viewType={CommentViewType.Flat}
         moderators={None}
         admins={None}
         maxCommentsShown={None}
@@ -694,8 +703,11 @@ export class Home extends Component<any, HomeState> {
         community_name: None,
         page: Some(this.state.page),
         limit: Some(fetchLimit),
-        sort: Some(this.state.sort),
+        max_depth: None,
+        sort: Some(postToCommentSortType(this.state.sort)),
         saved_only: Some(false),
+        post_id: None,
+        parent_id: None,
         auth: auth(false).ok(),
         type_: Some(this.state.listingType),
       });
index e2faffcc9afa0d58ee6da41baf5addf064b0b2b3..2a8a7b2984640cade3ebe22dc365fface8dc794f 100644 (file)
@@ -2,8 +2,11 @@ import { None, Some } from "@sniptt/monads";
 import { Component, linkEvent } from "inferno";
 import {
   BlockPersonResponse,
+  CommentReplyResponse,
+  CommentReplyView,
   CommentReportResponse,
   CommentResponse,
+  CommentSortType,
   CommentView,
   GetPersonMentions,
   GetPersonMentionsResponse,
@@ -17,14 +20,13 @@ import {
   PrivateMessageResponse,
   PrivateMessagesResponse,
   PrivateMessageView,
-  SortType,
   UserOperation,
   wsJsonToRes,
   wsUserOp,
 } from "lemmy-js-client";
 import { Subscription } from "rxjs";
 import { i18n } from "../../i18next";
-import { InitialFetchRequest } from "../../interfaces";
+import { CommentViewType, InitialFetchRequest } from "../../interfaces";
 import { UserService, WebSocketService } from "../../services";
 import {
   auth,
@@ -44,10 +46,10 @@ import {
   wsSubscribe,
 } from "../../utils";
 import { CommentNodes } from "../comment/comment-nodes";
+import { CommentSortSelect } from "../common/comment-sort-select";
 import { HtmlTags } from "../common/html-tags";
 import { Icon, Spinner } from "../common/icon";
 import { Paginator } from "../common/paginator";
-import { SortSelect } from "../common/sort-select";
 import { PrivateMessage } from "../private_message/private-message";
 
 enum UnreadOrAll {
@@ -70,18 +72,18 @@ enum ReplyEnum {
 type ReplyType = {
   id: number;
   type_: ReplyEnum;
-  view: CommentView | PrivateMessageView | PersonMentionView;
+  view: CommentView | PrivateMessageView | PersonMentionView | CommentReplyView;
   published: string;
 };
 
 interface InboxState {
   unreadOrAll: UnreadOrAll;
   messageType: MessageType;
-  replies: CommentView[];
+  replies: CommentReplyView[];
   mentions: PersonMentionView[];
   messages: PrivateMessageView[];
   combined: ReplyType[];
-  sort: SortType;
+  sort: CommentSortType;
   page: number;
   siteRes: GetSiteResponse;
   loading: boolean;
@@ -102,7 +104,7 @@ export class Inbox extends Component<any, InboxState> {
     mentions: [],
     messages: [],
     combined: [],
-    sort: SortType.New,
+    sort: CommentSortType.New,
     page: 1,
     siteRes: this.isoData.site_res,
     loading: true,
@@ -323,19 +325,17 @@ export class Inbox extends Component<any, InboxState> {
       <div className="mb-2">
         <span class="mr-3">{this.unreadOrAllRadios()}</span>
         <span class="mr-3">{this.messageTypeRadios()}</span>
-        <SortSelect
+        <CommentSortSelect
           sort={this.state.sort}
           onChange={this.handleSortChange}
-          hideHot
-          hideMostComments
         />
       </div>
     );
   }
 
-  replyToReplyType(r: CommentView): ReplyType {
+  replyToReplyType(r: CommentReplyView): ReplyType {
     return {
-      id: r.comment.id,
+      id: r.comment_reply.id,
       type_: ReplyEnum.Reply,
       view: r,
       published: r.comment.published,
@@ -382,7 +382,10 @@ export class Inbox extends Component<any, InboxState> {
         return (
           <CommentNodes
             key={i.id}
-            nodes={[{ comment_view: i.view as CommentView }]}
+            nodes={[
+              { comment_view: i.view as CommentView, children: [], depth: 0 },
+            ]}
+            viewType={CommentViewType.Flat}
             moderators={None}
             admins={None}
             maxCommentsShown={None}
@@ -397,7 +400,14 @@ export class Inbox extends Component<any, InboxState> {
         return (
           <CommentNodes
             key={i.id}
-            nodes={[{ comment_view: i.view as PersonMentionView }]}
+            nodes={[
+              {
+                comment_view: i.view as PersonMentionView,
+                children: [],
+                depth: 0,
+              },
+            ]}
+            viewType={CommentViewType.Flat}
             moderators={None}
             admins={None}
             maxCommentsShown={None}
@@ -429,6 +439,7 @@ export class Inbox extends Component<any, InboxState> {
       <div>
         <CommentNodes
           nodes={commentsToFlatNodes(this.state.replies)}
+          viewType={CommentViewType.Flat}
           moderators={None}
           admins={None}
           maxCommentsShown={None}
@@ -448,7 +459,8 @@ export class Inbox extends Component<any, InboxState> {
         {this.state.mentions.map(umv => (
           <CommentNodes
             key={umv.person_mention.id}
-            nodes={[{ comment_view: umv }]}
+            nodes={[{ comment_view: umv, children: [], depth: 0 }]}
+            viewType={CommentViewType.Flat}
             moderators={None}
             admins={None}
             maxCommentsShown={None}
@@ -498,9 +510,11 @@ export class Inbox extends Component<any, InboxState> {
   static fetchInitialData(req: InitialFetchRequest): Promise<any>[] {
     let promises: Promise<any>[] = [];
 
+    let sort = Some(CommentSortType.New);
+
     // It can be /u/me, or /username/1
     let repliesForm = new GetReplies({
-      sort: Some(SortType.New),
+      sort,
       unread_only: Some(true),
       page: Some(1),
       limit: Some(fetchLimit),
@@ -509,7 +523,7 @@ export class Inbox extends Component<any, InboxState> {
     promises.push(req.client.getReplies(repliesForm));
 
     let personMentionsForm = new GetPersonMentions({
-      sort: Some(SortType.New),
+      sort,
       unread_only: Some(true),
       page: Some(1),
       limit: Some(fetchLimit),
@@ -565,7 +579,7 @@ export class Inbox extends Component<any, InboxState> {
     );
   }
 
-  handleSortChange(val: SortType) {
+  handleSortChange(val: CommentSortType) {
     this.state.sort = val;
     this.state.page = 1;
     this.setState(this.state);
@@ -581,6 +595,7 @@ export class Inbox extends Component<any, InboxState> {
     i.state.replies = [];
     i.state.mentions = [];
     i.state.messages = [];
+    i.state.combined = i.buildCombined();
     UserService.Instance.unreadInboxCountSub.next(0);
     window.scrollTo(0, 0);
     i.setState(i.state);
@@ -716,34 +731,51 @@ export class Inbox extends Component<any, InboxState> {
       let data = wsJsonToRes<CommentResponse>(msg, CommentResponse);
       editCommentRes(data.comment_view, this.state.replies);
       this.setState(this.state);
-    } else if (op == UserOperation.MarkCommentAsRead) {
-      let data = wsJsonToRes<CommentResponse>(msg, CommentResponse);
+    } else if (op == UserOperation.MarkCommentReplyAsRead) {
+      let data = wsJsonToRes<CommentReplyResponse>(msg, CommentReplyResponse);
+      console.log(data);
 
-      // If youre in the unread view, just remove it from the list
-      if (
-        this.state.unreadOrAll == UnreadOrAll.Unread &&
-        data.comment_view.comment.read
-      ) {
-        this.state.replies = this.state.replies.filter(
-          r => r.comment.id !== data.comment_view.comment.id
-        );
-        this.state.combined = this.state.combined.filter(
-          r => r.id !== data.comment_view.comment.id
-        );
-      } else {
-        let found = this.state.replies.find(
-          c => c.comment.id == data.comment_view.comment.id
-        );
+      let found = this.state.replies.find(
+        c => c.comment_reply.id == data.comment_reply_view.comment_reply.id
+      );
+
+      if (found) {
         let combinedView = this.state.combined.find(
-          i => i.id == data.comment_view.comment.id
-        ).view as CommentView;
-        found.comment.read = combinedView.comment.read =
-          data.comment_view.comment.read;
-      }
+          i => i.id == data.comment_reply_view.comment_reply.id
+        ).view as CommentReplyView;
+        found.comment.content = combinedView.comment.content =
+          data.comment_reply_view.comment.content;
+        found.comment.updated = combinedView.comment.updated =
+          data.comment_reply_view.comment.updated;
+        found.comment.removed = combinedView.comment.removed =
+          data.comment_reply_view.comment.removed;
+        found.comment.deleted = combinedView.comment.deleted =
+          data.comment_reply_view.comment.deleted;
+        found.counts.upvotes = combinedView.counts.upvotes =
+          data.comment_reply_view.counts.upvotes;
+        found.counts.downvotes = combinedView.counts.downvotes =
+          data.comment_reply_view.counts.downvotes;
+        found.counts.score = combinedView.counts.score =
+          data.comment_reply_view.counts.score;
 
-      this.sendUnreadCount(data.comment_view.comment.read);
+        // If youre in the unread view, just remove it from the list
+        if (
+          this.state.unreadOrAll == UnreadOrAll.Unread &&
+          data.comment_reply_view.comment_reply.read
+        ) {
+          this.state.replies = this.state.replies.filter(
+            r => r.comment_reply.id !== data.comment_reply_view.comment_reply.id
+          );
+          this.state.combined = this.state.combined.filter(
+            r => r.id !== data.comment_reply_view.comment_reply.id
+          );
+        } else {
+          found.comment_reply.read = combinedView.comment_reply.read =
+            data.comment_reply_view.comment_reply.read;
+        }
+      }
+      this.sendUnreadCount(data.comment_reply_view.comment_reply.read);
       this.setState(this.state);
-      setupTippy();
     } else if (op == UserOperation.MarkPersonMentionAsRead) {
       let data = wsJsonToRes<PersonMentionResponse>(msg, PersonMentionResponse);
 
@@ -791,71 +823,6 @@ export class Inbox extends Component<any, InboxState> {
       }
       this.sendUnreadCount(data.person_mention_view.person_mention.read);
       this.setState(this.state);
-    } else if (op == UserOperation.CreateComment) {
-      let data = wsJsonToRes<CommentResponse>(msg, CommentResponse);
-
-      UserService.Instance.myUserInfo.match({
-        some: mui => {
-          if (data.recipient_ids.includes(mui.local_user_view.local_user.id)) {
-            this.state.replies.unshift(data.comment_view);
-            this.state.combined.unshift(
-              this.replyToReplyType(data.comment_view)
-            );
-            this.setState(this.state);
-          } else if (
-            data.comment_view.creator.id == mui.local_user_view.person.id
-          ) {
-            // If youre in the unread view, just remove it from the list
-            if (this.state.unreadOrAll == UnreadOrAll.Unread) {
-              this.state.replies = this.state.replies.filter(
-                r =>
-                  r.comment.id !==
-                  data.comment_view.comment.parent_id.unwrapOr(0)
-              );
-              this.state.mentions = this.state.mentions.filter(
-                m =>
-                  m.comment.id !==
-                  data.comment_view.comment.parent_id.unwrapOr(0)
-              );
-              this.state.combined = this.state.combined.filter(r => {
-                if (this.isMention(r.view))
-                  return (
-                    r.view.comment.id !==
-                    data.comment_view.comment.parent_id.unwrapOr(0)
-                  );
-                else
-                  return (
-                    r.id !== data.comment_view.comment.parent_id.unwrapOr(0)
-                  );
-              });
-            } else {
-              let mention_found = this.state.mentions.find(
-                i =>
-                  i.comment.id ==
-                  data.comment_view.comment.parent_id.unwrapOr(0)
-              );
-              if (mention_found) {
-                mention_found.person_mention.read = true;
-              }
-              let reply_found = this.state.replies.find(
-                i =>
-                  i.comment.id ==
-                  data.comment_view.comment.parent_id.unwrapOr(0)
-              );
-              if (reply_found) {
-                reply_found.comment.read = true;
-              }
-              this.state.combined = this.buildCombined();
-            }
-            this.sendUnreadCount(true);
-            this.setState(this.state);
-            setupTippy();
-            // TODO this seems wrong, you should be using form_id
-            toast(i18n.t("reply_sent"));
-          }
-        },
-        none: void 0,
-      });
     } else if (op == UserOperation.CreatePrivateMessage) {
       let data = wsJsonToRes<PrivateMessageResponse>(
         msg,
@@ -904,4 +871,8 @@ export class Inbox extends Component<any, InboxState> {
   isMention(view: any): view is PersonMentionView {
     return (view as PersonMentionView).person_mention !== undefined;
   }
+
+  isReply(view: any): view is CommentReplyView {
+    return (view as CommentReplyView).comment_reply !== undefined;
+  }
 }
index 0dabf2aff9e4c6e5bda5220c281fec4217e01118..6ce7a8b8d0b02c3070866adfa9983b15ed58de12 100644 (file)
@@ -7,7 +7,7 @@ import {
   PostView,
   SortType,
 } from "lemmy-js-client";
-import { PersonDetailsView } from "../../interfaces";
+import { CommentViewType, PersonDetailsView } from "../../interfaces";
 import { commentsToFlatNodes, setupTippy } from "../../utils";
 import { CommentNodes } from "../comment/comment-nodes";
 import { Paginator } from "../common/paginator";
@@ -89,7 +89,8 @@ export class PersonDetails extends Component<PersonDetailsProps, any> {
         return (
           <CommentNodes
             key={i.id}
-            nodes={[{ comment_view: c }]}
+            nodes={[{ comment_view: c, children: [], depth: 0 }]}
+            viewType={CommentViewType.Flat}
             admins={Some(this.props.admins)}
             moderators={None}
             maxCommentsShown={None}
@@ -159,6 +160,7 @@ export class PersonDetails extends Component<PersonDetailsProps, any> {
       <div>
         <CommentNodes
           nodes={commentsToFlatNodes(this.props.personRes.comments)}
+          viewType={CommentViewType.Flat}
           admins={Some(this.props.admins)}
           moderators={None}
           maxCommentsShown={None}
index cbe84d91fcbb1ce7261f12e81cfaf4677e27a414..672d3d6925d28a13d245c29eeaaaf158dcdd1994 100644 (file)
@@ -408,7 +408,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
           className={`btn-animate btn btn-link p-0 ${
             this.state.my_vote.unwrapOr(0) == 1 ? "text-info" : "text-muted"
           }`}
-          onClick={linkEvent(this, this.handlePostLike)}
+          onClick={this.handlePostLike}
           data-tippy-content={i18n.t("upvote")}
           aria-label={i18n.t("upvote")}
         >
@@ -431,7 +431,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
                 ? "text-danger"
                 : "text-muted"
             }`}
-            onClick={linkEvent(this, this.handlePostDisLike)}
+            onClick={this.handlePostDisLike}
             data-tippy-content={i18n.t("downvote")}
             aria-label={i18n.t("downvote")}
           >
@@ -647,7 +647,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
               this.state.my_vote.unwrapOr(0) == 1 ? "text-info" : "text-muted"
             }`}
             {...tippy}
-            onClick={linkEvent(this, this.handlePostLike)}
+            onClick={this.handlePostLike}
             aria-label={i18n.t("upvote")}
           >
             <Icon icon="arrow-up1" classes="icon-inline small" />
@@ -662,7 +662,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
                   ? "text-danger"
                   : "text-muted"
               }`}
-              onClick={linkEvent(this, this.handlePostDisLike)}
+              onClick={this.handlePostDisLike}
               {...tippy}
               aria-label={i18n.t("downvote")}
             >
@@ -1250,7 +1250,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
     });
   }
 
-  handlePostLike(i: PostListing, event: any) {
+  handlePostLike(event: any) {
     event.preventDefault();
     if (UserService.Instance.myUserInfo.isNone()) {
       this.context.router.history.push(`/login`);
@@ -1260,31 +1260,31 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
     let newVote = myVote == 1 ? 0 : 1;
 
     if (myVote == 1) {
-      i.state.score--;
-      i.state.upvotes--;
+      this.state.score--;
+      this.state.upvotes--;
     } else if (myVote == -1) {
-      i.state.downvotes--;
-      i.state.upvotes++;
-      i.state.score += 2;
+      this.state.downvotes--;
+      this.state.upvotes++;
+      this.state.score += 2;
     } else {
-      i.state.upvotes++;
-      i.state.score++;
+      this.state.upvotes++;
+      this.state.score++;
     }
 
-    i.state.my_vote = Some(newVote);
+    this.state.my_vote = Some(newVote);
 
     let form = new CreatePostLike({
-      post_id: i.props.post_view.post.id,
+      post_id: this.props.post_view.post.id,
       score: newVote,
       auth: auth().unwrap(),
     });
 
     WebSocketService.Instance.send(wsClient.likePost(form));
-    i.setState(i.state);
+    this.setState(this.state);
     setupTippy();
   }
 
-  handlePostDisLike(i: PostListing, event: any) {
+  handlePostDisLike(event: any) {
     event.preventDefault();
     if (UserService.Instance.myUserInfo.isNone()) {
       this.context.router.history.push(`/login`);
@@ -1294,27 +1294,27 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
     let newVote = myVote == -1 ? 0 : -1;
 
     if (myVote == 1) {
-      i.state.score -= 2;
-      i.state.upvotes--;
-      i.state.downvotes++;
+      this.state.score -= 2;
+      this.state.upvotes--;
+      this.state.downvotes++;
     } else if (myVote == -1) {
-      i.state.downvotes--;
-      i.state.score++;
+      this.state.downvotes--;
+      this.state.score++;
     } else {
-      i.state.downvotes++;
-      i.state.score--;
+      this.state.downvotes++;
+      this.state.score--;
     }
 
-    i.state.my_vote = Some(newVote);
+    this.state.my_vote = Some(newVote);
 
     let form = new CreatePostLike({
-      post_id: i.props.post_view.post.id,
+      post_id: this.props.post_view.post.id,
       score: newVote,
       auth: auth().unwrap(),
     });
 
     WebSocketService.Instance.send(wsClient.likePost(form));
-    i.setState(i.state);
+    this.setState(this.state);
     setupTippy();
   }
 
index 9702c3d18460d445c335879856f5c062b998dc21..e8b861921babdf5c90e67f0e1d2d231713f3588e 100644 (file)
@@ -7,15 +7,18 @@ import {
   BanFromCommunityResponse,
   BanPersonResponse,
   BlockPersonResponse,
+  CommentNode as CommentNodeI,
   CommentReportResponse,
   CommentResponse,
+  CommentSortType,
   CommunityResponse,
+  GetComments,
+  GetCommentsResponse,
   GetCommunityResponse,
   GetPost,
   GetPostResponse,
   GetSiteResponse,
   ListingType,
-  MarkCommentAsRead,
   PostReportResponse,
   PostResponse,
   PostView,
@@ -30,17 +33,13 @@ import {
 } from "lemmy-js-client";
 import { Subscription } from "rxjs";
 import { i18n } from "../../i18next";
-import {
-  CommentNode as CommentNodeI,
-  CommentSortType,
-  CommentViewType,
-  InitialFetchRequest,
-} from "../../interfaces";
+import { CommentViewType, InitialFetchRequest } from "../../interfaces";
 import { UserService, WebSocketService } from "../../services";
 import {
   auth,
   buildCommentsTree,
   commentsToFlatNodes,
+  commentTreeMaxDepth,
   createCommentLikeRes,
   createPostLikeRes,
   debounce,
@@ -48,6 +47,8 @@ import {
   enableDownvotes,
   enableNsfw,
   getCommentIdFromProps,
+  getCommentParentId,
+  getDepthFromComment,
   getIdFromProps,
   insertCommentIntoTree,
   isBrowser,
@@ -73,10 +74,11 @@ import { PostListing } from "./post-listing";
 const commentsShownInterval = 15;
 
 interface PostState {
+  postId: Option<number>;
+  commentId: Option<number>;
   postRes: Option<GetPostResponse>;
-  postId: number;
+  commentsRes: Option<GetCommentsResponse>;
   commentTree: CommentNodeI[];
-  commentId?: number;
   commentSort: CommentSortType;
   commentViewType: CommentViewType;
   scrolled?: boolean;
@@ -90,14 +92,19 @@ interface PostState {
 
 export class Post extends Component<any, PostState> {
   private subscription: Subscription;
-  private isoData = setIsoData(this.context, GetPostResponse);
+  private isoData = setIsoData(
+    this.context,
+    GetPostResponse,
+    GetCommentsResponse
+  );
   private commentScrollDebounced: () => void;
   private emptyState: PostState = {
     postRes: None,
+    commentsRes: None,
     postId: getIdFromProps(this.props),
-    commentTree: [],
     commentId: getCommentIdFromProps(this.props),
-    commentSort: CommentSortType.Hot,
+    commentTree: [],
+    commentSort: CommentSortType[CommentSortType.Hot],
     commentViewType: CommentViewType.Tree,
     scrolled: false,
     loading: true,
@@ -120,10 +127,19 @@ export class Post extends Component<any, PostState> {
     // Only fetch the data if coming from another route
     if (this.isoData.path == this.context.router.route.match.url) {
       this.state.postRes = Some(this.isoData.routeData[0] as GetPostResponse);
-      this.state.commentTree = buildCommentsTree(
-        this.state.postRes.unwrap().comments,
-        this.state.commentSort
+      this.state.commentsRes = Some(
+        this.isoData.routeData[1] as GetCommentsResponse
       );
+
+      this.state.commentsRes.match({
+        some: res => {
+          this.state.commentTree = buildCommentsTree(
+            res.comments,
+            this.state.commentId.isSome()
+          );
+        },
+        none: void 0,
+      });
       this.state.loading = false;
 
       if (isBrowser()) {
@@ -133,14 +149,14 @@ export class Post extends Component<any, PostState> {
               this.state.postRes.unwrap().community_view.community.id,
           })
         );
-        WebSocketService.Instance.send(
-          wsClient.postJoin({ post_id: this.state.postId })
-        );
+
+        this.state.postId.match({
+          some: post_id =>
+            WebSocketService.Instance.send(wsClient.postJoin({ post_id })),
+          none: void 0,
+        });
 
         this.fetchCrossPosts();
-        if (this.state.commentId) {
-          this.scrollCommentIntoView();
-        }
 
         if (this.checkScrollIntoCommentsParam) {
           this.scrollIntoCommentSection();
@@ -152,11 +168,28 @@ export class Post extends Component<any, PostState> {
   }
 
   fetchPost() {
-    let form = new GetPost({
+    this.setState({ commentsRes: None });
+    let postForm = new GetPost({
       id: this.state.postId,
+      comment_id: this.state.commentId,
+      auth: auth(false).ok(),
+    });
+    WebSocketService.Instance.send(wsClient.getPost(postForm));
+
+    let commentsForm = new GetComments({
+      post_id: this.state.postId,
+      parent_id: this.state.commentId,
+      max_depth: Some(commentTreeMaxDepth),
+      page: None,
+      limit: None,
+      sort: Some(this.state.commentSort),
+      type_: Some(ListingType.All),
+      community_name: None,
+      community_id: None,
+      saved_only: Some(false),
       auth: auth(false).ok(),
     });
-    WebSocketService.Instance.send(wsClient.getPost(form));
+    WebSocketService.Instance.send(wsClient.getComments(commentsForm));
   }
 
   fetchCrossPosts() {
@@ -184,15 +217,44 @@ export class Post extends Component<any, PostState> {
 
   static fetchInitialData(req: InitialFetchRequest): Promise<any>[] {
     let pathSplit = req.path.split("/");
+    let promises: Promise<any>[] = [];
 
+    let pathType = pathSplit[1];
     let id = Number(pathSplit[2]);
 
     let postForm = new GetPost({
-      id,
+      id: None,
+      comment_id: None,
+      auth: req.auth,
+    });
+
+    let commentsForm = new GetComments({
+      post_id: None,
+      parent_id: None,
+      max_depth: Some(commentTreeMaxDepth),
+      page: None,
+      limit: None,
+      sort: Some(CommentSortType.Hot),
+      type_: Some(ListingType.All),
+      community_name: None,
+      community_id: None,
+      saved_only: Some(false),
       auth: req.auth,
     });
 
-    return [req.client.getPost(postForm)];
+    // Set the correct id based on the path type
+    if (pathType == "post") {
+      postForm.id = Some(id);
+      commentsForm.post_id = Some(id);
+    } else {
+      postForm.comment_id = Some(id);
+      commentsForm.parent_id = Some(id);
+    }
+
+    promises.push(req.client.getPost(postForm));
+    promises.push(req.client.getComments(commentsForm));
+
+    return promises;
   }
 
   componentWillUnmount() {
@@ -222,18 +284,6 @@ export class Post extends Component<any, PostState> {
     }
   }
 
-  scrollCommentIntoView() {
-    let commentElement = document.getElementById(
-      `comment-${this.state.commentId}`
-    );
-    if (commentElement) {
-      commentElement.scrollIntoView();
-      commentElement.classList.add("mark");
-      this.state.scrolled = true;
-      this.markScrolledAsRead(this.state.commentId);
-    }
-  }
-
   get checkScrollIntoCommentsParam() {
     return Boolean(
       new URLSearchParams(this.props.location.search).get("scrollToComments")
@@ -244,39 +294,6 @@ export class Post extends Component<any, PostState> {
     this.state.commentSectionRef.current?.scrollIntoView();
   }
 
-  // TODO this needs some re-work
-  markScrolledAsRead(commentId: number) {
-    this.state.postRes.match({
-      some: res => {
-        let found = res.comments.find(c => c.comment.id == commentId);
-        let parent = res.comments.find(
-          c => found.comment.parent_id.unwrapOr(0) == c.comment.id
-        );
-        let parent_person_id = parent
-          ? parent.creator.id
-          : res.post_view.creator.id;
-
-        UserService.Instance.myUserInfo.match({
-          some: mui => {
-            if (mui.local_user_view.person.id == parent_person_id) {
-              let form = new MarkCommentAsRead({
-                comment_id: found.comment.id,
-                read: true,
-                auth: auth().unwrap(),
-              });
-              WebSocketService.Instance.send(wsClient.markCommentAsRead(form));
-              UserService.Instance.unreadInboxCountSub.next(
-                UserService.Instance.unreadInboxCountSub.value - 1
-              );
-            }
-          },
-          none: void 0,
-        });
-      },
-      none: void 0,
-    });
-  }
-
   isBottom(el: Element): boolean {
     return el?.getBoundingClientRect().bottom <= window.innerHeight;
   }
@@ -351,7 +368,7 @@ export class Post extends Component<any, PostState> {
                   />
                   <div ref={this.state.commentSectionRef} className="mb-2" />
                   <CommentForm
-                    node={Right(this.state.postId)}
+                    node={Right(res.post_view.post.id)}
                     disabled={res.post_view.post.locked}
                   />
                   <div class="d-block d-md-none">
@@ -371,10 +388,10 @@ export class Post extends Component<any, PostState> {
                     </button>
                     {this.state.showSidebarMobile && this.sidebar()}
                   </div>
-                  {res.comments.length > 0 && this.sortRadios()}
+                  {this.sortRadios()}
                   {this.state.commentViewType == CommentViewType.Tree &&
                     this.commentsTree()}
-                  {this.state.commentViewType == CommentViewType.Chat &&
+                  {this.state.commentViewType == CommentViewType.Flat &&
                     this.commentsFlat()}
                 </div>
                 <div class="d-none d-md-block col-md-4">{this.sidebar()}</div>
@@ -393,7 +410,8 @@ export class Post extends Component<any, PostState> {
         <div class="btn-group btn-group-toggle flex-wrap mr-3 mb-2">
           <label
             className={`btn btn-outline-secondary pointer ${
-              this.state.commentSort === CommentSortType.Hot && "active"
+              CommentSortType[this.state.commentSort] === CommentSortType.Hot &&
+              "active"
             }`}
           >
             {i18n.t("hot")}
@@ -406,7 +424,8 @@ export class Post extends Component<any, PostState> {
           </label>
           <label
             className={`btn btn-outline-secondary pointer ${
-              this.state.commentSort === CommentSortType.Top && "active"
+              CommentSortType[this.state.commentSort] === CommentSortType.Top &&
+              "active"
             }`}
           >
             {i18n.t("top")}
@@ -419,7 +438,8 @@ export class Post extends Component<any, PostState> {
           </label>
           <label
             className={`btn btn-outline-secondary pointer ${
-              this.state.commentSort === CommentSortType.New && "active"
+              CommentSortType[this.state.commentSort] === CommentSortType.New &&
+              "active"
             }`}
           >
             {i18n.t("new")}
@@ -432,7 +452,8 @@ export class Post extends Component<any, PostState> {
           </label>
           <label
             className={`btn btn-outline-secondary pointer ${
-              this.state.commentSort === CommentSortType.Old && "active"
+              CommentSortType[this.state.commentSort] === CommentSortType.Old &&
+              "active"
             }`}
           >
             {i18n.t("old")}
@@ -447,14 +468,14 @@ export class Post extends Component<any, PostState> {
         <div class="btn-group btn-group-toggle flex-wrap mb-2">
           <label
             className={`btn btn-outline-secondary pointer ${
-              this.state.commentViewType === CommentViewType.Chat && "active"
+              this.state.commentViewType === CommentViewType.Flat && "active"
             }`}
           >
             {i18n.t("chat")}
             <input
               type="radio"
-              value={CommentViewType.Chat}
-              checked={this.state.commentViewType === CommentViewType.Chat}
+              value={CommentViewType.Flat}
+              checked={this.state.commentViewType === CommentViewType.Flat}
               onChange={linkEvent(this, this.handleCommentViewTypeChange)}
             />
           </label>
@@ -465,21 +486,26 @@ export class Post extends Component<any, PostState> {
 
   commentsFlat() {
     // These are already sorted by new
-    return this.state.postRes.match({
-      some: res => (
-        <div>
-          <CommentNodes
-            nodes={commentsToFlatNodes(res.comments)}
-            maxCommentsShown={Some(this.state.maxCommentsShown)}
-            noIndent
-            locked={res.post_view.post.locked}
-            moderators={Some(res.moderators)}
-            admins={Some(this.state.siteRes.admins)}
-            enableDownvotes={enableDownvotes(this.state.siteRes)}
-            showContext
-          />
-        </div>
-      ),
+    return this.state.commentsRes.match({
+      some: commentsRes =>
+        this.state.postRes.match({
+          some: postRes => (
+            <div>
+              <CommentNodes
+                nodes={commentsToFlatNodes(commentsRes.comments)}
+                viewType={this.state.commentViewType}
+                maxCommentsShown={Some(this.state.maxCommentsShown)}
+                noIndent
+                locked={postRes.post_view.post.locked}
+                moderators={Some(postRes.moderators)}
+                admins={Some(this.state.siteRes.admins)}
+                enableDownvotes={enableDownvotes(this.state.siteRes)}
+                showContext
+              />
+            </div>
+          ),
+          none: <></>,
+        }),
       none: <></>,
     });
   }
@@ -503,21 +529,18 @@ export class Post extends Component<any, PostState> {
   }
 
   handleCommentSortChange(i: Post, event: any) {
-    i.state.commentSort = Number(event.target.value);
+    i.state.commentSort = CommentSortType[event.target.value];
     i.state.commentViewType = CommentViewType.Tree;
-    i.state.commentTree = buildCommentsTree(
-      i.state.postRes.map(r => r.comments).unwrapOr([]),
-      i.state.commentSort
-    );
     i.setState(i.state);
+    i.fetchPost();
   }
 
   handleCommentViewTypeChange(i: Post, event: any) {
     i.state.commentViewType = Number(event.target.value);
     i.state.commentSort = CommentSortType.New;
     i.state.commentTree = buildCommentsTree(
-      i.state.postRes.map(r => r.comments).unwrapOr([]),
-      i.state.commentSort
+      i.state.commentsRes.map(r => r.comments).unwrapOr([]),
+      i.state.commentId.isSome()
     );
     i.setState(i.state);
   }
@@ -527,12 +550,52 @@ export class Post extends Component<any, PostState> {
     i.setState(i.state);
   }
 
+  handleViewPost(i: Post) {
+    i.state.postRes.match({
+      some: res =>
+        i.context.router.history.push(`/post/${res.post_view.post.id}`),
+      none: void 0,
+    });
+  }
+
+  handleViewContext(i: Post) {
+    i.state.commentsRes.match({
+      some: res =>
+        i.context.router.history.push(
+          `/comment/${getCommentParentId(res.comments[0].comment).unwrap()}`
+        ),
+      none: void 0,
+    });
+  }
+
   commentsTree() {
+    let showContextButton =
+      getDepthFromComment(this.state.commentTree[0].comment_view.comment) > 0;
+
     return this.state.postRes.match({
       some: res => (
         <div>
+          {this.state.commentId.isSome() && (
+            <>
+              <button
+                class="pl-0 d-block btn btn-link text-muted"
+                onClick={linkEvent(this, this.handleViewPost)}
+              >
+                {i18n.t("view_all_comments")} ➔
+              </button>
+              {showContextButton && (
+                <button
+                  class="pl-0 d-block btn btn-link text-muted"
+                  onClick={linkEvent(this, this.handleViewContext)}
+                >
+                  {i18n.t("show_context")} ➔
+                </button>
+              )}
+            </>
+          )}
           <CommentNodes
             nodes={this.state.commentTree}
+            viewType={this.state.commentViewType}
             maxCommentsShown={Some(this.state.maxCommentsShown)}
             locked={res.post_view.post.locked}
             moderators={Some(res.moderators)}
@@ -552,27 +615,29 @@ export class Post extends Component<any, PostState> {
       toast(i18n.t(msg.error), "danger");
       return;
     } else if (msg.reconnect) {
-      let postId = Number(this.props.match.params.id);
-      WebSocketService.Instance.send(wsClient.postJoin({ post_id: postId }));
-      WebSocketService.Instance.send(
-        wsClient.getPost({
-          id: postId,
-          auth: auth(false).ok(),
-        })
-      );
+      this.state.postRes.match({
+        some: res => {
+          let postId = res.post_view.post.id;
+          WebSocketService.Instance.send(
+            wsClient.postJoin({ post_id: postId })
+          );
+          WebSocketService.Instance.send(
+            wsClient.getPost({
+              id: Some(postId),
+              comment_id: None,
+              auth: auth(false).ok(),
+            })
+          );
+        },
+        none: void 0,
+      });
     } else if (op == UserOperation.GetPost) {
       let data = wsJsonToRes<GetPostResponse>(msg, GetPostResponse);
       this.state.postRes = Some(data);
 
-      this.state.commentTree = buildCommentsTree(
-        this.state.postRes.map(r => r.comments).unwrapOr([]),
-        this.state.commentSort
-      );
-      this.state.loading = false;
-
       // join the rooms
       WebSocketService.Instance.send(
-        wsClient.postJoin({ post_id: this.state.postId })
+        wsClient.postJoin({ post_id: data.post_view.post.id })
       );
       WebSocketService.Instance.send(
         wsClient.communityJoin({
@@ -581,18 +646,36 @@ export class Post extends Component<any, PostState> {
       );
 
       // Get cross-posts
+      // TODO move this into initial fetch and refetch
       this.fetchCrossPosts();
       this.setState(this.state);
       setupTippy();
-      if (!this.state.commentId) restoreScrollPosition(this.context);
+      if (this.state.commentId.isNone()) restoreScrollPosition(this.context);
 
       if (this.checkScrollIntoCommentsParam) {
         this.scrollIntoCommentSection();
       }
-
-      if (this.state.commentId && !this.state.scrolled) {
-        this.scrollCommentIntoView();
-      }
+    } else if (op == UserOperation.GetComments) {
+      let data = wsJsonToRes<GetCommentsResponse>(msg, GetCommentsResponse);
+      // You might need to append here, since this could be building more comments from a tree fetch
+      this.state.commentsRes.match({
+        some: res => {
+          // Remove the first comment, since it is the parent
+          let newComments = data.comments;
+          newComments.shift();
+          res.comments.push(...newComments);
+        },
+        none: () => {
+          this.state.commentsRes = Some(data);
+        },
+      });
+      // this.state.commentsRes = Some(data);
+      this.state.commentTree = buildCommentsTree(
+        this.state.commentsRes.map(r => r.comments).unwrapOr([]),
+        this.state.commentId.isSome()
+      );
+      this.state.loading = false;
+      this.setState(this.state);
     } else if (op == UserOperation.CreateComment) {
       let data = wsJsonToRes<CommentResponse>(msg, CommentResponse);
 
@@ -606,11 +689,19 @@ export class Post extends Component<any, PostState> {
       // Necessary since it might be a user reply, which has the recipients, to avoid double
       if (data.recipient_ids.length == 0 && !creatorBlocked) {
         this.state.postRes.match({
-          some: res => {
-            res.comments.unshift(data.comment_view);
-            insertCommentIntoTree(this.state.commentTree, data.comment_view);
-            res.post_view.counts.comments++;
-          },
+          some: postRes =>
+            this.state.commentsRes.match({
+              some: commentsRes => {
+                commentsRes.comments.unshift(data.comment_view);
+                insertCommentIntoTree(
+                  this.state.commentTree,
+                  data.comment_view,
+                  this.state.commentId.isSome()
+                );
+                postRes.post_view.counts.comments++;
+              },
+              none: void 0,
+            }),
           none: void 0,
         });
         this.setState(this.state);
@@ -624,14 +715,14 @@ export class Post extends Component<any, PostState> {
       let data = wsJsonToRes<CommentResponse>(msg, CommentResponse);
       editCommentRes(
         data.comment_view,
-        this.state.postRes.map(r => r.comments).unwrapOr([])
+        this.state.commentsRes.map(r => r.comments).unwrapOr([])
       );
       this.setState(this.state);
     } else if (op == UserOperation.SaveComment) {
       let data = wsJsonToRes<CommentResponse>(msg, CommentResponse);
       saveCommentRes(
         data.comment_view,
-        this.state.postRes.map(r => r.comments).unwrapOr([])
+        this.state.commentsRes.map(r => r.comments).unwrapOr([])
       );
       this.setState(this.state);
       setupTippy();
@@ -639,7 +730,7 @@ export class Post extends Component<any, PostState> {
       let data = wsJsonToRes<CommentResponse>(msg, CommentResponse);
       createCommentLikeRes(
         data.comment_view,
-        this.state.postRes.map(r => r.comments).unwrapOr([])
+        this.state.commentsRes.map(r => r.comments).unwrapOr([])
       );
       this.setState(this.state);
     } else if (op == UserOperation.CreatePostLike) {
@@ -685,15 +776,19 @@ export class Post extends Component<any, PostState> {
         BanFromCommunityResponse
       );
       this.state.postRes.match({
-        some: res => {
-          res.comments
-            .filter(c => c.creator.id == data.person_view.person.id)
-            .forEach(c => (c.creator_banned_from_community = data.banned));
-          if (res.post_view.creator.id == data.person_view.person.id) {
-            res.post_view.creator_banned_from_community = data.banned;
-          }
-          this.setState(this.state);
-        },
+        some: postRes =>
+          this.state.commentsRes.match({
+            some: commentsRes => {
+              commentsRes.comments
+                .filter(c => c.creator.id == data.person_view.person.id)
+                .forEach(c => (c.creator_banned_from_community = data.banned));
+              if (postRes.post_view.creator.id == data.person_view.person.id) {
+                postRes.post_view.creator_banned_from_community = data.banned;
+              }
+              this.setState(this.state);
+            },
+            none: void 0,
+          }),
         none: void 0,
       });
     } else if (op == UserOperation.AddModToCommunity) {
@@ -711,15 +806,19 @@ export class Post extends Component<any, PostState> {
     } else if (op == UserOperation.BanPerson) {
       let data = wsJsonToRes<BanPersonResponse>(msg, BanPersonResponse);
       this.state.postRes.match({
-        some: res => {
-          res.comments
-            .filter(c => c.creator.id == data.person_view.person.id)
-            .forEach(c => (c.creator.banned = data.banned));
-          if (res.post_view.creator.id == data.person_view.person.id) {
-            res.post_view.creator.banned = data.banned;
-          }
-          this.setState(this.state);
-        },
+        some: postRes =>
+          this.state.commentsRes.match({
+            some: commentsRes => {
+              commentsRes.comments
+                .filter(c => c.creator.id == data.person_view.person.id)
+                .forEach(c => (c.creator.banned = data.banned));
+              if (postRes.post_view.creator.id == data.person_view.person.id) {
+                postRes.post_view.creator.banned = data.banned;
+              }
+              this.setState(this.state);
+            },
+            none: void 0,
+          }),
         none: void 0,
       });
     } else if (op == UserOperation.AddAdmin) {
index 1306caec10f09842acd91b68728030ac13a82e01..c54b2d226513b97a0db85c8ed5ef8c691d0efa9a 100644 (file)
@@ -26,8 +26,8 @@ import {
   wsUserOp,
 } from "lemmy-js-client";
 import { Subscription } from "rxjs";
-import { InitialFetchRequest } from "shared/interfaces";
 import { i18n } from "../i18next";
+import { CommentViewType, InitialFetchRequest } from "../interfaces";
 import { WebSocketService } from "../services";
 import {
   auth,
@@ -594,7 +594,14 @@ export class Search extends Component<any, SearchState> {
               {i.type_ == "comments" && (
                 <CommentNodes
                   key={(i.data as CommentView).comment.id}
-                  nodes={[{ comment_view: i.data as CommentView }]}
+                  nodes={[
+                    {
+                      comment_view: i.data as CommentView,
+                      children: [],
+                      depth: 0,
+                    },
+                  ]}
+                  viewType={CommentViewType.Flat}
                   moderators={None}
                   admins={None}
                   maxCommentsShown={None}
@@ -631,6 +638,7 @@ export class Search extends Component<any, SearchState> {
     return (
       <CommentNodes
         nodes={commentsToFlatNodes(comments)}
+        viewType={CommentViewType.Flat}
         locked
         noIndent
         moderators={None}
index 0a157b57596a53e6821b365823af285458ae7cd4..c25f5b16d155f09de702cdcb94707b2b442a3d2d 100644 (file)
@@ -1,10 +1,5 @@
 import { Either, Option } from "@sniptt/monads";
-import {
-  CommentView,
-  GetSiteResponse,
-  LemmyHttp,
-  PersonMentionView,
-} from "lemmy-js-client";
+import { GetSiteResponse, LemmyHttp } from "lemmy-js-client";
 
 /**
  * This contains serialized data, it needs to be deserialized before use.
@@ -32,12 +27,6 @@ export interface InitialFetchRequest {
   path: string;
 }
 
-export interface CommentNode {
-  comment_view: CommentView | PersonMentionView;
-  children?: CommentNode[];
-  depth?: number;
-}
-
 export interface PostFormParams {
   name: Option<string>;
   url: Option<string>;
@@ -45,16 +34,9 @@ export interface PostFormParams {
   nameOrId: Option<Either<string, number>>;
 }
 
-export enum CommentSortType {
-  Hot,
-  Top,
-  New,
-  Old,
-}
-
 export enum CommentViewType {
   Tree,
-  Chat,
+  Flat,
 }
 
 export enum DataType {
index 8035471b9028bb88b55711b35d07daf71398d0fc..336503f220e4fc450340160e8bdc2a4d87970d84 100644 (file)
@@ -72,12 +72,12 @@ export const routes: IRoutePropsWithFetch[] = [
     fetchInitialData: req => Communities.fetchInitialData(req),
   },
   {
-    path: `/post/:id/comment/:comment_id`,
+    path: `/post/:post_id`,
     component: Post,
     fetchInitialData: req => Post.fetchInitialData(req),
   },
   {
-    path: `/post/:id`,
+    path: `/comment/:comment_id`,
     component: Post,
     fetchInitialData: req => Post.fetchInitialData(req),
   },
index a91162177ef8d147fbdc27e397651cbeac6b783c..b19ee22313c314b2f00fa0d1c9d3f4c08fb66fff 100644 (file)
@@ -4,7 +4,10 @@ import emojiShortName from "emoji-short-name";
 import {
   BlockCommunityResponse,
   BlockPersonResponse,
+  Comment as CommentI,
+  CommentNode as CommentNodeI,
   CommentReportView,
+  CommentSortType,
   CommentView,
   CommunityBlockView,
   CommunityModeratorView,
@@ -39,12 +42,7 @@ import tippy from "tippy.js";
 import Toastify from "toastify-js";
 import { httpBase } from "./env";
 import { i18n, languages } from "./i18next";
-import {
-  CommentNode as CommentNodeI,
-  CommentSortType,
-  DataType,
-  IsoData,
-} from "./interfaces";
+import { DataType, IsoData } from "./interfaces";
 import { UserService, WebSocketService } from "./services";
 
 var Tribute: any;
@@ -74,6 +72,7 @@ export const postRefetchSeconds: number = 60 * 1000;
 export const fetchLimit = 20;
 export const trendingFetchLimit = 6;
 export const mentionDropdownFetchLimit = 10;
+export const commentTreeMaxDepth = 8;
 
 export const relTags = "noopener nofollow";
 
@@ -611,7 +610,7 @@ export function notifyComment(comment_view: CommentView, router: any) {
   let info: NotifyInfo = {
     name: comment_view.creator.name,
     icon: comment_view.creator.avatar,
-    link: `/post/${comment_view.post.id}/comment/${comment_view.comment.id}`,
+    link: `/comment/${comment_view.comment.id}`,
     body: comment_view.comment.content,
   };
   notify(info, router);
@@ -813,12 +812,14 @@ export function getRecipientIdFromProps(props: any): number {
     : 1;
 }
 
-export function getIdFromProps(props: any): number {
-  return Number(props.match.params.id);
+export function getIdFromProps(props: any): Option<number> {
+  let id: string = props.match.params.post_id;
+  return id ? Some(Number(id)) : None;
 }
 
-export function getCommentIdFromProps(props: any): number {
-  return Number(props.match.params.comment_id);
+export function getCommentIdFromProps(props: any): Option<number> {
+  let id: string = props.match.params.comment_id;
+  return id ? Some(Number(id)) : None;
 }
 
 export function getUsernameFromProps(props: any): string {
@@ -985,61 +986,12 @@ export function updateRegistrationApplicationRes(
 export function commentsToFlatNodes(comments: CommentView[]): CommentNodeI[] {
   let nodes: CommentNodeI[] = [];
   for (let comment of comments) {
-    nodes.push({ comment_view: comment });
+    nodes.push({ comment_view: comment, children: [], depth: 0 });
   }
   return nodes;
 }
 
-function commentSort(tree: CommentNodeI[], sort: CommentSortType) {
-  // First, put removed and deleted comments at the bottom, then do your other sorts
-  if (sort == CommentSortType.Top) {
-    tree.sort(
-      (a, b) =>
-        +a.comment_view.comment.removed - +b.comment_view.comment.removed ||
-        +a.comment_view.comment.deleted - +b.comment_view.comment.deleted ||
-        b.comment_view.counts.score - a.comment_view.counts.score
-    );
-  } else if (sort == CommentSortType.New) {
-    tree.sort(
-      (a, b) =>
-        +a.comment_view.comment.removed - +b.comment_view.comment.removed ||
-        +a.comment_view.comment.deleted - +b.comment_view.comment.deleted ||
-        b.comment_view.comment.published.localeCompare(
-          a.comment_view.comment.published
-        )
-    );
-  } else if (sort == CommentSortType.Old) {
-    tree.sort(
-      (a, b) =>
-        +a.comment_view.comment.removed - +b.comment_view.comment.removed ||
-        +a.comment_view.comment.deleted - +b.comment_view.comment.deleted ||
-        a.comment_view.comment.published.localeCompare(
-          b.comment_view.comment.published
-        )
-    );
-  } else if (sort == CommentSortType.Hot) {
-    tree.sort(
-      (a, b) =>
-        +a.comment_view.comment.removed - +b.comment_view.comment.removed ||
-        +a.comment_view.comment.deleted - +b.comment_view.comment.deleted ||
-        hotRankComment(b.comment_view as CommentView) -
-          hotRankComment(a.comment_view as CommentView)
-    );
-  }
-
-  // Go through the children recursively
-  for (let node of tree) {
-    if (node.children) {
-      commentSort(node.children, sort);
-    }
-  }
-}
-
-export function commentSortSortType(tree: CommentNodeI[], sort: SortType) {
-  commentSort(tree, convertCommentSortType(sort));
-}
-
-function convertCommentSortType(sort: SortType): CommentSortType {
+export function convertCommentSortType(sort: SortType): CommentSortType {
   if (
     sort == SortType.TopAll ||
     sort == SortType.TopDay ||
@@ -1059,21 +1011,32 @@ function convertCommentSortType(sort: SortType): CommentSortType {
 
 export function buildCommentsTree(
   comments: CommentView[],
-  commentSortType: CommentSortType
+  parentComment: boolean
 ): CommentNodeI[] {
   let map = new Map<number, CommentNodeI>();
+  let depthOffset = !parentComment
+    ? 0
+    : getDepthFromComment(comments[0].comment);
+
   for (let comment_view of comments) {
     let node: CommentNodeI = {
       comment_view: comment_view,
       children: [],
-      depth: 0,
+      depth: getDepthFromComment(comment_view.comment) - depthOffset,
     };
     map.set(comment_view.comment.id, { ...node });
   }
+
   let tree: CommentNodeI[] = [];
+
+  // if its a parent comment fetch, then push the first comment to the top node.
+  if (parentComment) {
+    tree.push(map.get(comments[0].comment.id));
+  }
+
   for (let comment_view of comments) {
     let child = map.get(comment_view.comment.id);
-    let parent_id = comment_view.comment.parent_id;
+    let parent_id = getCommentParentId(comment_view.comment);
     parent_id.match({
       some: parentId => {
         let parent = map.get(parentId);
@@ -1083,26 +1046,37 @@ export function buildCommentsTree(
         }
       },
       none: () => {
-        tree.push(child);
+        if (!parentComment) {
+          tree.push(child);
+        }
       },
     });
-
-    setDepth(child);
   }
 
-  commentSort(tree, commentSortType);
-
   return tree;
 }
 
-function setDepth(node: CommentNodeI, i = 0) {
-  for (let child of node.children) {
-    child.depth = i;
-    setDepth(child, i + 1);
+export function getCommentParentId(comment: CommentI): Option<number> {
+  let split = comment.path.split(".");
+  // remove the 0
+  split.shift();
+
+  if (split.length > 1) {
+    return Some(Number(split[split.length - 2]));
+  } else {
+    return None;
   }
 }
 
-export function insertCommentIntoTree(tree: CommentNodeI[], cv: CommentView) {
+export function getDepthFromComment(comment: CommentI): number {
+  return comment.path.split(".").length - 2;
+}
+
+export function insertCommentIntoTree(
+  tree: CommentNodeI[],
+  cv: CommentView,
+  parentComment: boolean
+) {
   // Building a fake node to be used for later
   let node: CommentNodeI = {
     comment_view: cv,
@@ -1110,7 +1084,7 @@ export function insertCommentIntoTree(tree: CommentNodeI[], cv: CommentView) {
     depth: 0,
   };
 
-  cv.comment.parent_id.match({
+  getCommentParentId(cv.comment).match({
     some: parentId => {
       let parentComment = searchCommentTree(tree, parentId);
       parentComment.match({
@@ -1122,7 +1096,9 @@ export function insertCommentIntoTree(tree: CommentNodeI[], cv: CommentView) {
       });
     },
     none: () => {
-      tree.unshift(node);
+      if (!parentComment) {
+        tree.unshift(node);
+      }
     },
   });
 }
@@ -1149,6 +1125,7 @@ export function searchCommentTree(
 
 export const colorList: string[] = [
   hsl(0),
+  hsl(50),
   hsl(100),
   hsl(150),
   hsl(200),
@@ -1439,3 +1416,15 @@ export function enableDownvotes(siteRes: GetSiteResponse): boolean {
 export function enableNsfw(siteRes: GetSiteResponse): boolean {
   return siteRes.site_view.map(s => s.site.enable_nsfw).unwrapOr(false);
 }
+
+export function postToCommentSortType(sort: SortType): CommentSortType {
+  if ([SortType.Active, SortType.Hot].includes(sort)) {
+    return CommentSortType.Hot;
+  } else if ([SortType.New, SortType.NewComments].includes(sort)) {
+    return CommentSortType.New;
+  } else if (sort == SortType.Old) {
+    return CommentSortType.Old;
+  } else {
+    return CommentSortType.Top;
+  }
+}
index 7e37d660d1cda6f67f479adfb0ebead09b6c4431..f972e114b1a66f974aea13856da67446d6865403 100644 (file)
--- a/yarn.lock
+++ b/yarn.lock
@@ -5056,10 +5056,10 @@ lcid@^1.0.0:
   dependencies:
     invert-kv "^1.0.0"
 
-lemmy-js-client@0.17.0-rc.38:
-  version "0.17.0-rc.38"
-  resolved "https://registry.yarnpkg.com/lemmy-js-client/-/lemmy-js-client-0.17.0-rc.38.tgz#bbe0667ad44bbd0c1e516813d3c81b65c7f6a329"
-  integrity sha512-uDC19s3+Eva+Hu3LhIPkT5j0Ngh7F84TA4VnfxMVJN6BQZFLZUmTvAErwJcqyj5vz3sNnw4jsEeTSGPODSXfeg==
+lemmy-js-client@0.17.0-rc.39:
+  version "0.17.0-rc.39"
+  resolved "https://registry.yarnpkg.com/lemmy-js-client/-/lemmy-js-client-0.17.0-rc.39.tgz#b17c5c0d9a0f36c90c17be99845a5703091ab306"
+  integrity sha512-MsKavo5xOob6DgfjyhbmXyFvXwdW4iwftStJ7Bz3ArlHXy6zGBp+2uy2rU2c5ujivNDX72ol3TupTHBtSXLs4w==
 
 levn@^0.4.1:
   version "0.4.1"