import { Component, linkEvent } from 'inferno'; import { Subscription } from "rxjs"; import { retryWhen, delay, take } from 'rxjs/operators'; import { UserOperation, Community, Post as PostI, GetPostResponse, PostResponse, Comment, CommentForm as CommentFormI, CommentResponse, CommentSortType, CreatePostLikeResponse, CommunityUser, CommunityResponse, CommentNode as CommentNodeI, BanFromCommunityResponse, BanUserResponse, AddModToCommunityResponse, AddAdminResponse, UserView, SearchType, SortType, SearchForm, SearchResponse } from '../interfaces'; import { WebSocketService, UserService } from '../services'; import { msgOp, hotRank } from '../utils'; import { PostListing } from './post-listing'; import { PostListings } from './post-listings'; import { Sidebar } from './sidebar'; import { CommentForm } from './comment-form'; import { CommentNodes } from './comment-nodes'; import * as autosize from 'autosize'; import { i18n } from '../i18next'; import { T } from 'inferno-i18next'; interface PostState { post: PostI; comments: Array; commentSort: CommentSortType; community: Community; moderators: Array; admins: Array; scrolled?: boolean; scrolled_comment_id?: number; loading: boolean; crossPosts: Array; } export class Post extends Component { private subscription: Subscription; private emptyState: PostState = { post: null, comments: [], commentSort: CommentSortType.Hot, community: null, moderators: [], admins: [], scrolled: false, loading: true, crossPosts: [], } constructor(props: any, context: any) { super(props, context); this.state = this.emptyState; let postId = Number(this.props.match.params.id); if (this.props.match.params.comment_id) { this.state.scrolled_comment_id = this.props.match.params.comment_id; } this.subscription = WebSocketService.Instance.subject .pipe(retryWhen(errors => errors.pipe(delay(3000), take(10)))) .subscribe( (msg) => this.parseMessage(msg), (err) => console.error(err), () => console.log('complete') ); WebSocketService.Instance.getPost(postId); } componentWillUnmount() { this.subscription.unsubscribe(); } componentDidMount() { autosize(document.querySelectorAll('textarea')); } componentDidUpdate(_lastProps: any, lastState: PostState, _snapshot: any) { if (this.state.scrolled_comment_id && !this.state.scrolled && lastState.comments.length > 0) { var elmnt = document.getElementById(`comment-${this.state.scrolled_comment_id}`); elmnt.scrollIntoView(); elmnt.classList.add("mark-two"); this.state.scrolled = true; this.markScrolledAsRead(this.state.scrolled_comment_id); } } markScrolledAsRead(commentId: number) { let found = this.state.comments.find(c => c.id == commentId); let parent = this.state.comments.find(c => found.parent_id == c.id); let parent_user_id = parent ? parent.creator_id : this.state.post.creator_id; if (UserService.Instance.user && UserService.Instance.user.id == parent_user_id) { let form: CommentFormI = { content: found.content, edit_id: found.id, creator_id: found.creator_id, post_id: found.post_id, parent_id: found.parent_id, read: true, auth: null }; WebSocketService.Instance.editComment(form); } } render() { return (
{this.state.loading ?
:
{this.state.crossPosts.length > 0 && <>
#
}
{this.sortRadios()} {this.commentsTree()}
{this.state.comments.length > 0 && this.newComments()}
{this.sidebar()}
}
) } sortRadios() { return (
) } newComments() { return (
#
{this.state.comments.map(comment => )}
) } sidebar() { return (
); } handleCommentSortChange(i: Post, event: any) { i.state.commentSort = Number(event.target.value); i.setState(i.state); } private buildCommentsTree(): Array { let map = new Map(); for (let comment of this.state.comments) { let node: CommentNodeI = { comment: comment, children: [] }; map.set(comment.id, { ...node }); } let tree: Array = []; for (let comment of this.state.comments) { if( comment.parent_id ) { map.get(comment.parent_id).children.push(map.get(comment.id)); } else { tree.push(map.get(comment.id)); } } this.sortTree(tree); return tree; } sortTree(tree: Array) { if (this.state.commentSort == CommentSortType.Top) { tree.sort((a, b) => b.comment.score - a.comment.score); } else if (this.state.commentSort == CommentSortType.New) { tree.sort((a, b) => b.comment.published.localeCompare(a.comment.published)); } else if (this.state.commentSort == CommentSortType.Hot) { tree.sort((a, b) => hotRank(b.comment) - hotRank(a.comment)); } for (let node of tree) { this.sortTree(node.children); } } commentsTree() { let nodes = this.buildCommentsTree(); return (
); } parseMessage(msg: any) { console.log(msg); let op: UserOperation = msgOp(msg); if (msg.error) { alert(i18n.t(msg.error)); return; } else if (op == UserOperation.GetPost) { let res: GetPostResponse = msg; this.state.post = res.post; this.state.post = res.post; this.state.comments = res.comments; this.state.community = res.community; this.state.moderators = res.moderators; this.state.admins = res.admins; this.state.loading = false; document.title = `${this.state.post.name} - ${WebSocketService.Instance.site.name}`; // Get cross-posts let form: SearchForm = { q: res.post.url, type_: SearchType[SearchType.Url], sort: SortType[SortType.TopAll], page: 1, limit: 6, }; WebSocketService.Instance.search(form); this.setState(this.state); } else if (op == UserOperation.CreateComment) { let res: CommentResponse = msg; this.state.comments.unshift(res.comment); this.setState(this.state); } else if (op == UserOperation.EditComment) { let res: CommentResponse = msg; let found = this.state.comments.find(c => c.id == res.comment.id); found.content = res.comment.content; found.updated = res.comment.updated; found.removed = res.comment.removed; found.deleted = res.comment.deleted; found.upvotes = res.comment.upvotes; found.downvotes = res.comment.downvotes; found.score = res.comment.score; found.read = res.comment.read; this.setState(this.state); } else if (op == UserOperation.SaveComment) { let res: CommentResponse = msg; let found = this.state.comments.find(c => c.id == res.comment.id); found.saved = res.comment.saved; this.setState(this.state); } else if (op == UserOperation.CreateCommentLike) { let res: CommentResponse = msg; let found: Comment = this.state.comments.find(c => c.id === res.comment.id); found.score = res.comment.score; found.upvotes = res.comment.upvotes; found.downvotes = res.comment.downvotes; if (res.comment.my_vote !== null) found.my_vote = res.comment.my_vote; this.setState(this.state); } else if (op == UserOperation.CreatePostLike) { let res: CreatePostLikeResponse = msg; this.state.post.my_vote = res.post.my_vote; this.state.post.score = res.post.score; this.state.post.upvotes = res.post.upvotes; this.state.post.downvotes = res.post.downvotes; this.setState(this.state); } else if (op == UserOperation.EditPost) { let res: PostResponse = msg; this.state.post = res.post; this.setState(this.state); } else if (op == UserOperation.SavePost) { let res: PostResponse = msg; this.state.post = res.post; this.setState(this.state); } else if (op == UserOperation.EditCommunity) { let res: CommunityResponse = msg; this.state.community = res.community; this.state.post.community_id = res.community.id; this.state.post.community_name = res.community.name; this.setState(this.state); } else if (op == UserOperation.FollowCommunity) { let res: CommunityResponse = msg; this.state.community.subscribed = res.community.subscribed; this.state.community.number_of_subscribers = res.community.number_of_subscribers; this.setState(this.state); } else if (op == UserOperation.BanFromCommunity) { let res: BanFromCommunityResponse = msg; this.state.comments.filter(c => c.creator_id == res.user.id) .forEach(c => c.banned_from_community = res.banned); this.setState(this.state); } else if (op == UserOperation.AddModToCommunity) { let res: AddModToCommunityResponse = msg; this.state.moderators = res.moderators; this.setState(this.state); } else if (op == UserOperation.BanUser) { let res: BanUserResponse = msg; this.state.comments.filter(c => c.creator_id == res.user.id) .forEach(c => c.banned = res.banned); this.setState(this.state); } else if (op == UserOperation.AddAdmin) { let res: AddAdminResponse = msg; this.state.admins = res.admins; this.setState(this.state); } else if (op == UserOperation.Search) { let res: SearchResponse = msg; this.state.crossPosts = res.posts.filter(p => p.id != this.state.post.id); this.setState(this.state); } } }