]> Untitled Git - lemmy-ui.git/blob - src/shared/components/post/post.tsx
Adding new site setup fields. (#840)
[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         `${res.post_view.post.name} - ${this.state.siteRes.site_view.site.name}`,
321       none: "",
322     });
323   }
324
325   get imageTag(): Option<string> {
326     return this.state.postRes.match({
327       some: res =>
328         res.post_view.post.thumbnail_url.or(
329           res.post_view.post.url.match({
330             some: url => (isImage(url) ? Some(url) : None),
331             none: None,
332           })
333         ),
334       none: None,
335     });
336   }
337
338   get descriptionTag(): Option<string> {
339     return this.state.postRes.andThen(r => r.post_view.post.body);
340   }
341
342   render() {
343     return (
344       <div className="container-lg">
345         {this.state.loading ? (
346           <h5>
347             <Spinner large />
348           </h5>
349         ) : (
350           this.state.postRes.match({
351             some: res => (
352               <div className="row">
353                 <div className="col-12 col-md-8 mb-3">
354                   <HtmlTags
355                     title={this.documentTitle}
356                     path={this.context.router.route.match.url}
357                     image={this.imageTag}
358                     description={this.descriptionTag}
359                   />
360                   <PostListing
361                     post_view={res.post_view}
362                     duplicates={this.state.crossPosts}
363                     showBody
364                     showCommunity
365                     moderators={Some(res.moderators)}
366                     admins={Some(this.state.siteRes.admins)}
367                     enableDownvotes={enableDownvotes(this.state.siteRes)}
368                     enableNsfw={enableNsfw(this.state.siteRes)}
369                     allLanguages={this.state.siteRes.all_languages}
370                   />
371                   <div ref={this.state.commentSectionRef} className="mb-2" />
372                   <CommentForm
373                     node={Right(res.post_view.post.id)}
374                     disabled={res.post_view.post.locked}
375                     allLanguages={this.state.siteRes.all_languages}
376                   />
377                   <div className="d-block d-md-none">
378                     <button
379                       className="btn btn-secondary d-inline-block mb-2 mr-3"
380                       onClick={linkEvent(this, this.handleShowSidebarMobile)}
381                     >
382                       {i18n.t("sidebar")}{" "}
383                       <Icon
384                         icon={
385                           this.state.showSidebarMobile
386                             ? `minus-square`
387                             : `plus-square`
388                         }
389                         classes="icon-inline"
390                       />
391                     </button>
392                     {this.state.showSidebarMobile && this.sidebar()}
393                   </div>
394                   {this.sortRadios()}
395                   {this.state.commentViewType == CommentViewType.Tree &&
396                     this.commentsTree()}
397                   {this.state.commentViewType == CommentViewType.Flat &&
398                     this.commentsFlat()}
399                 </div>
400                 <div className="d-none d-md-block col-md-4">
401                   {this.sidebar()}
402                 </div>
403               </div>
404             ),
405             none: <></>,
406           })
407         )}
408       </div>
409     );
410   }
411
412   sortRadios() {
413     return (
414       <>
415         <div className="btn-group btn-group-toggle flex-wrap mr-3 mb-2">
416           <label
417             className={`btn btn-outline-secondary pointer ${
418               CommentSortType[this.state.commentSort] === CommentSortType.Hot &&
419               "active"
420             }`}
421           >
422             {i18n.t("hot")}
423             <input
424               type="radio"
425               value={CommentSortType.Hot}
426               checked={this.state.commentSort === CommentSortType.Hot}
427               onChange={linkEvent(this, this.handleCommentSortChange)}
428             />
429           </label>
430           <label
431             className={`btn btn-outline-secondary pointer ${
432               CommentSortType[this.state.commentSort] === CommentSortType.Top &&
433               "active"
434             }`}
435           >
436             {i18n.t("top")}
437             <input
438               type="radio"
439               value={CommentSortType.Top}
440               checked={this.state.commentSort === CommentSortType.Top}
441               onChange={linkEvent(this, this.handleCommentSortChange)}
442             />
443           </label>
444           <label
445             className={`btn btn-outline-secondary pointer ${
446               CommentSortType[this.state.commentSort] === CommentSortType.New &&
447               "active"
448             }`}
449           >
450             {i18n.t("new")}
451             <input
452               type="radio"
453               value={CommentSortType.New}
454               checked={this.state.commentSort === CommentSortType.New}
455               onChange={linkEvent(this, this.handleCommentSortChange)}
456             />
457           </label>
458           <label
459             className={`btn btn-outline-secondary pointer ${
460               CommentSortType[this.state.commentSort] === CommentSortType.Old &&
461               "active"
462             }`}
463           >
464             {i18n.t("old")}
465             <input
466               type="radio"
467               value={CommentSortType.Old}
468               checked={this.state.commentSort === CommentSortType.Old}
469               onChange={linkEvent(this, this.handleCommentSortChange)}
470             />
471           </label>
472         </div>
473         <div className="btn-group btn-group-toggle flex-wrap mb-2">
474           <label
475             className={`btn btn-outline-secondary pointer ${
476               this.state.commentViewType === CommentViewType.Flat && "active"
477             }`}
478           >
479             {i18n.t("chat")}
480             <input
481               type="radio"
482               value={CommentViewType.Flat}
483               checked={this.state.commentViewType === CommentViewType.Flat}
484               onChange={linkEvent(this, this.handleCommentViewTypeChange)}
485             />
486           </label>
487         </div>
488       </>
489     );
490   }
491
492   commentsFlat() {
493     // These are already sorted by new
494     return this.state.commentsRes.match({
495       some: commentsRes =>
496         this.state.postRes.match({
497           some: postRes => (
498             <div>
499               <CommentNodes
500                 nodes={commentsToFlatNodes(commentsRes.comments)}
501                 viewType={this.state.commentViewType}
502                 maxCommentsShown={Some(this.state.maxCommentsShown)}
503                 noIndent
504                 locked={postRes.post_view.post.locked}
505                 moderators={Some(postRes.moderators)}
506                 admins={Some(this.state.siteRes.admins)}
507                 enableDownvotes={enableDownvotes(this.state.siteRes)}
508                 showContext
509                 allLanguages={this.state.siteRes.all_languages}
510               />
511             </div>
512           ),
513           none: <></>,
514         }),
515       none: <></>,
516     });
517   }
518
519   sidebar() {
520     return this.state.postRes.match({
521       some: res => (
522         <div className="mb-3">
523           <Sidebar
524             community_view={res.community_view}
525             moderators={res.moderators}
526             admins={this.state.siteRes.admins}
527             online={res.online}
528             enableNsfw={enableNsfw(this.state.siteRes)}
529             showIcon
530           />
531         </div>
532       ),
533       none: <></>,
534     });
535   }
536
537   handleCommentSortChange(i: Post, event: any) {
538     i.setState({
539       commentSort: CommentSortType[event.target.value],
540       commentViewType: CommentViewType.Tree,
541     });
542     i.fetchPost();
543   }
544
545   handleCommentViewTypeChange(i: Post, event: any) {
546     i.setState({
547       commentViewType: Number(event.target.value),
548       commentSort: CommentSortType.New,
549       commentTree: buildCommentsTree(
550         i.state.commentsRes.map(r => r.comments).unwrapOr([]),
551         i.state.commentId.isSome()
552       ),
553     });
554   }
555
556   handleShowSidebarMobile(i: Post) {
557     i.setState({ showSidebarMobile: !i.state.showSidebarMobile });
558   }
559
560   handleViewPost(i: Post) {
561     i.state.postRes.match({
562       some: res =>
563         i.context.router.history.push(`/post/${res.post_view.post.id}`),
564       none: void 0,
565     });
566   }
567
568   handleViewContext(i: Post) {
569     i.state.commentsRes.match({
570       some: res =>
571         i.context.router.history.push(
572           `/comment/${getCommentParentId(res.comments[0].comment).unwrap()}`
573         ),
574       none: void 0,
575     });
576   }
577
578   commentsTree() {
579     let showContextButton = toOption(this.state.commentTree[0]).match({
580       some: comment => getDepthFromComment(comment.comment_view.comment) > 0,
581       none: false,
582     });
583
584     return this.state.postRes.match({
585       some: res => (
586         <div>
587           {this.state.commentId.isSome() && (
588             <>
589               <button
590                 className="pl-0 d-block btn btn-link text-muted"
591                 onClick={linkEvent(this, this.handleViewPost)}
592               >
593                 {i18n.t("view_all_comments")} âž”
594               </button>
595               {showContextButton && (
596                 <button
597                   className="pl-0 d-block btn btn-link text-muted"
598                   onClick={linkEvent(this, this.handleViewContext)}
599                 >
600                   {i18n.t("show_context")} âž”
601                 </button>
602               )}
603             </>
604           )}
605           <CommentNodes
606             nodes={this.state.commentTree}
607             viewType={this.state.commentViewType}
608             maxCommentsShown={Some(this.state.maxCommentsShown)}
609             locked={res.post_view.post.locked}
610             moderators={Some(res.moderators)}
611             admins={Some(this.state.siteRes.admins)}
612             enableDownvotes={enableDownvotes(this.state.siteRes)}
613             allLanguages={this.state.siteRes.all_languages}
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 }