]> Untitled Git - lemmy-ui.git/blob - src/shared/components/home/home.tsx
Use my fork of inferno-i18next. Fixes #413 (#415)
[lemmy-ui.git] / src / shared / components / home / home.tsx
1 import { Component, linkEvent } from "inferno";
2 import { T } from "inferno-i18next-dess";
3 import { Link } from "inferno-router";
4 import {
5   AddAdminResponse,
6   BanPersonResponse,
7   BlockPersonResponse,
8   CommentResponse,
9   CommentView,
10   CommunityView,
11   GetComments,
12   GetCommentsResponse,
13   GetPosts,
14   GetPostsResponse,
15   GetSiteResponse,
16   ListCommunities,
17   ListCommunitiesResponse,
18   ListingType,
19   PostResponse,
20   PostView,
21   SiteResponse,
22   SortType,
23   UserOperation,
24 } from "lemmy-js-client";
25 import { Subscription } from "rxjs";
26 import { i18n } from "../../i18next";
27 import { DataType, InitialFetchRequest } from "../../interfaces";
28 import { UserService, WebSocketService } from "../../services";
29 import {
30   authField,
31   commentsToFlatNodes,
32   createCommentLikeRes,
33   createPostLikeFindRes,
34   editCommentRes,
35   editPostFindRes,
36   fetchLimit,
37   getDataTypeFromProps,
38   getListingTypeFromProps,
39   getPageFromProps,
40   getSortTypeFromProps,
41   mdToHtml,
42   notifyPost,
43   restoreScrollPosition,
44   saveCommentRes,
45   saveScrollPosition,
46   setIsoData,
47   setOptionalAuth,
48   setupTippy,
49   showLocal,
50   toast,
51   updatePersonBlock,
52   wsClient,
53   wsJsonToRes,
54   wsSubscribe,
55   wsUserOp,
56 } from "../../utils";
57 import { CommentNodes } from "../comment/comment-nodes";
58 import { BannerIconHeader } from "../common/banner-icon-header";
59 import { DataTypeSelect } from "../common/data-type-select";
60 import { HtmlTags } from "../common/html-tags";
61 import { Icon, Spinner } from "../common/icon";
62 import { ListingTypeSelect } from "../common/listing-type-select";
63 import { Paginator } from "../common/paginator";
64 import { SortSelect } from "../common/sort-select";
65 import { CommunityLink } from "../community/community-link";
66 import { PersonListing } from "../person/person-listing";
67 import { PostListings } from "../post/post-listings";
68 import { SiteForm } from "./site-form";
69
70 interface HomeState {
71   trendingCommunities: CommunityView[];
72   siteRes: GetSiteResponse;
73   showEditSite: boolean;
74   showSubscribedMobile: boolean;
75   showTrendingMobile: boolean;
76   showSidebarMobile: boolean;
77   loading: boolean;
78   posts: PostView[];
79   comments: CommentView[];
80   listingType: ListingType;
81   dataType: DataType;
82   sort: SortType;
83   page: number;
84 }
85
86 interface HomeProps {
87   listingType: ListingType;
88   dataType: DataType;
89   sort: SortType;
90   page: number;
91 }
92
93 interface UrlParams {
94   listingType?: ListingType;
95   dataType?: string;
96   sort?: SortType;
97   page?: number;
98 }
99
100 export class Home extends Component<any, HomeState> {
101   private isoData = setIsoData(this.context);
102   private subscription: Subscription;
103   private emptyState: HomeState = {
104     trendingCommunities: [],
105     siteRes: this.isoData.site_res,
106     showEditSite: false,
107     showSubscribedMobile: false,
108     showTrendingMobile: false,
109     showSidebarMobile: false,
110     loading: true,
111     posts: [],
112     comments: [],
113     listingType: getListingTypeFromProps(this.props),
114     dataType: getDataTypeFromProps(this.props),
115     sort: getSortTypeFromProps(this.props),
116     page: getPageFromProps(this.props),
117   };
118
119   constructor(props: any, context: any) {
120     super(props, context);
121
122     this.state = this.emptyState;
123     this.handleEditCancel = this.handleEditCancel.bind(this);
124     this.handleSortChange = this.handleSortChange.bind(this);
125     this.handleListingTypeChange = this.handleListingTypeChange.bind(this);
126     this.handleDataTypeChange = this.handleDataTypeChange.bind(this);
127     this.handlePageChange = this.handlePageChange.bind(this);
128
129     this.parseMessage = this.parseMessage.bind(this);
130     this.subscription = wsSubscribe(this.parseMessage);
131
132     // Only fetch the data if coming from another route
133     if (this.isoData.path == this.context.router.route.match.url) {
134       if (this.state.dataType == DataType.Post) {
135         this.state.posts = this.isoData.routeData[0].posts;
136       } else {
137         this.state.comments = this.isoData.routeData[0].comments;
138       }
139       this.state.trendingCommunities = this.isoData.routeData[1].communities;
140       this.state.loading = false;
141     } else {
142       this.fetchTrendingCommunities();
143       this.fetchData();
144     }
145
146     setupTippy();
147   }
148
149   fetchTrendingCommunities() {
150     let listCommunitiesForm: ListCommunities = {
151       type_: ListingType.Local,
152       sort: SortType.Hot,
153       limit: 6,
154       auth: authField(false),
155     };
156     WebSocketService.Instance.send(
157       wsClient.listCommunities(listCommunitiesForm)
158     );
159   }
160
161   componentDidMount() {
162     // This means it hasn't been set up yet
163     if (!this.state.siteRes.site_view) {
164       this.context.router.history.push("/setup");
165     }
166
167     WebSocketService.Instance.send(wsClient.communityJoin({ community_id: 0 }));
168   }
169
170   componentWillUnmount() {
171     saveScrollPosition(this.context);
172     this.subscription.unsubscribe();
173     window.isoData.path = undefined;
174   }
175
176   static getDerivedStateFromProps(props: any): HomeProps {
177     return {
178       listingType: getListingTypeFromProps(props),
179       dataType: getDataTypeFromProps(props),
180       sort: getSortTypeFromProps(props),
181       page: getPageFromProps(props),
182     };
183   }
184
185   static fetchInitialData(req: InitialFetchRequest): Promise<any>[] {
186     let pathSplit = req.path.split("/");
187     let dataType: DataType = pathSplit[3]
188       ? DataType[pathSplit[3]]
189       : DataType.Post;
190
191     // TODO figure out auth default_listingType, default_sort_type
192     let type_: ListingType = pathSplit[5]
193       ? ListingType[pathSplit[5]]
194       : UserService.Instance.myUserInfo
195       ? Object.values(ListingType)[
196           UserService.Instance.myUserInfo.local_user_view.local_user
197             .default_listing_type
198         ]
199       : ListingType.Local;
200     let sort: SortType = pathSplit[7]
201       ? SortType[pathSplit[7]]
202       : UserService.Instance.myUserInfo
203       ? Object.values(SortType)[
204           UserService.Instance.myUserInfo.local_user_view.local_user
205             .default_sort_type
206         ]
207       : SortType.Active;
208
209     let page = pathSplit[9] ? Number(pathSplit[9]) : 1;
210
211     let promises: Promise<any>[] = [];
212
213     if (dataType == DataType.Post) {
214       let getPostsForm: GetPosts = {
215         page,
216         limit: fetchLimit,
217         sort,
218         type_,
219         saved_only: false,
220       };
221       setOptionalAuth(getPostsForm, req.auth);
222       promises.push(req.client.getPosts(getPostsForm));
223     } else {
224       let getCommentsForm: GetComments = {
225         page,
226         limit: fetchLimit,
227         sort,
228         type_,
229         saved_only: false,
230       };
231       setOptionalAuth(getCommentsForm, req.auth);
232       promises.push(req.client.getComments(getCommentsForm));
233     }
234
235     let trendingCommunitiesForm: ListCommunities = {
236       type_: ListingType.Local,
237       sort: SortType.Hot,
238       limit: 6,
239     };
240     promises.push(req.client.listCommunities(trendingCommunitiesForm));
241
242     return promises;
243   }
244
245   componentDidUpdate(_: any, lastState: HomeState) {
246     if (
247       lastState.listingType !== this.state.listingType ||
248       lastState.dataType !== this.state.dataType ||
249       lastState.sort !== this.state.sort ||
250       lastState.page !== this.state.page
251     ) {
252       this.setState({ loading: true });
253       this.fetchData();
254     }
255   }
256
257   get documentTitle(): string {
258     return `${
259       this.state.siteRes.site_view
260         ? this.state.siteRes.site_view.site.description
261           ? `${this.state.siteRes.site_view.site.name} - ${this.state.siteRes.site_view.site.description}`
262           : this.state.siteRes.site_view.site.name
263         : "Lemmy"
264     }`;
265   }
266
267   render() {
268     return (
269       <div class="container">
270         <HtmlTags
271           title={this.documentTitle}
272           path={this.context.router.route.match.url}
273         />
274         {this.state.siteRes.site_view?.site && (
275           <div class="row">
276             <main role="main" class="col-12 col-md-8">
277               <div class="d-block d-md-none">{this.mobileView()}</div>
278               {this.posts()}
279             </main>
280             <aside class="d-none d-md-block col-md-4">{this.mySidebar()}</aside>
281           </div>
282         )}
283       </div>
284     );
285   }
286
287   mobileView() {
288     return (
289       <div class="row">
290         <div class="col-12">
291           {UserService.Instance.myUserInfo &&
292             UserService.Instance.myUserInfo.follows.length > 0 && (
293               <button
294                 class="btn btn-secondary d-inline-block mb-2 mr-3"
295                 onClick={linkEvent(this, this.handleShowSubscribedMobile)}
296               >
297                 {i18n.t("subscribed")}{" "}
298                 <Icon
299                   icon={
300                     this.state.showSubscribedMobile
301                       ? `minus-square`
302                       : `plus-square`
303                   }
304                   classes="icon-inline"
305                 />
306               </button>
307             )}
308           <button
309             class="btn btn-secondary d-inline-block mb-2 mr-3"
310             onClick={linkEvent(this, this.handleShowTrendingMobile)}
311           >
312             {i18n.t("trending")}{" "}
313             <Icon
314               icon={
315                 this.state.showTrendingMobile ? `minus-square` : `plus-square`
316               }
317               classes="icon-inline"
318             />
319           </button>
320           <button
321             class="btn btn-secondary d-inline-block mb-2 mr-3"
322             onClick={linkEvent(this, this.handleShowSidebarMobile)}
323           >
324             {i18n.t("sidebar")}{" "}
325             <Icon
326               icon={
327                 this.state.showSidebarMobile ? `minus-square` : `plus-square`
328               }
329               classes="icon-inline"
330             />
331           </button>
332           {this.state.showSubscribedMobile && (
333             <div class="col-12 card border-secondary mb-3">
334               <div class="card-body">{this.subscribedCommunities()}</div>
335             </div>
336           )}
337           {this.state.showTrendingMobile && (
338             <div class="col-12 card border-secondary mb-3">
339               <div class="card-body">{this.trendingCommunities()}</div>
340             </div>
341           )}
342           {this.state.showSidebarMobile && (
343             <div class="col-12 card border-secondary mb-3">
344               <div class="card-body">{this.sidebar()}</div>
345             </div>
346           )}
347         </div>
348       </div>
349     );
350   }
351
352   mySidebar() {
353     return (
354       <div>
355         {!this.state.loading && (
356           <div>
357             <div class="card border-secondary mb-3">
358               <div class="card-body">
359                 {this.trendingCommunities()}
360                 {this.createCommunityButton()}
361                 {this.exploreCommunitiesButton()}
362               </div>
363             </div>
364
365             {UserService.Instance.myUserInfo &&
366               UserService.Instance.myUserInfo.follows.length > 0 && (
367                 <div class="card border-secondary mb-3">
368                   <div class="card-body">{this.subscribedCommunities()}</div>
369                 </div>
370               )}
371
372             <div class="card border-secondary mb-3">
373               <div class="card-body">{this.sidebar()}</div>
374             </div>
375           </div>
376         )}
377       </div>
378     );
379   }
380
381   createCommunityButton() {
382     return (
383       <Link className="mt-2 btn btn-secondary btn-block" to="/create_community">
384         {i18n.t("create_a_community")}
385       </Link>
386     );
387   }
388
389   exploreCommunitiesButton() {
390     return (
391       <Link className="btn btn-secondary btn-block" to="/communities">
392         {i18n.t("explore_communities")}
393       </Link>
394     );
395   }
396
397   trendingCommunities() {
398     return (
399       <div>
400         <h5>
401           <T i18nKey="trending_communities">
402             #
403             <Link className="text-body" to="/communities">
404               #
405             </Link>
406           </T>
407         </h5>
408         <ul class="list-inline mb-0">
409           {this.state.trendingCommunities.map(cv => (
410             <li class="list-inline-item d-inline-block">
411               <CommunityLink community={cv.community} />
412             </li>
413           ))}
414         </ul>
415       </div>
416     );
417   }
418
419   subscribedCommunities() {
420     return (
421       <div>
422         <h5>
423           <T i18nKey="subscribed_to_communities">
424             #
425             <Link className="text-body" to="/communities">
426               #
427             </Link>
428           </T>
429         </h5>
430         <ul class="list-inline mb-0">
431           {UserService.Instance.myUserInfo.follows.map(cfv => (
432             <li class="list-inline-item d-inline-block">
433               <CommunityLink community={cfv.community} />
434             </li>
435           ))}
436         </ul>
437       </div>
438     );
439   }
440
441   sidebar() {
442     let site = this.state.siteRes.site_view.site;
443     return (
444       <div>
445         {!this.state.showEditSite ? (
446           <div>
447             <div class="mb-2">
448               {this.siteName()}
449               {this.adminButtons()}
450             </div>
451             <BannerIconHeader banner={site.banner} />
452             {this.siteInfo()}
453           </div>
454         ) : (
455           <SiteForm site={site} onCancel={this.handleEditCancel} />
456         )}
457       </div>
458     );
459   }
460
461   updateUrl(paramUpdates: UrlParams) {
462     const listingTypeStr = paramUpdates.listingType || this.state.listingType;
463     const dataTypeStr = paramUpdates.dataType || DataType[this.state.dataType];
464     const sortStr = paramUpdates.sort || this.state.sort;
465     const page = paramUpdates.page || this.state.page;
466     this.props.history.push(
467       `/home/data_type/${dataTypeStr}/listing_type/${listingTypeStr}/sort/${sortStr}/page/${page}`
468     );
469   }
470
471   siteInfo() {
472     let site = this.state.siteRes.site_view.site;
473     return (
474       <div>
475         {site.description && <h6>{site.description}</h6>}
476         {site.sidebar && this.siteSidebar()}
477         {this.badges()}
478         {this.admins()}
479       </div>
480     );
481   }
482
483   siteName() {
484     let site = this.state.siteRes.site_view.site;
485     return site.name && <h5 class="mb-0">{site.name}</h5>;
486   }
487
488   admins() {
489     return (
490       <ul class="mt-1 list-inline small mb-0">
491         <li class="list-inline-item">{i18n.t("admins")}:</li>
492         {this.state.siteRes.admins.map(av => (
493           <li class="list-inline-item">
494             <PersonListing person={av.person} />
495           </li>
496         ))}
497       </ul>
498     );
499   }
500
501   badges() {
502     let counts = this.state.siteRes.site_view.counts;
503     return (
504       <ul class="my-2 list-inline">
505         <li className="list-inline-item badge badge-secondary">
506           {i18n.t("number_online", { count: this.state.siteRes.online })}
507         </li>
508         <li
509           className="list-inline-item badge badge-secondary pointer"
510           data-tippy-content={`${i18n.t("number_of_users", {
511             count: counts.users_active_day,
512           })} ${i18n.t("active_in_the_last")} ${i18n.t("day")}`}
513         >
514           {i18n.t("number_of_users", {
515             count: counts.users_active_day,
516           })}{" "}
517           / {i18n.t("day")}
518         </li>
519         <li
520           className="list-inline-item badge badge-secondary pointer"
521           data-tippy-content={`${i18n.t("number_of_users", {
522             count: counts.users_active_week,
523           })} ${i18n.t("active_in_the_last")} ${i18n.t("week")}`}
524         >
525           {i18n.t("number_of_users", {
526             count: counts.users_active_week,
527           })}{" "}
528           / {i18n.t("week")}
529         </li>
530         <li
531           className="list-inline-item badge badge-secondary pointer"
532           data-tippy-content={`${i18n.t("number_of_users", {
533             count: counts.users_active_month,
534           })} ${i18n.t("active_in_the_last")} ${i18n.t("month")}`}
535         >
536           {i18n.t("number_of_users", {
537             count: counts.users_active_month,
538           })}{" "}
539           / {i18n.t("month")}
540         </li>
541         <li
542           className="list-inline-item badge badge-secondary pointer"
543           data-tippy-content={`${i18n.t("number_of_users", {
544             count: counts.users_active_half_year,
545           })} ${i18n.t("active_in_the_last")} ${i18n.t("number_of_months", {
546             count: 6,
547           })}`}
548         >
549           {i18n.t("number_of_users", {
550             count: counts.users_active_half_year,
551           })}{" "}
552           / {i18n.t("number_of_months", { count: 6 })}
553         </li>
554         <li className="list-inline-item badge badge-secondary">
555           {i18n.t("number_of_users", {
556             count: counts.users,
557           })}
558         </li>
559         <li className="list-inline-item badge badge-secondary">
560           {i18n.t("number_of_communities", {
561             count: counts.communities,
562           })}
563         </li>
564         <li className="list-inline-item badge badge-secondary">
565           {i18n.t("number_of_posts", {
566             count: counts.posts,
567           })}
568         </li>
569         <li className="list-inline-item badge badge-secondary">
570           {i18n.t("number_of_comments", {
571             count: counts.comments,
572           })}
573         </li>
574         <li className="list-inline-item">
575           <Link className="badge badge-secondary" to="/modlog">
576             {i18n.t("modlog")}
577           </Link>
578         </li>
579       </ul>
580     );
581   }
582
583   adminButtons() {
584     return (
585       this.canAdmin && (
586         <ul class="list-inline mb-1 text-muted font-weight-bold">
587           <li className="list-inline-item-action">
588             <button
589               class="btn btn-link d-inline-block text-muted"
590               onClick={linkEvent(this, this.handleEditClick)}
591               aria-label={i18n.t("edit")}
592               data-tippy-content={i18n.t("edit")}
593             >
594               <Icon icon="edit" classes="icon-inline" />
595             </button>
596           </li>
597         </ul>
598       )
599     );
600   }
601
602   siteSidebar() {
603     return (
604       <div
605         className="md-div"
606         dangerouslySetInnerHTML={mdToHtml(
607           this.state.siteRes.site_view.site.sidebar
608         )}
609       />
610     );
611   }
612
613   posts() {
614     return (
615       <div class="main-content-wrapper">
616         {this.state.loading ? (
617           <h5>
618             <Spinner large />
619           </h5>
620         ) : (
621           <div>
622             {this.selects()}
623             {this.listings()}
624             <Paginator
625               page={this.state.page}
626               onChange={this.handlePageChange}
627             />
628           </div>
629         )}
630       </div>
631     );
632   }
633
634   listings() {
635     let site = this.state.siteRes.site_view.site;
636     return this.state.dataType == DataType.Post ? (
637       <PostListings
638         posts={this.state.posts}
639         showCommunity
640         removeDuplicates
641         enableDownvotes={site.enable_downvotes}
642         enableNsfw={site.enable_nsfw}
643       />
644     ) : (
645       <CommentNodes
646         nodes={commentsToFlatNodes(this.state.comments)}
647         noIndent
648         showCommunity
649         showContext
650         enableDownvotes={site.enable_downvotes}
651       />
652     );
653   }
654
655   selects() {
656     return (
657       <div className="mb-3">
658         <span class="mr-3">
659           <DataTypeSelect
660             type_={this.state.dataType}
661             onChange={this.handleDataTypeChange}
662           />
663         </span>
664         <span class="mr-3">
665           <ListingTypeSelect
666             type_={this.state.listingType}
667             showLocal={showLocal(this.isoData)}
668             onChange={this.handleListingTypeChange}
669           />
670         </span>
671         <span class="mr-2">
672           <SortSelect sort={this.state.sort} onChange={this.handleSortChange} />
673         </span>
674         {this.state.listingType == ListingType.All && (
675           <a
676             href={`/feeds/all.xml?sort=${this.state.sort}`}
677             rel="noopener"
678             title="RSS"
679           >
680             <Icon icon="rss" classes="text-muted small" />
681           </a>
682         )}
683         {this.state.listingType == ListingType.Local && (
684           <a
685             href={`/feeds/local.xml?sort=${this.state.sort}`}
686             rel="noopener"
687             title="RSS"
688           >
689             <Icon icon="rss" classes="text-muted small" />
690           </a>
691         )}
692         {UserService.Instance.myUserInfo &&
693           this.state.listingType == ListingType.Subscribed && (
694             <a
695               href={`/feeds/front/${UserService.Instance.auth}.xml?sort=${this.state.sort}`}
696               title="RSS"
697               rel="noopener"
698             >
699               <Icon icon="rss" classes="text-muted small" />
700             </a>
701           )}
702       </div>
703     );
704   }
705
706   get canAdmin(): boolean {
707     return (
708       UserService.Instance.myUserInfo &&
709       this.state.siteRes.admins
710         .map(a => a.person.id)
711         .includes(UserService.Instance.myUserInfo.local_user_view.person.id)
712     );
713   }
714
715   handleEditClick(i: Home) {
716     i.state.showEditSite = true;
717     i.setState(i.state);
718   }
719
720   handleEditCancel() {
721     this.state.showEditSite = false;
722     this.setState(this.state);
723   }
724
725   handleShowSubscribedMobile(i: Home) {
726     i.state.showSubscribedMobile = !i.state.showSubscribedMobile;
727     i.setState(i.state);
728   }
729
730   handleShowTrendingMobile(i: Home) {
731     i.state.showTrendingMobile = !i.state.showTrendingMobile;
732     i.setState(i.state);
733   }
734
735   handleShowSidebarMobile(i: Home) {
736     i.state.showSidebarMobile = !i.state.showSidebarMobile;
737     i.setState(i.state);
738   }
739
740   handlePageChange(page: number) {
741     this.updateUrl({ page });
742     window.scrollTo(0, 0);
743   }
744
745   handleSortChange(val: SortType) {
746     this.updateUrl({ sort: val, page: 1 });
747     window.scrollTo(0, 0);
748   }
749
750   handleListingTypeChange(val: ListingType) {
751     this.updateUrl({ listingType: val, page: 1 });
752     window.scrollTo(0, 0);
753   }
754
755   handleDataTypeChange(val: DataType) {
756     this.updateUrl({ dataType: DataType[val], page: 1 });
757     window.scrollTo(0, 0);
758   }
759
760   fetchData() {
761     if (this.state.dataType == DataType.Post) {
762       let getPostsForm: GetPosts = {
763         page: this.state.page,
764         limit: fetchLimit,
765         sort: this.state.sort,
766         type_: this.state.listingType,
767         saved_only: false,
768         auth: authField(false),
769       };
770       WebSocketService.Instance.send(wsClient.getPosts(getPostsForm));
771     } else {
772       let getCommentsForm: GetComments = {
773         page: this.state.page,
774         limit: fetchLimit,
775         sort: this.state.sort,
776         type_: this.state.listingType,
777         saved_only: false,
778         auth: authField(false),
779       };
780       WebSocketService.Instance.send(wsClient.getComments(getCommentsForm));
781     }
782   }
783
784   parseMessage(msg: any) {
785     let op = wsUserOp(msg);
786     console.log(msg);
787     if (msg.error) {
788       toast(i18n.t(msg.error), "danger");
789       return;
790     } else if (msg.reconnect) {
791       WebSocketService.Instance.send(
792         wsClient.communityJoin({ community_id: 0 })
793       );
794       this.fetchData();
795     } else if (op == UserOperation.ListCommunities) {
796       let data = wsJsonToRes<ListCommunitiesResponse>(msg).data;
797       this.state.trendingCommunities = data.communities;
798       this.setState(this.state);
799     } else if (op == UserOperation.EditSite) {
800       let data = wsJsonToRes<SiteResponse>(msg).data;
801       this.state.siteRes.site_view = data.site_view;
802       this.state.showEditSite = false;
803       this.setState(this.state);
804       toast(i18n.t("site_saved"));
805     } else if (op == UserOperation.GetPosts) {
806       let data = wsJsonToRes<GetPostsResponse>(msg).data;
807       this.state.posts = data.posts;
808       this.state.loading = false;
809       this.setState(this.state);
810       restoreScrollPosition(this.context);
811       setupTippy();
812     } else if (op == UserOperation.CreatePost) {
813       let data = wsJsonToRes<PostResponse>(msg).data;
814
815       // NSFW check
816       let nsfw = data.post_view.post.nsfw || data.post_view.community.nsfw;
817       let nsfwCheck =
818         !nsfw ||
819         (nsfw &&
820           UserService.Instance.myUserInfo &&
821           UserService.Instance.myUserInfo.local_user_view.local_user.show_nsfw);
822
823       // Only push these if you're on the first page, and you pass the nsfw check
824       if (this.state.page == 1 && nsfwCheck) {
825         // If you're on subscribed, only push it if you're subscribed.
826         if (this.state.listingType == ListingType.Subscribed) {
827           if (
828             UserService.Instance.myUserInfo.follows
829               .map(c => c.community.id)
830               .includes(data.post_view.community.id)
831           ) {
832             this.state.posts.unshift(data.post_view);
833             if (
834               UserService.Instance.myUserInfo?.local_user_view.local_user
835                 .show_new_post_notifs
836             ) {
837               notifyPost(data.post_view, this.context.router);
838             }
839           }
840         } else if (this.state.listingType == ListingType.Local) {
841           // If you're on the local view, only push it if its local
842           if (data.post_view.post.local) {
843             this.state.posts.unshift(data.post_view);
844             if (
845               UserService.Instance.myUserInfo?.local_user_view.local_user
846                 .show_new_post_notifs
847             ) {
848               notifyPost(data.post_view, this.context.router);
849             }
850           }
851         } else {
852           this.state.posts.unshift(data.post_view);
853           if (
854             UserService.Instance.myUserInfo?.local_user_view.local_user
855               .show_new_post_notifs
856           ) {
857             notifyPost(data.post_view, this.context.router);
858           }
859         }
860         this.setState(this.state);
861       }
862     } else if (
863       op == UserOperation.EditPost ||
864       op == UserOperation.DeletePost ||
865       op == UserOperation.RemovePost ||
866       op == UserOperation.LockPost ||
867       op == UserOperation.StickyPost ||
868       op == UserOperation.SavePost
869     ) {
870       let data = wsJsonToRes<PostResponse>(msg).data;
871       editPostFindRes(data.post_view, this.state.posts);
872       this.setState(this.state);
873     } else if (op == UserOperation.CreatePostLike) {
874       let data = wsJsonToRes<PostResponse>(msg).data;
875       createPostLikeFindRes(data.post_view, this.state.posts);
876       this.setState(this.state);
877     } else if (op == UserOperation.AddAdmin) {
878       let data = wsJsonToRes<AddAdminResponse>(msg).data;
879       this.state.siteRes.admins = data.admins;
880       this.setState(this.state);
881     } else if (op == UserOperation.BanPerson) {
882       let data = wsJsonToRes<BanPersonResponse>(msg).data;
883       let found = this.state.siteRes.banned.find(
884         p => (p.person.id = data.person_view.person.id)
885       );
886
887       // Remove the banned if its found in the list, and the action is an unban
888       if (found && !data.banned) {
889         this.state.siteRes.banned = this.state.siteRes.banned.filter(
890           i => i.person.id !== data.person_view.person.id
891         );
892       } else {
893         this.state.siteRes.banned.push(data.person_view);
894       }
895
896       this.state.posts
897         .filter(p => p.creator.id == data.person_view.person.id)
898         .forEach(p => (p.creator.banned = data.banned));
899
900       this.setState(this.state);
901     } else if (op == UserOperation.GetComments) {
902       let data = wsJsonToRes<GetCommentsResponse>(msg).data;
903       this.state.comments = data.comments;
904       this.state.loading = false;
905       this.setState(this.state);
906     } else if (
907       op == UserOperation.EditComment ||
908       op == UserOperation.DeleteComment ||
909       op == UserOperation.RemoveComment
910     ) {
911       let data = wsJsonToRes<CommentResponse>(msg).data;
912       editCommentRes(data.comment_view, this.state.comments);
913       this.setState(this.state);
914     } else if (op == UserOperation.CreateComment) {
915       let data = wsJsonToRes<CommentResponse>(msg).data;
916
917       // Necessary since it might be a user reply
918       if (data.form_id) {
919         // If you're on subscribed, only push it if you're subscribed.
920         if (this.state.listingType == ListingType.Subscribed) {
921           if (
922             UserService.Instance.myUserInfo.follows
923               .map(c => c.community.id)
924               .includes(data.comment_view.community.id)
925           ) {
926             this.state.comments.unshift(data.comment_view);
927           }
928         } else {
929           this.state.comments.unshift(data.comment_view);
930         }
931         this.setState(this.state);
932       }
933     } else if (op == UserOperation.SaveComment) {
934       let data = wsJsonToRes<CommentResponse>(msg).data;
935       saveCommentRes(data.comment_view, this.state.comments);
936       this.setState(this.state);
937     } else if (op == UserOperation.CreateCommentLike) {
938       let data = wsJsonToRes<CommentResponse>(msg).data;
939       createCommentLikeRes(data.comment_view, this.state.comments);
940       this.setState(this.state);
941     } else if (op == UserOperation.BlockPerson) {
942       let data = wsJsonToRes<BlockPersonResponse>(msg).data;
943       updatePersonBlock(data);
944     }
945   }
946 }