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