]> Untitled Git - lemmy-ui.git/blobdiff - src/shared/components/community/community.tsx
Adding Community Language fixes. #783 (#868)
[lemmy-ui.git] / src / shared / components / community / community.tsx
index 1d77d44f689047a5ad4d26b832b58e41389e9462..7ce6099988d9c9a866a3906532ea4e0fac3d88ba 100644 (file)
@@ -1,7 +1,9 @@
+import { None, Option, Some } from "@sniptt/monads";
 import { Component, linkEvent } from "inferno";
 import {
   AddModToCommunityResponse,
   BanFromCommunityResponse,
+  BlockCommunityResponse,
   BlockPersonResponse,
   CommentReportResponse,
   CommentResponse,
@@ -18,38 +20,51 @@ import {
   PostReportResponse,
   PostResponse,
   PostView,
+  PurgeItemResponse,
   SortType,
+  toOption,
   UserOperation,
+  wsJsonToRes,
+  wsUserOp,
 } from "lemmy-js-client";
 import { Subscription } from "rxjs";
 import { i18n } from "../../i18next";
-import { DataType, InitialFetchRequest } from "../../interfaces";
+import {
+  CommentViewType,
+  DataType,
+  InitialFetchRequest,
+} from "../../interfaces";
 import { UserService, WebSocketService } from "../../services";
 import {
-  authField,
+  auth,
   commentsToFlatNodes,
   communityRSSUrl,
   createCommentLikeRes,
   createPostLikeFindRes,
   editCommentRes,
   editPostFindRes,
+  enableDownvotes,
+  enableNsfw,
   fetchLimit,
   getDataTypeFromProps,
   getPageFromProps,
   getSortTypeFromProps,
+  isPostBlocked,
   notifyPost,
+  nsfwCheck,
+  postToCommentSortType,
+  relTags,
   restoreScrollPosition,
   saveCommentRes,
   saveScrollPosition,
   setIsoData,
-  setOptionalAuth,
   setupTippy,
+  showLocal,
   toast,
+  updateCommunityBlock,
   updatePersonBlock,
   wsClient,
-  wsJsonToRes,
   wsSubscribe,
-  wsUserOp,
 } from "../../utils";
 import { CommentNodes } from "../comment/comment-nodes";
 import { BannerIconHeader } from "../common/banner-icon-header";
@@ -59,11 +74,12 @@ import { Icon, Spinner } from "../common/icon";
 import { Paginator } from "../common/paginator";
 import { SortSelect } from "../common/sort-select";
 import { Sidebar } from "../community/sidebar";
+import { SiteSidebar } from "../home/site-sidebar";
 import { PostListings } from "../post/post-listings";
 import { CommunityLink } from "./community-link";
 
 interface State {
-  communityRes: GetCommunityResponse;
+  communityRes: Option<GetCommunityResponse>;
   siteRes: GetSiteResponse;
   communityName: string;
   communityLoading: boolean;
@@ -90,10 +106,15 @@ interface UrlParams {
 }
 
 export class Community extends Component<any, State> {
-  private isoData = setIsoData(this.context);
+  private isoData = setIsoData(
+    this.context,
+    GetCommunityResponse,
+    GetPostsResponse,
+    GetCommentsResponse
+  );
   private subscription: Subscription;
   private emptyState: State = {
-    communityRes: undefined,
+    communityRes: None,
     communityName: this.props.match.params.name,
     communityLoading: true,
     postsLoading: true,
@@ -120,34 +141,49 @@ export class Community extends Component<any, State> {
 
     // Only fetch the data if coming from another route
     if (this.isoData.path == this.context.router.route.match.url) {
-      this.state.communityRes = this.isoData.routeData[0];
-      if (this.state.dataType == DataType.Post) {
-        this.state.posts = this.isoData.routeData[1].posts;
-      } else {
-        this.state.comments = this.isoData.routeData[1].comments;
+      this.state = {
+        ...this.state,
+        communityRes: Some(this.isoData.routeData[0] as GetCommunityResponse),
+      };
+      let postsRes = Some(this.isoData.routeData[1] as GetPostsResponse);
+      let commentsRes = Some(this.isoData.routeData[2] as GetCommentsResponse);
+
+      if (postsRes.isSome()) {
+        this.state = { ...this.state, posts: postsRes.unwrap().posts };
       }
-      this.state.communityLoading = false;
-      this.state.postsLoading = false;
-      this.state.commentsLoading = false;
+
+      if (commentsRes.isSome()) {
+        this.state = { ...this.state, comments: commentsRes.unwrap().comments };
+      }
+
+      this.state = {
+        ...this.state,
+        communityLoading: false,
+        postsLoading: false,
+        commentsLoading: false,
+      };
     } else {
       this.fetchCommunity();
       this.fetchData();
     }
-    setupTippy();
   }
 
   fetchCommunity() {
-    let form: GetCommunity = {
-      name: this.state.communityName ? this.state.communityName : null,
-      auth: authField(false),
-    };
+    let form = new GetCommunity({
+      name: Some(this.state.communityName),
+      id: None,
+      auth: auth(false).ok(),
+    });
     WebSocketService.Instance.send(wsClient.getCommunity(form));
   }
 
+  componentDidMount() {
+    setupTippy();
+  }
+
   componentWillUnmount() {
     saveScrollPosition(this.context);
     this.subscription.unsubscribe();
-    window.isoData.path = undefined;
   }
 
   static getDerivedStateFromProps(props: any): CommunityProps {
@@ -162,65 +198,66 @@ export class Community extends Component<any, State> {
     let pathSplit = req.path.split("/");
     let promises: Promise<any>[] = [];
 
-    // It can be /c/main, or /c/1
-    let idOrName = pathSplit[2];
-    let id: number;
-    let name_: string;
-    if (isNaN(Number(idOrName))) {
-      name_ = idOrName;
-    } else {
-      id = Number(idOrName);
-    }
-
-    let communityForm: GetCommunity = id ? { id } : { name: name_ };
-    setOptionalAuth(communityForm, req.auth);
+    let communityName = pathSplit[2];
+    let communityForm = new GetCommunity({
+      name: Some(communityName),
+      id: None,
+      auth: req.auth,
+    });
     promises.push(req.client.getCommunity(communityForm));
 
     let dataType: DataType = pathSplit[4]
       ? DataType[pathSplit[4]]
       : DataType.Post;
 
-    let sort: SortType = pathSplit[6]
-      ? SortType[pathSplit[6]]
-      : UserService.Instance.myUserInfo
-      ? Object.values(SortType)[
-          UserService.Instance.myUserInfo.local_user_view.local_user
-            .default_sort_type
-        ]
-      : SortType.Active;
+    let sort: Option<SortType> = toOption(
+      pathSplit[6]
+        ? SortType[pathSplit[6]]
+        : UserService.Instance.myUserInfo.match({
+            some: mui =>
+              Object.values(SortType)[
+                mui.local_user_view.local_user.default_sort_type
+              ],
+            none: SortType.Active,
+          })
+    );
 
-    let page = pathSplit[8] ? Number(pathSplit[8]) : 1;
+    let page = toOption(pathSplit[8] ? Number(pathSplit[8]) : 1);
 
     if (dataType == DataType.Post) {
-      let getPostsForm: GetPosts = {
+      let getPostsForm = new GetPosts({
+        community_name: Some(communityName),
+        community_id: None,
         page,
-        limit: fetchLimit,
+        limit: Some(fetchLimit),
         sort,
-        type_: ListingType.Community,
-        saved_only: false,
-      };
-      setOptionalAuth(getPostsForm, req.auth);
-      this.setName(getPostsForm, name_);
+        type_: Some(ListingType.All),
+        saved_only: Some(false),
+        auth: req.auth,
+      });
       promises.push(req.client.getPosts(getPostsForm));
+      promises.push(Promise.resolve());
     } else {
-      let getCommentsForm: GetComments = {
+      let getCommentsForm = new GetComments({
+        community_name: Some(communityName),
+        community_id: None,
         page,
-        limit: fetchLimit,
-        sort,
-        type_: ListingType.Community,
-        saved_only: false,
-      };
-      setOptionalAuth(getCommentsForm, req.auth);
+        limit: Some(fetchLimit),
+        max_depth: None,
+        sort: sort.map(postToCommentSortType),
+        type_: Some(ListingType.All),
+        saved_only: Some(false),
+        post_id: None,
+        parent_id: None,
+        auth: req.auth,
+      });
+      promises.push(Promise.resolve());
       promises.push(req.client.getComments(getCommentsForm));
     }
 
     return promises;
   }
 
-  static setName(obj: any, name_: string) {
-    obj.community_name = name_;
-  }
-
   componentDidUpdate(_: any, lastState: State) {
     if (
       lastState.dataType !== this.state.dataType ||
@@ -233,79 +270,134 @@ export class Community extends Component<any, State> {
   }
 
   get documentTitle(): string {
-    return `${this.state.communityRes.community_view.community.title} - ${this.state.siteRes.site_view.site.name}`;
+    return this.state.communityRes.match({
+      some: res =>
+        `${res.community_view.community.title} - ${this.state.siteRes.site_view.site.name}`,
+      none: "",
+    });
   }
 
   render() {
-    let cv = this.state.communityRes?.community_view;
+    // For some reason, this returns an empty vec if it matches the site langs
+    let communityLangs = this.state.communityRes.map(r => {
+      let langs = r.discussion_languages;
+      if (langs.length == 0) {
+        return this.state.siteRes.all_languages.map(l => l.id);
+      } else {
+        return langs;
+      }
+    });
+
     return (
-      <div class="container">
+      <div className="container-lg">
         {this.state.communityLoading ? (
           <h5>
             <Spinner large />
           </h5>
         ) : (
-          <>
-            <HtmlTags
-              title={this.documentTitle}
-              path={this.context.router.route.match.url}
-              description={cv.community.description}
-              image={cv.community.icon}
-            />
+          this.state.communityRes.match({
+            some: res => (
+              <>
+                <HtmlTags
+                  title={this.documentTitle}
+                  path={this.context.router.route.match.url}
+                  description={res.community_view.community.description}
+                  image={res.community_view.community.icon}
+                />
 
-            <div class="row">
-              <div class="col-12 col-md-8">
-                {this.communityInfo()}
-                <div class="d-block d-md-none">
-                  <button
-                    class="btn btn-secondary d-inline-block mb-2 mr-3"
-                    onClick={linkEvent(this, this.handleShowSidebarMobile)}
-                  >
-                    {i18n.t("sidebar")}{" "}
-                    <Icon
-                      icon={
-                        this.state.showSidebarMobile
-                          ? `minus-square`
-                          : `plus-square`
-                      }
-                      classes="icon-inline"
+                <div className="row">
+                  <div className="col-12 col-md-8">
+                    {this.communityInfo()}
+                    <div className="d-block d-md-none">
+                      <button
+                        className="btn btn-secondary d-inline-block mb-2 mr-3"
+                        onClick={linkEvent(this, this.handleShowSidebarMobile)}
+                      >
+                        {i18n.t("sidebar")}{" "}
+                        <Icon
+                          icon={
+                            this.state.showSidebarMobile
+                              ? `minus-square`
+                              : `plus-square`
+                          }
+                          classes="icon-inline"
+                        />
+                      </button>
+                      {this.state.showSidebarMobile && (
+                        <>
+                          <Sidebar
+                            community_view={res.community_view}
+                            moderators={res.moderators}
+                            admins={this.state.siteRes.admins}
+                            online={res.online}
+                            enableNsfw={enableNsfw(this.state.siteRes)}
+                            editable
+                            allLanguages={this.state.siteRes.all_languages}
+                            siteLanguages={
+                              this.state.siteRes.discussion_languages
+                            }
+                            communityLanguages={communityLangs}
+                          />
+                          {!res.community_view.community.local &&
+                            res.site.match({
+                              some: site => (
+                                <SiteSidebar
+                                  site={site}
+                                  showLocal={showLocal(this.isoData)}
+                                  admins={None}
+                                  counts={None}
+                                  online={None}
+                                />
+                              ),
+                              none: <></>,
+                            })}
+                        </>
+                      )}
+                    </div>
+                    {this.selects()}
+                    {this.listings()}
+                    <Paginator
+                      page={this.state.page}
+                      onChange={this.handlePageChange}
                     />
-                  </button>
-                  {this.state.showSidebarMobile && (
+                  </div>
+                  <div className="d-none d-md-block col-md-4">
                     <Sidebar
-                      community_view={cv}
-                      moderators={this.state.communityRes.moderators}
+                      community_view={res.community_view}
+                      moderators={res.moderators}
                       admins={this.state.siteRes.admins}
-                      online={this.state.communityRes.online}
-                      enableNsfw={this.state.siteRes.site_view.site.enable_nsfw}
+                      online={res.online}
+                      enableNsfw={enableNsfw(this.state.siteRes)}
+                      editable
+                      allLanguages={this.state.siteRes.all_languages}
+                      siteLanguages={this.state.siteRes.discussion_languages}
+                      communityLanguages={communityLangs}
                     />
-                  )}
+                    {!res.community_view.community.local &&
+                      res.site.match({
+                        some: site => (
+                          <SiteSidebar
+                            site={site}
+                            showLocal={showLocal(this.isoData)}
+                            admins={None}
+                            counts={None}
+                            online={None}
+                          />
+                        ),
+                        none: <></>,
+                      })}
+                  </div>
                 </div>
-                {this.selects()}
-                {this.listings()}
-                <Paginator
-                  page={this.state.page}
-                  onChange={this.handlePageChange}
-                />
-              </div>
-              <div class="d-none d-md-block col-md-4">
-                <Sidebar
-                  community_view={cv}
-                  moderators={this.state.communityRes.moderators}
-                  admins={this.state.siteRes.admins}
-                  online={this.state.communityRes.online}
-                  enableNsfw={this.state.siteRes.site_view.site.enable_nsfw}
-                />
-              </div>
-            </div>
-          </>
+              </>
+            ),
+            none: <></>,
+          })
         )}
       </div>
     );
   }
 
   listings() {
-    let site = this.state.siteRes.site_view.site;
     return this.state.dataType == DataType.Post ? (
       this.state.postsLoading ? (
         <h5>
@@ -315,8 +407,10 @@ export class Community extends Component<any, State> {
         <PostListings
           posts={this.state.posts}
           removeDuplicates
-          enableDownvotes={site.enable_downvotes}
-          enableNsfw={site.enable_nsfw}
+          enableDownvotes={enableDownvotes(this.state.siteRes)}
+          enableNsfw={enableNsfw(this.state.siteRes)}
+          allLanguages={this.state.siteRes.all_languages}
+          siteLanguages={this.state.siteRes.discussion_languages}
         />
       )
     ) : this.state.commentsLoading ? (
@@ -326,52 +420,66 @@ export class Community extends Component<any, State> {
     ) : (
       <CommentNodes
         nodes={commentsToFlatNodes(this.state.comments)}
+        viewType={CommentViewType.Flat}
         noIndent
         showContext
-        enableDownvotes={site.enable_downvotes}
+        enableDownvotes={enableDownvotes(this.state.siteRes)}
+        moderators={this.state.communityRes.map(r => r.moderators)}
+        admins={Some(this.state.siteRes.admins)}
+        maxCommentsShown={None}
+        allLanguages={this.state.siteRes.all_languages}
+        siteLanguages={this.state.siteRes.discussion_languages}
       />
     );
   }
 
   communityInfo() {
-    let community = this.state.communityRes.community_view.community;
-    return (
-      <div class="mb-2">
-        <BannerIconHeader banner={community.banner} icon={community.icon} />
-        <h5 class="mb-0 overflow-wrap-anywhere">{community.title}</h5>
-        <CommunityLink
-          community={community}
-          realLink
-          useApubName
-          muted
-          hideAvatar
-        />
-      </div>
-    );
+    return this.state.communityRes
+      .map(r => r.community_view.community)
+      .match({
+        some: community => (
+          <div className="mb-2">
+            <BannerIconHeader banner={community.banner} icon={community.icon} />
+            <h5 className="mb-0 overflow-wrap-anywhere">{community.title}</h5>
+            <CommunityLink
+              community={community}
+              realLink
+              useApubName
+              muted
+              hideAvatar
+            />
+          </div>
+        ),
+        none: <></>,
+      });
   }
 
   selects() {
+    let communityRss = this.state.communityRes.map(r =>
+      communityRSSUrl(r.community_view.community.actor_id, this.state.sort)
+    );
     return (
-      <div class="mb-3">
-        <span class="mr-3">
+      <div className="mb-3">
+        <span className="mr-3">
           <DataTypeSelect
             type_={this.state.dataType}
             onChange={this.handleDataTypeChange}
           />
         </span>
-        <span class="mr-2">
+        <span className="mr-2">
           <SortSelect sort={this.state.sort} onChange={this.handleSortChange} />
         </span>
-        <a
-          href={communityRSSUrl(
-            this.state.communityRes.community_view.community.actor_id,
-            this.state.sort
-          )}
-          title="RSS"
-          rel="noopener"
-        >
-          <Icon icon="rss" classes="text-muted small" />
-        </a>
+        {communityRss.match({
+          some: rss => (
+            <>
+              <a href={rss} title="RSS" rel={relTags}>
+                <Icon icon="rss" classes="text-muted small" />
+              </a>
+              <link rel="alternate" type="application/atom+xml" href={rss} />
+            </>
+          ),
+          none: <></>,
+        })}
       </div>
     );
   }
@@ -392,8 +500,7 @@ export class Community extends Component<any, State> {
   }
 
   handleShowSidebarMobile(i: Community) {
-    i.state.showSidebarMobile = !i.state.showSidebarMobile;
-    i.setState(i.state);
+    i.setState({ showSidebarMobile: !i.state.showSidebarMobile });
   }
 
   updateUrl(paramUpdates: UrlParams) {
@@ -410,26 +517,31 @@ export class Community extends Component<any, State> {
 
   fetchData() {
     if (this.state.dataType == DataType.Post) {
-      let form: GetPosts = {
-        page: this.state.page,
-        limit: fetchLimit,
-        sort: this.state.sort,
-        type_: ListingType.Community,
-        community_name: this.state.communityName,
-        saved_only: false,
-        auth: authField(false),
-      };
+      let form = new GetPosts({
+        page: Some(this.state.page),
+        limit: Some(fetchLimit),
+        sort: Some(this.state.sort),
+        type_: Some(ListingType.All),
+        community_name: Some(this.state.communityName),
+        community_id: None,
+        saved_only: Some(false),
+        auth: auth(false).ok(),
+      });
       WebSocketService.Instance.send(wsClient.getPosts(form));
     } else {
-      let form: GetComments = {
-        page: this.state.page,
-        limit: fetchLimit,
-        sort: this.state.sort,
-        type_: ListingType.Community,
-        community_name: this.state.communityName,
-        saved_only: false,
-        auth: authField(false),
-      };
+      let form = new GetComments({
+        page: Some(this.state.page),
+        limit: Some(fetchLimit),
+        max_depth: None,
+        sort: Some(postToCommentSortType(this.state.sort)),
+        type_: Some(ListingType.All),
+        community_name: Some(this.state.communityName),
+        community_id: None,
+        saved_only: Some(false),
+        post_id: None,
+        parent_id: None,
+        auth: auth(false).ok(),
+      });
       WebSocketService.Instance.send(wsClient.getComments(form));
     }
   }
@@ -442,17 +554,20 @@ export class Community extends Component<any, State> {
       this.context.router.history.push("/");
       return;
     } else if (msg.reconnect) {
-      WebSocketService.Instance.send(
-        wsClient.communityJoin({
-          community_id: this.state.communityRes.community_view.community.id,
-        })
-      );
+      this.state.communityRes.match({
+        some: res => {
+          WebSocketService.Instance.send(
+            wsClient.communityJoin({
+              community_id: res.community_view.community.id,
+            })
+          );
+        },
+        none: void 0,
+      });
       this.fetchData();
     } else if (op == UserOperation.GetCommunity) {
-      let data = wsJsonToRes<GetCommunityResponse>(msg).data;
-      this.state.communityRes = data;
-      this.state.communityLoading = false;
-      this.setState(this.state);
+      let data = wsJsonToRes<GetCommunityResponse>(msg, GetCommunityResponse);
+      this.setState({ communityRes: Some(data), communityLoading: false });
       // TODO why is there no auth in this form?
       WebSocketService.Instance.send(
         wsClient.communityJoin({
@@ -464,21 +579,29 @@ export class Community extends Component<any, State> {
       op == UserOperation.DeleteCommunity ||
       op == UserOperation.RemoveCommunity
     ) {
-      let data = wsJsonToRes<CommunityResponse>(msg).data;
-      this.state.communityRes.community_view = data.community_view;
+      let data = wsJsonToRes<CommunityResponse>(msg, CommunityResponse);
+      this.state.communityRes.match({
+        some: res => {
+          res.community_view = data.community_view;
+          res.discussion_languages = data.discussion_languages;
+        },
+        none: void 0,
+      });
       this.setState(this.state);
     } else if (op == UserOperation.FollowCommunity) {
-      let data = wsJsonToRes<CommunityResponse>(msg).data;
-      this.state.communityRes.community_view.subscribed =
-        data.community_view.subscribed;
-      this.state.communityRes.community_view.counts.subscribers =
-        data.community_view.counts.subscribers;
+      let data = wsJsonToRes<CommunityResponse>(msg, CommunityResponse);
+      this.state.communityRes.match({
+        some: res => {
+          res.community_view.subscribed = data.community_view.subscribed;
+          res.community_view.counts.subscribers =
+            data.community_view.counts.subscribers;
+        },
+        none: void 0,
+      });
       this.setState(this.state);
     } else if (op == UserOperation.GetPosts) {
-      let data = wsJsonToRes<GetPostsResponse>(msg).data;
-      this.state.posts = data.posts;
-      this.state.postsLoading = false;
-      this.setState(this.state);
+      let data = wsJsonToRes<GetPostsResponse>(msg, GetPostsResponse);
+      this.setState({ posts: data.posts, postsLoading: false });
       restoreScrollPosition(this.context);
       setupTippy();
     } else if (
@@ -486,32 +609,51 @@ export class Community extends Component<any, State> {
       op == UserOperation.DeletePost ||
       op == UserOperation.RemovePost ||
       op == UserOperation.LockPost ||
-      op == UserOperation.StickyPost ||
+      op == UserOperation.FeaturePost ||
       op == UserOperation.SavePost
     ) {
-      let data = wsJsonToRes<PostResponse>(msg).data;
+      let data = wsJsonToRes<PostResponse>(msg, PostResponse);
       editPostFindRes(data.post_view, this.state.posts);
       this.setState(this.state);
     } else if (op == UserOperation.CreatePost) {
-      let data = wsJsonToRes<PostResponse>(msg).data;
-      this.state.posts.unshift(data.post_view);
+      let data = wsJsonToRes<PostResponse>(msg, PostResponse);
+
+      let showPostNotifs = UserService.Instance.myUserInfo
+        .map(m => m.local_user_view.local_user.show_new_post_notifs)
+        .unwrapOr(false);
+
+      // Only push these if you're on the first page, you pass the nsfw check, and it isn't blocked
+      //
       if (
-        UserService.Instance.myUserInfo?.local_user_view.local_user
-          .show_new_post_notifs
+        this.state.page == 1 &&
+        nsfwCheck(data.post_view) &&
+        !isPostBlocked(data.post_view)
       ) {
-        notifyPost(data.post_view, this.context.router);
+        this.state.posts.unshift(data.post_view);
+        if (showPostNotifs) {
+          notifyPost(data.post_view, this.context.router);
+        }
+        this.setState(this.state);
       }
-      this.setState(this.state);
     } else if (op == UserOperation.CreatePostLike) {
-      let data = wsJsonToRes<PostResponse>(msg).data;
+      let data = wsJsonToRes<PostResponse>(msg, PostResponse);
       createPostLikeFindRes(data.post_view, this.state.posts);
       this.setState(this.state);
     } else if (op == UserOperation.AddModToCommunity) {
-      let data = wsJsonToRes<AddModToCommunityResponse>(msg).data;
-      this.state.communityRes.moderators = data.moderators;
+      let data = wsJsonToRes<AddModToCommunityResponse>(
+        msg,
+        AddModToCommunityResponse
+      );
+      this.state.communityRes.match({
+        some: res => (res.moderators = data.moderators),
+        none: void 0,
+      });
       this.setState(this.state);
     } else if (op == UserOperation.BanFromCommunity) {
-      let data = wsJsonToRes<BanFromCommunityResponse>(msg).data;
+      let data = wsJsonToRes<BanFromCommunityResponse>(
+        msg,
+        BanFromCommunityResponse
+      );
 
       // TODO this might be incorrect
       this.state.posts
@@ -520,20 +662,18 @@ export class Community extends Component<any, State> {
 
       this.setState(this.state);
     } else if (op == UserOperation.GetComments) {
-      let data = wsJsonToRes<GetCommentsResponse>(msg).data;
-      this.state.comments = data.comments;
-      this.state.commentsLoading = false;
-      this.setState(this.state);
+      let data = wsJsonToRes<GetCommentsResponse>(msg, GetCommentsResponse);
+      this.setState({ comments: data.comments, commentsLoading: false });
     } else if (
       op == UserOperation.EditComment ||
       op == UserOperation.DeleteComment ||
       op == UserOperation.RemoveComment
     ) {
-      let data = wsJsonToRes<CommentResponse>(msg).data;
+      let data = wsJsonToRes<CommentResponse>(msg, CommentResponse);
       editCommentRes(data.comment_view, this.state.comments);
       this.setState(this.state);
     } else if (op == UserOperation.CreateComment) {
-      let data = wsJsonToRes<CommentResponse>(msg).data;
+      let data = wsJsonToRes<CommentResponse>(msg, CommentResponse);
 
       // Necessary since it might be a user reply
       if (data.form_id) {
@@ -541,26 +681,43 @@ export class Community extends Component<any, State> {
         this.setState(this.state);
       }
     } else if (op == UserOperation.SaveComment) {
-      let data = wsJsonToRes<CommentResponse>(msg).data;
+      let data = wsJsonToRes<CommentResponse>(msg, CommentResponse);
       saveCommentRes(data.comment_view, this.state.comments);
       this.setState(this.state);
     } else if (op == UserOperation.CreateCommentLike) {
-      let data = wsJsonToRes<CommentResponse>(msg).data;
+      let data = wsJsonToRes<CommentResponse>(msg, CommentResponse);
       createCommentLikeRes(data.comment_view, this.state.comments);
       this.setState(this.state);
     } else if (op == UserOperation.BlockPerson) {
-      let data = wsJsonToRes<BlockPersonResponse>(msg).data;
+      let data = wsJsonToRes<BlockPersonResponse>(msg, BlockPersonResponse);
       updatePersonBlock(data);
     } else if (op == UserOperation.CreatePostReport) {
-      let data = wsJsonToRes<PostReportResponse>(msg).data;
+      let data = wsJsonToRes<PostReportResponse>(msg, PostReportResponse);
       if (data) {
         toast(i18n.t("report_created"));
       }
     } else if (op == UserOperation.CreateCommentReport) {
-      let data = wsJsonToRes<CommentReportResponse>(msg).data;
+      let data = wsJsonToRes<CommentReportResponse>(msg, CommentReportResponse);
       if (data) {
         toast(i18n.t("report_created"));
       }
+    } else if (op == UserOperation.PurgeCommunity) {
+      let data = wsJsonToRes<PurgeItemResponse>(msg, PurgeItemResponse);
+      if (data.success) {
+        toast(i18n.t("purge_success"));
+        this.context.router.history.push(`/`);
+      }
+    } else if (op == UserOperation.BlockCommunity) {
+      let data = wsJsonToRes<BlockCommunityResponse>(
+        msg,
+        BlockCommunityResponse
+      );
+      this.state.communityRes.match({
+        some: res => (res.community_view.blocked = data.blocked),
+        none: void 0,
+      });
+      updateCommunityBlock(data);
+      this.setState(this.state);
     }
   }
 }