]> Untitled Git - lemmy-ui.git/blob - src/shared/components/post/post.tsx
Upgrade inferno v8.0.0 try2 (#790)
[lemmy-ui.git] / src / shared / components / post / post.tsx
1 import { None, Option, Right, Some } from "@sniptt/monads";
2 import autosize from "autosize";
3 import { Component, createRef, linkEvent, RefObject } from "inferno";
4 import {
5   AddAdminResponse,
6   AddModToCommunityResponse,
7   BanFromCommunityResponse,
8   BanPersonResponse,
9   BlockPersonResponse,
10   CommentNode as CommentNodeI,
11   CommentReportResponse,
12   CommentResponse,
13   CommentSortType,
14   CommunityResponse,
15   GetComments,
16   GetCommentsResponse,
17   GetCommunityResponse,
18   GetPost,
19   GetPostResponse,
20   GetSiteResponse,
21   ListingType,
22   PostReportResponse,
23   PostResponse,
24   PostView,
25   PurgeItemResponse,
26   Search,
27   SearchResponse,
28   SearchType,
29   SortType,
30   toOption,
31   UserOperation,
32   wsJsonToRes,
33   wsUserOp,
34 } from "lemmy-js-client";
35 import { Subscription } from "rxjs";
36 import { i18n } from "../../i18next";
37 import { CommentViewType, InitialFetchRequest } from "../../interfaces";
38 import { UserService, WebSocketService } from "../../services";
39 import {
40   auth,
41   buildCommentsTree,
42   commentsToFlatNodes,
43   commentTreeMaxDepth,
44   createCommentLikeRes,
45   createPostLikeRes,
46   debounce,
47   editCommentRes,
48   enableDownvotes,
49   enableNsfw,
50   getCommentIdFromProps,
51   getCommentParentId,
52   getDepthFromComment,
53   getIdFromProps,
54   insertCommentIntoTree,
55   isBrowser,
56   isImage,
57   restoreScrollPosition,
58   saveCommentRes,
59   saveScrollPosition,
60   setIsoData,
61   setupTippy,
62   toast,
63   trendingFetchLimit,
64   updatePersonBlock,
65   wsClient,
66   wsSubscribe,
67 } from "../../utils";
68 import { CommentForm } from "../comment/comment-form";
69 import { CommentNodes } from "../comment/comment-nodes";
70 import { HtmlTags } from "../common/html-tags";
71 import { Icon, Spinner } from "../common/icon";
72 import { Sidebar } from "../community/sidebar";
73 import { PostListing } from "./post-listing";
74
75 const commentsShownInterval = 15;
76
77 interface PostState {
78   postId: Option<number>;
79   commentId: Option<number>;
80   postRes: Option<GetPostResponse>;
81   commentsRes: Option<GetCommentsResponse>;
82   commentTree: CommentNodeI[];
83   commentSort: CommentSortType;
84   commentViewType: CommentViewType;
85   scrolled?: boolean;
86   loading: boolean;
87   crossPosts: Option<PostView[]>;
88   siteRes: GetSiteResponse;
89   commentSectionRef?: RefObject<HTMLDivElement>;
90   showSidebarMobile: boolean;
91   maxCommentsShown: number;
92 }
93
94 export class Post extends Component<any, PostState> {
95   private subscription: Subscription;
96   private isoData = setIsoData(
97     this.context,
98     GetPostResponse,
99     GetCommentsResponse
100   );
101   private commentScrollDebounced: () => void;
102   private emptyState: PostState = {
103     postRes: None,
104     commentsRes: None,
105     postId: getIdFromProps(this.props),
106     commentId: getCommentIdFromProps(this.props),
107     commentTree: [],
108     commentSort: CommentSortType[CommentSortType.Hot],
109     commentViewType: CommentViewType.Tree,
110     scrolled: false,
111     loading: true,
112     crossPosts: None,
113     siteRes: this.isoData.site_res,
114     commentSectionRef: null,
115     showSidebarMobile: false,
116     maxCommentsShown: commentsShownInterval,
117   };
118
119   constructor(props: any, context: any) {
120     super(props, context);
121
122     this.state = this.emptyState;
123
124     this.parseMessage = this.parseMessage.bind(this);
125     this.subscription = wsSubscribe(this.parseMessage);
126
127     this.state = { ...this.state, commentSectionRef: createRef() };
128
129     // Only fetch the data if coming from another route
130     if (this.isoData.path == this.context.router.route.match.url) {
131       this.state = {
132         ...this.state,
133         postRes: Some(this.isoData.routeData[0] as GetPostResponse),
134         commentsRes: Some(this.isoData.routeData[1] as GetCommentsResponse),
135       };
136
137       if (this.state.commentsRes.isSome()) {
138         this.state = {
139           ...this.state,
140           commentTree: buildCommentsTree(
141             this.state.commentsRes.unwrap().comments,
142             this.state.commentId.isSome()
143           ),
144         };
145       }
146
147       this.state = { ...this.state, loading: false };
148
149       if (isBrowser()) {
150         WebSocketService.Instance.send(
151           wsClient.communityJoin({
152             community_id:
153               this.state.postRes.unwrap().community_view.community.id,
154           })
155         );
156
157         this.state.postId.match({
158           some: post_id =>
159             WebSocketService.Instance.send(wsClient.postJoin({ post_id })),
160           none: void 0,
161         });
162
163         this.fetchCrossPosts();
164
165         if (this.checkScrollIntoCommentsParam) {
166           this.scrollIntoCommentSection();
167         }
168       }
169     } else {
170       this.fetchPost();
171     }
172   }
173
174   fetchPost() {
175     this.setState({ commentsRes: None });
176     let postForm = new GetPost({
177       id: this.state.postId,
178       comment_id: this.state.commentId,
179       auth: auth(false).ok(),
180     });
181     WebSocketService.Instance.send(wsClient.getPost(postForm));
182
183     let commentsForm = new GetComments({
184       post_id: this.state.postId,
185       parent_id: this.state.commentId,
186       max_depth: Some(commentTreeMaxDepth),
187       page: None,
188       limit: None,
189       sort: Some(this.state.commentSort),
190       type_: Some(ListingType.All),
191       community_name: None,
192       community_id: None,
193       saved_only: Some(false),
194       auth: auth(false).ok(),
195     });
196     WebSocketService.Instance.send(wsClient.getComments(commentsForm));
197   }
198
199   fetchCrossPosts() {
200     this.state.postRes
201       .andThen(r => r.post_view.post.url)
202       .match({
203         some: url => {
204           let form = new Search({
205             q: url,
206             type_: Some(SearchType.Url),
207             sort: Some(SortType.TopAll),
208             listing_type: Some(ListingType.All),
209             page: Some(1),
210             limit: Some(trendingFetchLimit),
211             community_id: None,
212             community_name: None,
213             creator_id: None,
214             auth: auth(false).ok(),
215           });
216           WebSocketService.Instance.send(wsClient.search(form));
217         },
218         none: void 0,
219       });
220   }
221
222   static fetchInitialData(req: InitialFetchRequest): Promise<any>[] {
223     let pathSplit = req.path.split("/");
224     let promises: Promise<any>[] = [];
225
226     let pathType = pathSplit[1];
227     let id = Number(pathSplit[2]);
228
229     let postForm = new GetPost({
230       id: None,
231       comment_id: None,
232       auth: req.auth,
233     });
234
235     let commentsForm = new GetComments({
236       post_id: None,
237       parent_id: None,
238       max_depth: Some(commentTreeMaxDepth),
239       page: None,
240       limit: None,
241       sort: Some(CommentSortType.Hot),
242       type_: Some(ListingType.All),
243       community_name: None,
244       community_id: None,
245       saved_only: Some(false),
246       auth: req.auth,
247     });
248
249     // Set the correct id based on the path type
250     if (pathType == "post") {
251       postForm.id = Some(id);
252       commentsForm.post_id = Some(id);
253     } else {
254       postForm.comment_id = Some(id);
255       commentsForm.parent_id = Some(id);
256     }
257
258     promises.push(req.client.getPost(postForm));
259     promises.push(req.client.getComments(commentsForm));
260
261     return promises;
262   }
263
264   componentWillUnmount() {
265     this.subscription.unsubscribe();
266     document.removeEventListener("scroll", this.commentScrollDebounced);
267
268     saveScrollPosition(this.context);
269   }
270
271   componentDidMount() {
272     autosize(document.querySelectorAll("textarea"));
273
274     this.commentScrollDebounced = debounce(this.trackCommentsBoxScrolling, 100);
275     document.addEventListener("scroll", this.commentScrollDebounced);
276   }
277
278   componentDidUpdate(_lastProps: any) {
279     // Necessary if you are on a post and you click another post (same route)
280     if (_lastProps.location.pathname !== _lastProps.history.location.pathname) {
281       // TODO Couldnt get a refresh working. This does for now.
282       location.reload();
283
284       // let currentId = this.props.match.params.id;
285       // WebSocketService.Instance.getPost(currentId);
286       // this.context.refresh();
287       // this.context.router.history.push(_lastProps.location.pathname);
288     }
289   }
290
291   get checkScrollIntoCommentsParam() {
292     return Boolean(
293       new URLSearchParams(this.props.location.search).get("scrollToComments")
294     );
295   }
296
297   scrollIntoCommentSection() {
298     this.state.commentSectionRef.current?.scrollIntoView();
299   }
300
301   isBottom(el: Element): boolean {
302     return el?.getBoundingClientRect().bottom <= window.innerHeight;
303   }
304
305   /**
306    * Shows new comments when scrolling to the bottom of the comments div
307    */
308   trackCommentsBoxScrolling = () => {
309     const wrappedElement = document.getElementsByClassName("comments")[0];
310     if (wrappedElement && this.isBottom(wrappedElement)) {
311       this.setState({
312         maxCommentsShown: this.state.maxCommentsShown + commentsShownInterval,
313       });
314     }
315   };
316
317   get documentTitle(): string {
318     return this.state.postRes.match({
319       some: res =>
320         this.state.siteRes.site_view.match({
321           some: siteView =>
322             `${res.post_view.post.name} - ${siteView.site.name}`,
323           none: "",
324         }),
325       none: "",
326     });
327   }
328
329   get imageTag(): Option<string> {
330     return this.state.postRes.match({
331       some: res =>
332         res.post_view.post.thumbnail_url.or(
333           res.post_view.post.url.match({
334             some: url => (isImage(url) ? Some(url) : None),
335             none: None,
336           })
337         ),
338       none: None,
339     });
340   }
341
342   get descriptionTag(): Option<string> {
343     return this.state.postRes.andThen(r => r.post_view.post.body);
344   }
345
346   render() {
347     return (
348       <div className="container">
349         {this.state.loading ? (
350           <h5>
351             <Spinner large />
352           </h5>
353         ) : (
354           this.state.postRes.match({
355             some: res => (
356               <div className="row">
357                 <div className="col-12 col-md-8 mb-3">
358                   <HtmlTags
359                     title={this.documentTitle}
360                     path={this.context.router.route.match.url}
361                     image={this.imageTag}
362                     description={this.descriptionTag}
363                   />
364                   <PostListing
365                     post_view={res.post_view}
366                     duplicates={this.state.crossPosts}
367                     showBody
368                     showCommunity
369                     moderators={Some(res.moderators)}
370                     admins={Some(this.state.siteRes.admins)}
371                     enableDownvotes={enableDownvotes(this.state.siteRes)}
372                     enableNsfw={enableNsfw(this.state.siteRes)}
373                   />
374                   <div ref={this.state.commentSectionRef} className="mb-2" />
375                   <CommentForm
376                     node={Right(res.post_view.post.id)}
377                     disabled={res.post_view.post.locked}
378                   />
379                   <div className="d-block d-md-none">
380                     <button
381                       className="btn btn-secondary d-inline-block mb-2 mr-3"
382                       onClick={linkEvent(this, this.handleShowSidebarMobile)}
383                     >
384                       {i18n.t("sidebar")}{" "}
385                       <Icon
386                         icon={
387                           this.state.showSidebarMobile
388                             ? `minus-square`
389                             : `plus-square`
390                         }
391                         classes="icon-inline"
392                       />
393                     </button>
394                     {this.state.showSidebarMobile && this.sidebar()}
395                   </div>
396                   {this.sortRadios()}
397                   {this.state.commentViewType == CommentViewType.Tree &&
398                     this.commentsTree()}
399                   {this.state.commentViewType == CommentViewType.Flat &&
400                     this.commentsFlat()}
401                 </div>
402                 <div className="d-none d-md-block col-md-4">
403                   {this.sidebar()}
404                 </div>
405               </div>
406             ),
407             none: <></>,
408           })
409         )}
410       </div>
411     );
412   }
413
414   sortRadios() {
415     return (
416       <>
417         <div className="btn-group btn-group-toggle flex-wrap mr-3 mb-2">
418           <label
419             className={`btn btn-outline-secondary pointer ${
420               CommentSortType[this.state.commentSort] === CommentSortType.Hot &&
421               "active"
422             }`}
423           >
424             {i18n.t("hot")}
425             <input
426               type="radio"
427               value={CommentSortType.Hot}
428               checked={this.state.commentSort === CommentSortType.Hot}
429               onChange={linkEvent(this, this.handleCommentSortChange)}
430             />
431           </label>
432           <label
433             className={`btn btn-outline-secondary pointer ${
434               CommentSortType[this.state.commentSort] === CommentSortType.Top &&
435               "active"
436             }`}
437           >
438             {i18n.t("top")}
439             <input
440               type="radio"
441               value={CommentSortType.Top}
442               checked={this.state.commentSort === CommentSortType.Top}
443               onChange={linkEvent(this, this.handleCommentSortChange)}
444             />
445           </label>
446           <label
447             className={`btn btn-outline-secondary pointer ${
448               CommentSortType[this.state.commentSort] === CommentSortType.New &&
449               "active"
450             }`}
451           >
452             {i18n.t("new")}
453             <input
454               type="radio"
455               value={CommentSortType.New}
456               checked={this.state.commentSort === CommentSortType.New}
457               onChange={linkEvent(this, this.handleCommentSortChange)}
458             />
459           </label>
460           <label
461             className={`btn btn-outline-secondary pointer ${
462               CommentSortType[this.state.commentSort] === CommentSortType.Old &&
463               "active"
464             }`}
465           >
466             {i18n.t("old")}
467             <input
468               type="radio"
469               value={CommentSortType.Old}
470               checked={this.state.commentSort === CommentSortType.Old}
471               onChange={linkEvent(this, this.handleCommentSortChange)}
472             />
473           </label>
474         </div>
475         <div className="btn-group btn-group-toggle flex-wrap mb-2">
476           <label
477             className={`btn btn-outline-secondary pointer ${
478               this.state.commentViewType === CommentViewType.Flat && "active"
479             }`}
480           >
481             {i18n.t("chat")}
482             <input
483               type="radio"
484               value={CommentViewType.Flat}
485               checked={this.state.commentViewType === CommentViewType.Flat}
486               onChange={linkEvent(this, this.handleCommentViewTypeChange)}
487             />
488           </label>
489         </div>
490       </>
491     );
492   }
493
494   commentsFlat() {
495     // These are already sorted by new
496     return this.state.commentsRes.match({
497       some: commentsRes =>
498         this.state.postRes.match({
499           some: postRes => (
500             <div>
501               <CommentNodes
502                 nodes={commentsToFlatNodes(commentsRes.comments)}
503                 viewType={this.state.commentViewType}
504                 maxCommentsShown={Some(this.state.maxCommentsShown)}
505                 noIndent
506                 locked={postRes.post_view.post.locked}
507                 moderators={Some(postRes.moderators)}
508                 admins={Some(this.state.siteRes.admins)}
509                 enableDownvotes={enableDownvotes(this.state.siteRes)}
510                 showContext
511               />
512             </div>
513           ),
514           none: <></>,
515         }),
516       none: <></>,
517     });
518   }
519
520   sidebar() {
521     return this.state.postRes.match({
522       some: res => (
523         <div className="mb-3">
524           <Sidebar
525             community_view={res.community_view}
526             moderators={res.moderators}
527             admins={this.state.siteRes.admins}
528             online={res.online}
529             enableNsfw={enableNsfw(this.state.siteRes)}
530             showIcon
531           />
532         </div>
533       ),
534       none: <></>,
535     });
536   }
537
538   handleCommentSortChange(i: Post, event: any) {
539     i.setState({
540       commentSort: CommentSortType[event.target.value],
541       commentViewType: CommentViewType.Tree,
542     });
543     i.fetchPost();
544   }
545
546   handleCommentViewTypeChange(i: Post, event: any) {
547     i.setState({
548       commentViewType: Number(event.target.value),
549       commentSort: CommentSortType.New,
550       commentTree: buildCommentsTree(
551         i.state.commentsRes.map(r => r.comments).unwrapOr([]),
552         i.state.commentId.isSome()
553       ),
554     });
555   }
556
557   handleShowSidebarMobile(i: Post) {
558     i.setState({ showSidebarMobile: !i.state.showSidebarMobile });
559   }
560
561   handleViewPost(i: Post) {
562     i.state.postRes.match({
563       some: res =>
564         i.context.router.history.push(`/post/${res.post_view.post.id}`),
565       none: void 0,
566     });
567   }
568
569   handleViewContext(i: Post) {
570     i.state.commentsRes.match({
571       some: res =>
572         i.context.router.history.push(
573           `/comment/${getCommentParentId(res.comments[0].comment).unwrap()}`
574         ),
575       none: void 0,
576     });
577   }
578
579   commentsTree() {
580     let showContextButton = toOption(this.state.commentTree[0]).match({
581       some: comment => getDepthFromComment(comment.comment_view.comment) > 0,
582       none: false,
583     });
584
585     return this.state.postRes.match({
586       some: res => (
587         <div>
588           {this.state.commentId.isSome() && (
589             <>
590               <button
591                 className="pl-0 d-block btn btn-link text-muted"
592                 onClick={linkEvent(this, this.handleViewPost)}
593               >
594                 {i18n.t("view_all_comments")} âž”
595               </button>
596               {showContextButton && (
597                 <button
598                   className="pl-0 d-block btn btn-link text-muted"
599                   onClick={linkEvent(this, this.handleViewContext)}
600                 >
601                   {i18n.t("show_context")} âž”
602                 </button>
603               )}
604             </>
605           )}
606           <CommentNodes
607             nodes={this.state.commentTree}
608             viewType={this.state.commentViewType}
609             maxCommentsShown={Some(this.state.maxCommentsShown)}
610             locked={res.post_view.post.locked}
611             moderators={Some(res.moderators)}
612             admins={Some(this.state.siteRes.admins)}
613             enableDownvotes={enableDownvotes(this.state.siteRes)}
614           />
615         </div>
616       ),
617       none: <></>,
618     });
619   }
620
621   parseMessage(msg: any) {
622     let op = wsUserOp(msg);
623     console.log(msg);
624     if (msg.error) {
625       toast(i18n.t(msg.error), "danger");
626       return;
627     } else if (msg.reconnect) {
628       this.state.postRes.match({
629         some: res => {
630           let postId = res.post_view.post.id;
631           WebSocketService.Instance.send(
632             wsClient.postJoin({ post_id: postId })
633           );
634           WebSocketService.Instance.send(
635             wsClient.getPost({
636               id: Some(postId),
637               comment_id: None,
638               auth: auth(false).ok(),
639             })
640           );
641         },
642         none: void 0,
643       });
644     } else if (op == UserOperation.GetPost) {
645       let data = wsJsonToRes<GetPostResponse>(msg, GetPostResponse);
646       this.setState({ postRes: Some(data) });
647
648       // join the rooms
649       WebSocketService.Instance.send(
650         wsClient.postJoin({ post_id: data.post_view.post.id })
651       );
652       WebSocketService.Instance.send(
653         wsClient.communityJoin({
654           community_id: data.community_view.community.id,
655         })
656       );
657
658       // Get cross-posts
659       // TODO move this into initial fetch and refetch
660       this.fetchCrossPosts();
661       setupTippy();
662       if (this.state.commentId.isNone()) restoreScrollPosition(this.context);
663
664       if (this.checkScrollIntoCommentsParam) {
665         this.scrollIntoCommentSection();
666       }
667     } else if (op == UserOperation.GetComments) {
668       let data = wsJsonToRes<GetCommentsResponse>(msg, GetCommentsResponse);
669       // You might need to append here, since this could be building more comments from a tree fetch
670       this.state.commentsRes.match({
671         some: res => {
672           // Remove the first comment, since it is the parent
673           let newComments = data.comments;
674           newComments.shift();
675           res.comments.push(...newComments);
676         },
677         none: () => this.setState({ commentsRes: Some(data) }),
678       });
679       // this.state.commentsRes = Some(data);
680       this.setState({
681         commentTree: buildCommentsTree(
682           this.state.commentsRes.map(r => r.comments).unwrapOr([]),
683           this.state.commentId.isSome()
684         ),
685         loading: false,
686       });
687       this.setState(this.state);
688     } else if (op == UserOperation.CreateComment) {
689       let data = wsJsonToRes<CommentResponse>(msg, CommentResponse);
690
691       // Don't get comments from the post room, if the creator is blocked
692       let creatorBlocked = UserService.Instance.myUserInfo
693         .map(m => m.person_blocks)
694         .unwrapOr([])
695         .map(pb => pb.target.id)
696         .includes(data.comment_view.creator.id);
697
698       // Necessary since it might be a user reply, which has the recipients, to avoid double
699       if (data.recipient_ids.length == 0 && !creatorBlocked) {
700         this.state.postRes.match({
701           some: postRes =>
702             this.state.commentsRes.match({
703               some: commentsRes => {
704                 commentsRes.comments.unshift(data.comment_view);
705                 insertCommentIntoTree(
706                   this.state.commentTree,
707                   data.comment_view,
708                   this.state.commentId.isSome()
709                 );
710                 postRes.post_view.counts.comments++;
711               },
712               none: void 0,
713             }),
714           none: void 0,
715         });
716         this.setState(this.state);
717         setupTippy();
718       }
719     } else if (
720       op == UserOperation.EditComment ||
721       op == UserOperation.DeleteComment ||
722       op == UserOperation.RemoveComment
723     ) {
724       let data = wsJsonToRes<CommentResponse>(msg, CommentResponse);
725       editCommentRes(
726         data.comment_view,
727         this.state.commentsRes.map(r => r.comments).unwrapOr([])
728       );
729       this.setState(this.state);
730       setupTippy();
731     } else if (op == UserOperation.SaveComment) {
732       let data = wsJsonToRes<CommentResponse>(msg, CommentResponse);
733       saveCommentRes(
734         data.comment_view,
735         this.state.commentsRes.map(r => r.comments).unwrapOr([])
736       );
737       this.setState(this.state);
738       setupTippy();
739     } else if (op == UserOperation.CreateCommentLike) {
740       let data = wsJsonToRes<CommentResponse>(msg, CommentResponse);
741       createCommentLikeRes(
742         data.comment_view,
743         this.state.commentsRes.map(r => r.comments).unwrapOr([])
744       );
745       this.setState(this.state);
746     } else if (op == UserOperation.CreatePostLike) {
747       let data = wsJsonToRes<PostResponse>(msg, PostResponse);
748       this.state.postRes.match({
749         some: res => createPostLikeRes(data.post_view, res.post_view),
750         none: void 0,
751       });
752       this.setState(this.state);
753     } else if (
754       op == UserOperation.EditPost ||
755       op == UserOperation.DeletePost ||
756       op == UserOperation.RemovePost ||
757       op == UserOperation.LockPost ||
758       op == UserOperation.StickyPost ||
759       op == UserOperation.SavePost
760     ) {
761       let data = wsJsonToRes<PostResponse>(msg, PostResponse);
762       this.state.postRes.match({
763         some: res => (res.post_view = data.post_view),
764         none: void 0,
765       });
766       this.setState(this.state);
767       setupTippy();
768     } else if (
769       op == UserOperation.EditCommunity ||
770       op == UserOperation.DeleteCommunity ||
771       op == UserOperation.RemoveCommunity ||
772       op == UserOperation.FollowCommunity
773     ) {
774       let data = wsJsonToRes<CommunityResponse>(msg, CommunityResponse);
775       this.state.postRes.match({
776         some: res => {
777           res.community_view = data.community_view;
778           res.post_view.community = data.community_view.community;
779           this.setState(this.state);
780         },
781         none: void 0,
782       });
783     } else if (op == UserOperation.BanFromCommunity) {
784       let data = wsJsonToRes<BanFromCommunityResponse>(
785         msg,
786         BanFromCommunityResponse
787       );
788       this.state.postRes.match({
789         some: postRes =>
790           this.state.commentsRes.match({
791             some: commentsRes => {
792               commentsRes.comments
793                 .filter(c => c.creator.id == data.person_view.person.id)
794                 .forEach(c => (c.creator_banned_from_community = data.banned));
795               if (postRes.post_view.creator.id == data.person_view.person.id) {
796                 postRes.post_view.creator_banned_from_community = data.banned;
797               }
798               this.setState(this.state);
799             },
800             none: void 0,
801           }),
802         none: void 0,
803       });
804     } else if (op == UserOperation.AddModToCommunity) {
805       let data = wsJsonToRes<AddModToCommunityResponse>(
806         msg,
807         AddModToCommunityResponse
808       );
809       this.state.postRes.match({
810         some: res => {
811           res.moderators = data.moderators;
812           this.setState(this.state);
813         },
814         none: void 0,
815       });
816     } else if (op == UserOperation.BanPerson) {
817       let data = wsJsonToRes<BanPersonResponse>(msg, BanPersonResponse);
818       this.state.postRes.match({
819         some: postRes =>
820           this.state.commentsRes.match({
821             some: commentsRes => {
822               commentsRes.comments
823                 .filter(c => c.creator.id == data.person_view.person.id)
824                 .forEach(c => (c.creator.banned = data.banned));
825               if (postRes.post_view.creator.id == data.person_view.person.id) {
826                 postRes.post_view.creator.banned = data.banned;
827               }
828               this.setState(this.state);
829             },
830             none: void 0,
831           }),
832         none: void 0,
833       });
834     } else if (op == UserOperation.AddAdmin) {
835       let data = wsJsonToRes<AddAdminResponse>(msg, AddAdminResponse);
836       this.setState(s => ((s.siteRes.admins = data.admins), s));
837     } else if (op == UserOperation.Search) {
838       let data = wsJsonToRes<SearchResponse>(msg, SearchResponse);
839       let xPosts = data.posts.filter(
840         p => p.post.id != Number(this.props.match.params.id)
841       );
842       this.setState({ crossPosts: xPosts.length > 0 ? Some(xPosts) : None });
843     } else if (op == UserOperation.LeaveAdmin) {
844       let data = wsJsonToRes<GetSiteResponse>(msg, GetSiteResponse);
845       this.setState({ siteRes: data });
846     } else if (op == UserOperation.TransferCommunity) {
847       let data = wsJsonToRes<GetCommunityResponse>(msg, GetCommunityResponse);
848       this.state.postRes.match({
849         some: res => {
850           res.community_view = data.community_view;
851           res.post_view.community = data.community_view.community;
852           res.moderators = data.moderators;
853           this.setState(this.state);
854         },
855         none: void 0,
856       });
857     } else if (op == UserOperation.BlockPerson) {
858       let data = wsJsonToRes<BlockPersonResponse>(msg, BlockPersonResponse);
859       updatePersonBlock(data);
860     } else if (op == UserOperation.CreatePostReport) {
861       let data = wsJsonToRes<PostReportResponse>(msg, PostReportResponse);
862       if (data) {
863         toast(i18n.t("report_created"));
864       }
865     } else if (op == UserOperation.CreateCommentReport) {
866       let data = wsJsonToRes<CommentReportResponse>(msg, CommentReportResponse);
867       if (data) {
868         toast(i18n.t("report_created"));
869       }
870     } else if (
871       op == UserOperation.PurgePerson ||
872       op == UserOperation.PurgePost ||
873       op == UserOperation.PurgeComment ||
874       op == UserOperation.PurgeCommunity
875     ) {
876       let data = wsJsonToRes<PurgeItemResponse>(msg, PurgeItemResponse);
877       if (data.success) {
878         toast(i18n.t("purge_success"));
879         this.context.router.history.push(`/`);
880       }
881     }
882   }
883 }