Comments and posts no longer live-sorted. Fixes #51

- Only changing the view or sort, or reloading the page will resort.
- New comments by default added to the top, and left unsorted.
This commit is contained in:
Dessalines 2021-02-07 23:23:31 -05:00
parent 04955cc45e
commit b24eba6fea
7 changed files with 102 additions and 128 deletions

View file

@ -15,15 +15,10 @@ import {
AddAdmin, AddAdmin,
TransferCommunity, TransferCommunity,
TransferSite, TransferSite,
SortType,
CommentView, CommentView,
UserMentionView, UserMentionView,
} from 'lemmy-js-client'; } from 'lemmy-js-client';
import { import { CommentNode as CommentNodeI, BanType } from '../interfaces';
CommentSortType,
CommentNode as CommentNodeI,
BanType,
} from '../interfaces';
import { WebSocketService, UserService } from '../services'; import { WebSocketService, UserService } from '../services';
import { import {
mdToHtml, mdToHtml,
@ -82,8 +77,6 @@ interface CommentNodeProps {
// TODO is this necessary, can't I get it from the node itself? // TODO is this necessary, can't I get it from the node itself?
postCreatorId?: number; postCreatorId?: number;
showCommunity?: boolean; showCommunity?: boolean;
sort?: CommentSortType;
sortType?: SortType;
enableDownvotes: boolean; enableDownvotes: boolean;
} }
@ -745,8 +738,6 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
moderators={this.props.moderators} moderators={this.props.moderators}
admins={this.props.admins} admins={this.props.admins}
postCreatorId={this.props.postCreatorId} postCreatorId={this.props.postCreatorId}
sort={this.props.sort}
sortType={this.props.sortType}
enableDownvotes={this.props.enableDownvotes} enableDownvotes={this.props.enableDownvotes}
/> />
)} )}

View file

@ -1,11 +1,6 @@
import { Component } from 'inferno'; import { Component } from 'inferno';
import { CommentSortType, CommentNode as CommentNodeI } from '../interfaces'; import { CommentNode as CommentNodeI } from '../interfaces';
import { import { CommunityModeratorView, UserViewSafe } from 'lemmy-js-client';
CommunityModeratorView,
UserViewSafe,
SortType,
} from 'lemmy-js-client';
import { commentSort, commentSortSortType } from '../utils';
import { CommentNode } from './comment-node'; import { CommentNode } from './comment-node';
interface CommentNodesState {} interface CommentNodesState {}
@ -22,8 +17,6 @@ interface CommentNodesProps {
markable?: boolean; markable?: boolean;
showContext?: boolean; showContext?: boolean;
showCommunity?: boolean; showCommunity?: boolean;
sort?: CommentSortType;
sortType?: SortType;
enableDownvotes: boolean; enableDownvotes: boolean;
} }
@ -38,7 +31,7 @@ export class CommentNodes extends Component<
render() { render() {
return ( return (
<div className="comments"> <div className="comments">
{this.sorter().map(node => ( {this.props.nodes.map(node => (
<CommentNode <CommentNode
key={node.comment_view.comment.id} key={node.comment_view.comment.id}
node={node} node={node}
@ -52,22 +45,10 @@ export class CommentNodes extends Component<
markable={this.props.markable} markable={this.props.markable}
showContext={this.props.showContext} showContext={this.props.showContext}
showCommunity={this.props.showCommunity} showCommunity={this.props.showCommunity}
sort={this.props.sort}
sortType={this.props.sortType}
enableDownvotes={this.props.enableDownvotes} enableDownvotes={this.props.enableDownvotes}
/> />
))} ))}
</div> </div>
); );
} }
sorter(): CommentNodeI[] {
if (this.props.sort !== undefined) {
commentSort(this.props.nodes, this.props.sort);
} else if (this.props.sortType !== undefined) {
commentSortSortType(this.props.nodes, this.props.sortType);
}
return this.props.nodes;
}
} }

View file

@ -291,7 +291,6 @@ export class Community extends Component<any, State> {
<PostListings <PostListings
posts={this.state.posts} posts={this.state.posts}
removeDuplicates removeDuplicates
sort={this.state.sort}
enableDownvotes={site.enable_downvotes} enableDownvotes={site.enable_downvotes}
enableNsfw={site.enable_nsfw} enableNsfw={site.enable_nsfw}
/> />
@ -306,7 +305,6 @@ export class Community extends Component<any, State> {
<CommentNodes <CommentNodes
nodes={commentsToFlatNodes(this.state.comments)} nodes={commentsToFlatNodes(this.state.comments)}
noIndent noIndent
sortType={this.state.sort}
showContext showContext
enableDownvotes={site.enable_downvotes} enableDownvotes={site.enable_downvotes}
/> />

View file

@ -561,7 +561,6 @@ export class Main extends Component<any, MainState> {
posts={this.state.posts} posts={this.state.posts}
showCommunity showCommunity
removeDuplicates removeDuplicates
sort={this.state.sort}
enableDownvotes={site.enable_downvotes} enableDownvotes={site.enable_downvotes}
enableNsfw={site.enable_nsfw} enableNsfw={site.enable_nsfw}
/> />
@ -570,7 +569,6 @@ export class Main extends Component<any, MainState> {
nodes={commentsToFlatNodes(this.state.comments)} nodes={commentsToFlatNodes(this.state.comments)}
noIndent noIndent
showCommunity showCommunity
sortType={this.state.sort}
showContext showContext
enableDownvotes={site.enable_downvotes} enableDownvotes={site.enable_downvotes}
/> />

View file

@ -1,7 +1,6 @@
import { Component } from 'inferno'; import { Component } from 'inferno';
import { Link } from 'inferno-router'; import { Link } from 'inferno-router';
import { PostView, SortType } from 'lemmy-js-client'; import { PostView } from 'lemmy-js-client';
import { postSort } from '../utils';
import { PostListing } from './post-listing'; import { PostListing } from './post-listing';
import { i18n } from '../i18next'; import { i18n } from '../i18next';
import { T } from 'inferno-i18next'; import { T } from 'inferno-i18next';
@ -10,7 +9,6 @@ interface PostListingsProps {
posts: PostView[]; posts: PostView[];
showCommunity?: boolean; showCommunity?: boolean;
removeDuplicates?: boolean; removeDuplicates?: boolean;
sort?: SortType;
enableDownvotes: boolean; enableDownvotes: boolean;
enableNsfw: boolean; enableNsfw: boolean;
} }
@ -58,10 +56,6 @@ export class PostListings extends Component<PostListingsProps, any> {
out = this.removeDuplicates(out); out = this.removeDuplicates(out);
} }
if (this.props.sort !== undefined) {
postSort(out, this.props.sort, this.props.showCommunity == undefined);
}
return out; return out;
} }

View file

@ -52,6 +52,8 @@ import {
setOptionalAuth, setOptionalAuth,
saveScrollPosition, saveScrollPosition,
restoreScrollPosition, restoreScrollPosition,
buildCommentsTree,
insertCommentIntoTree,
} from '../utils'; } from '../utils';
import { PostListing } from './post-listing'; import { PostListing } from './post-listing';
import { Sidebar } from './sidebar'; import { Sidebar } from './sidebar';
@ -63,6 +65,7 @@ import { i18n } from '../i18next';
interface PostState { interface PostState {
postRes: GetPostResponse; postRes: GetPostResponse;
postId: number; postId: number;
commentTree: CommentNodeI[];
commentId?: number; commentId?: number;
commentSort: CommentSortType; commentSort: CommentSortType;
commentViewType: CommentViewType; commentViewType: CommentViewType;
@ -79,6 +82,7 @@ export class Post extends Component<any, PostState> {
private emptyState: PostState = { private emptyState: PostState = {
postRes: null, postRes: null,
postId: getIdFromProps(this.props), postId: getIdFromProps(this.props),
commentTree: [],
commentId: getCommentIdFromProps(this.props), commentId: getCommentIdFromProps(this.props),
commentSort: CommentSortType.Hot, commentSort: CommentSortType.Hot,
commentViewType: CommentViewType.Tree, commentViewType: CommentViewType.Tree,
@ -100,6 +104,10 @@ 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 = this.isoData.routeData[0]; this.state.postRes = this.isoData.routeData[0];
this.state.commentTree = buildCommentsTree(
this.state.postRes.comments,
this.state.commentSort
);
this.state.categories = this.isoData.routeData[1].categories; this.state.categories = this.isoData.routeData[1].categories;
this.state.loading = false; this.state.loading = false;
@ -368,6 +376,7 @@ export class Post extends Component<any, PostState> {
} }
commentsFlat() { commentsFlat() {
// These are already sorted by new
return ( return (
<div> <div>
<CommentNodes <CommentNodes
@ -379,7 +388,6 @@ export class Post extends Component<any, PostState> {
postCreatorId={this.state.postRes.post_view.creator.id} postCreatorId={this.state.postRes.post_view.creator.id}
showContext showContext
enableDownvotes={this.state.siteRes.site_view.site.enable_downvotes} enableDownvotes={this.state.siteRes.site_view.site.enable_downvotes}
sort={this.state.commentSort}
/> />
</div> </div>
); );
@ -404,58 +412,32 @@ 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 = Number(event.target.value);
i.state.commentViewType = CommentViewType.Tree; i.state.commentViewType = CommentViewType.Tree;
i.state.commentTree = buildCommentsTree(
i.state.postRes.comments,
i.state.commentSort
);
i.setState(i.state); i.setState(i.state);
} }
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.postRes.comments,
i.state.commentSort
);
i.setState(i.state); i.setState(i.state);
} }
buildCommentsTree(): CommentNodeI[] {
let map = new Map<number, CommentNodeI>();
for (let comment_view of this.state.postRes.comments) {
let node: CommentNodeI = {
comment_view: comment_view,
children: [],
};
map.set(comment_view.comment.id, { ...node });
}
let tree: CommentNodeI[] = [];
for (let comment_view of this.state.postRes.comments) {
let child = map.get(comment_view.comment.id);
if (comment_view.comment.parent_id) {
let parent_ = map.get(comment_view.comment.parent_id);
parent_.children.push(child);
} else {
tree.push(child);
}
this.setDepth(child);
}
return tree;
}
setDepth(node: CommentNodeI, i: number = 0): void {
for (let child of node.children) {
child.depth = i;
this.setDepth(child, i + 1);
}
}
commentsTree() { commentsTree() {
let nodes = this.buildCommentsTree();
return ( return (
<div> <div>
<CommentNodes <CommentNodes
nodes={nodes} nodes={this.state.commentTree}
locked={this.state.postRes.post_view.post.locked} locked={this.state.postRes.post_view.post.locked}
moderators={this.state.postRes.moderators} moderators={this.state.postRes.moderators}
admins={this.state.siteRes.admins} admins={this.state.siteRes.admins}
postCreatorId={this.state.postRes.post_view.creator.id} postCreatorId={this.state.postRes.post_view.creator.id}
sort={this.state.commentSort}
enableDownvotes={this.state.siteRes.site_view.site.enable_downvotes} enableDownvotes={this.state.siteRes.site_view.site.enable_downvotes}
/> />
</div> </div>
@ -480,6 +462,10 @@ export class Post extends Component<any, PostState> {
} else if (op == UserOperation.GetPost) { } else if (op == UserOperation.GetPost) {
let data = wsJsonToRes<GetPostResponse>(msg).data; let data = wsJsonToRes<GetPostResponse>(msg).data;
this.state.postRes = data; this.state.postRes = data;
this.state.commentTree = buildCommentsTree(
this.state.postRes.comments,
this.state.commentSort
);
this.state.loading = false; this.state.loading = false;
// Get cross-posts // Get cross-posts
@ -493,6 +479,7 @@ 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) { if (data.recipient_ids.length == 0) {
this.state.postRes.comments.unshift(data.comment_view); this.state.postRes.comments.unshift(data.comment_view);
insertCommentIntoTree(this.state.commentTree, data.comment_view);
this.state.postRes.post_view.counts.comments++; this.state.postRes.post_view.counts.comments++;
this.setState(this.state); this.setState(this.state);
setupTippy(); setupTippy();

View file

@ -942,7 +942,7 @@ export function commentsToFlatNodes(comments: CommentView[]): CommentNodeI[] {
return nodes; return nodes;
} }
export function commentSort(tree: CommentNodeI[], sort: CommentSortType) { function commentSort(tree: CommentNodeI[], sort: CommentSortType) {
// First, put removed and deleted comments at the bottom, then do your other sorts // First, put removed and deleted comments at the bottom, then do your other sorts
if (sort == CommentSortType.Top) { if (sort == CommentSortType.Top) {
tree.sort( tree.sort(
@ -1008,51 +1008,80 @@ function convertCommentSortType(sort: SortType): CommentSortType {
} }
} }
export function postSort( export function buildCommentsTree(
posts: PostView[], comments: CommentView[],
sort: SortType, commentSortType: CommentSortType
communityType: boolean ): CommentNodeI[] {
) { let map = new Map<number, CommentNodeI>();
// First, put removed and deleted comments at the bottom, then do your other sorts for (let comment_view of comments) {
if ( let node: CommentNodeI = {
sort == SortType.TopAll || comment_view: comment_view,
sort == SortType.TopDay || children: [],
sort == SortType.TopWeek || };
sort == SortType.TopMonth || map.set(comment_view.comment.id, { ...node });
sort == SortType.TopYear
) {
posts.sort(
(a, b) =>
+a.post.removed - +b.post.removed ||
+a.post.deleted - +b.post.deleted ||
(communityType && +b.post.stickied - +a.post.stickied) ||
b.counts.score - a.counts.score
);
} else if (sort == SortType.New) {
posts.sort(
(a, b) =>
+a.post.removed - +b.post.removed ||
+a.post.deleted - +b.post.deleted ||
(communityType && +b.post.stickied - +a.post.stickied) ||
b.post.published.localeCompare(a.post.published)
);
} else if (sort == SortType.Hot) {
posts.sort(
(a, b) =>
+a.post.removed - +b.post.removed ||
+a.post.deleted - +b.post.deleted ||
(communityType && +b.post.stickied - +a.post.stickied) ||
hotRankPost(b) - hotRankPost(a)
);
} else if (sort == SortType.Active) {
posts.sort(
(a, b) =>
+a.post.removed - +b.post.removed ||
+a.post.deleted - +b.post.deleted ||
(communityType && +b.post.stickied - +a.post.stickied) ||
hotRankActivePost(b) - hotRankActivePost(a)
);
} }
let tree: CommentNodeI[] = [];
for (let comment_view of comments) {
let child = map.get(comment_view.comment.id);
if (comment_view.comment.parent_id) {
let parent_ = map.get(comment_view.comment.parent_id);
parent_.children.push(child);
} else {
tree.push(child);
}
setDepth(child);
}
commentSort(tree, commentSortType);
return tree;
}
function setDepth(node: CommentNodeI, i: number = 0) {
for (let child of node.children) {
child.depth = i;
setDepth(child, i + 1);
}
}
export function insertCommentIntoTree(tree: CommentNodeI[], cv: CommentView) {
// Building a fake node to be used for later
let node: CommentNodeI = {
comment_view: cv,
children: [],
depth: 0,
};
if (cv.comment.parent_id) {
let parentComment = searchCommentTree(tree, cv.comment.parent_id);
if (parentComment) {
node.depth = parentComment.depth + 1;
parentComment.children.unshift(node);
}
} else {
tree.unshift(node);
}
}
export function searchCommentTree(
tree: CommentNodeI[],
id: number
): CommentNodeI {
for (let node of tree) {
if (node.comment_view.comment.id === id) {
return node;
}
for (const child of node.children) {
const res = searchCommentTree([child], id);
if (res) {
return res;
}
}
}
return null;
} }
export const colorList: string[] = [ export const colorList: string[] = [
@ -1068,10 +1097,6 @@ function hsl(num: number) {
return `hsla(${num}, 35%, 50%, 1)`; return `hsla(${num}, 35%, 50%, 1)`;
} }
// function randomHsl() {
// return `hsla(${Math.random() * 360}, 100%, 50%, 1)`;
// }
export function previewLines( export function previewLines(
text: string, text: string,
maxChars: number = 300, maxChars: number = 300,