]> Untitled Git - lemmy-ui.git/blobdiff - src/shared/components/post.tsx
Remove categories
[lemmy-ui.git] / src / shared / components / post.tsx
index 1406de70ac0aec008a5f7d889fbe78d93641e478..b1bd9aaa52608b709621b5859f63361ae3db8d2d 100644 (file)
@@ -1,32 +1,34 @@
-import { Component, linkEvent } from 'inferno';
-import { Helmet } from 'inferno-helmet';
-import { Subscription } from 'rxjs';
+import { Component, linkEvent } from "inferno";
+import { HtmlTags } from "./html-tags";
+import { Spinner } from "./icon";
+import { Subscription } from "rxjs";
 import {
   UserOperation,
-  Post as PostI,
+  PostView,
   GetPostResponse,
   PostResponse,
-  MarkCommentAsReadForm,
+  MarkCommentAsRead,
   CommentResponse,
   CommunityResponse,
-  CommentNode as CommentNodeI,
   BanFromCommunityResponse,
   BanUserResponse,
   AddModToCommunityResponse,
   AddAdminResponse,
   SearchType,
   SortType,
-  SearchForm,
-  GetPostForm,
+  Search,
+  GetPost,
   SearchResponse,
   GetSiteResponse,
   GetCommunityResponse,
-  WebSocketJsonResponse,
-  ListCategoriesResponse,
-  Category,
-} from 'lemmy-js-client';
-import { CommentSortType, CommentViewType } from '../interfaces';
-import { WebSocketService, UserService } from '../services';
+} from "lemmy-js-client";
+import {
+  CommentSortType,
+  CommentViewType,
+  InitialFetchRequest,
+  CommentNode as CommentNodeI,
+} from "../interfaces";
+import { WebSocketService, UserService } from "../services";
 import {
   wsJsonToRes,
   toast,
@@ -36,33 +38,40 @@ import {
   createPostLikeRes,
   commentsToFlatNodes,
   setupTippy,
-  favIconUrl,
   setIsoData,
   getIdFromProps,
   getCommentIdFromProps,
   wsSubscribe,
-  setAuth,
-  lemmyHttp,
   isBrowser,
-} from '../utils';
-import { PostListing } from './post-listing';
-import { Sidebar } from './sidebar';
-import { CommentForm } from './comment-form';
-import { CommentNodes } from './comment-nodes';
-import autosize from 'autosize';
-import { i18n } from '../i18next';
+  previewLines,
+  isImage,
+  wsUserOp,
+  wsClient,
+  authField,
+  setOptionalAuth,
+  saveScrollPosition,
+  restoreScrollPosition,
+  buildCommentsTree,
+  insertCommentIntoTree,
+} from "../utils";
+import { PostListing } from "./post-listing";
+import { Sidebar } from "./sidebar";
+import { CommentForm } from "./comment-form";
+import { CommentNodes } from "./comment-nodes";
+import autosize from "autosize";
+import { i18n } from "../i18next";
 
 interface PostState {
   postRes: GetPostResponse;
   postId: number;
+  commentTree: CommentNodeI[];
   commentId?: number;
   commentSort: CommentSortType;
   commentViewType: CommentViewType;
   scrolled?: boolean;
   loading: boolean;
-  crossPosts: PostI[];
+  crossPosts: PostView[];
   siteRes: GetSiteResponse;
-  categories: Category[];
 }
 
 export class Post extends Component<any, PostState> {
@@ -71,14 +80,14 @@ export class Post extends Component<any, PostState> {
   private emptyState: PostState = {
     postRes: null,
     postId: getIdFromProps(this.props),
+    commentTree: [],
     commentId: getCommentIdFromProps(this.props),
     commentSort: CommentSortType.Hot,
     commentViewType: CommentViewType.Tree,
     scrolled: false,
     loading: true,
     crossPosts: [],
-    siteRes: this.isoData.site,
-    categories: [],
+    siteRes: this.isoData.site_res,
   };
 
   constructor(props: any, context: any) {
@@ -92,51 +101,75 @@ export class Post extends Component<any, PostState> {
     // Only fetch the data if coming from another route
     if (this.isoData.path == this.context.router.route.match.url) {
       this.state.postRes = this.isoData.routeData[0];
-      this.state.categories = this.isoData.routeData[1].categories;
+      this.state.commentTree = buildCommentsTree(
+        this.state.postRes.comments,
+        this.state.commentSort
+      );
       this.state.loading = false;
 
-      if (isBrowser() && this.state.commentId) {
-        this.scrollCommentIntoView();
+      if (isBrowser()) {
+        this.fetchCrossPosts();
+        if (this.state.commentId) {
+          this.scrollCommentIntoView();
+        }
       }
     } else {
       this.fetchPost();
-      WebSocketService.Instance.listCategories();
     }
   }
 
   fetchPost() {
-    let form: GetPostForm = {
+    let form: GetPost = {
       id: this.state.postId,
+      auth: authField(false),
     };
-    WebSocketService.Instance.getPost(form);
+    WebSocketService.Instance.send(wsClient.getPost(form));
+  }
+
+  fetchCrossPosts() {
+    if (this.state.postRes.post_view.post.url) {
+      let form: Search = {
+        q: this.state.postRes.post_view.post.url,
+        type_: SearchType.Url,
+        sort: SortType.TopAll,
+        page: 1,
+        limit: 6,
+        auth: authField(false),
+      };
+      WebSocketService.Instance.send(wsClient.search(form));
+    }
   }
 
-  static fetchInitialData(auth: string, path: string): Promise<any>[] {
-    let pathSplit = path.split('/');
+  static fetchInitialData(req: InitialFetchRequest): Promise<any>[] {
+    let pathSplit = req.path.split("/");
     let promises: Promise<any>[] = [];
 
     let id = Number(pathSplit[2]);
 
-    let postForm: GetPostForm = {
+    let postForm: GetPost = {
       id,
     };
-    setAuth(postForm, auth);
+    setOptionalAuth(postForm, req.auth);
 
-    promises.push(lemmyHttp.getPost(postForm));
-    promises.push(lemmyHttp.listCategories());
+    promises.push(req.client.getPost(postForm));
 
     return promises;
   }
 
   componentWillUnmount() {
     this.subscription.unsubscribe();
+    window.isoData.path = undefined;
+    saveScrollPosition(this.context);
   }
 
   componentDidMount() {
-    autosize(document.querySelectorAll('textarea'));
+    WebSocketService.Instance.send(
+      wsClient.postJoin({ post_id: this.state.postId })
+    );
+    autosize(document.querySelectorAll("textarea"));
   }
 
-  componentDidUpdate(_lastProps: any, lastState: PostState, _snapshot: any) {
+  componentDidUpdate(_lastProps: any, lastState: PostState) {
     if (
       this.state.commentId &&
       !this.state.scrolled &&
@@ -148,12 +181,11 @@ export class Post extends Component<any, PostState> {
 
     // Necessary if you are on a post and you click another post (same route)
     if (_lastProps.location.pathname !== _lastProps.history.location.pathname) {
-      // Couldnt get a refresh working. This does for now.
+      // TODO Couldnt get a refresh working. This does for now.
       location.reload();
 
       // let currentId = this.props.match.params.id;
       // WebSocketService.Instance.getPost(currentId);
-      // this.context.router.history.push('/sponsors');
       // this.context.refresh();
       // this.context.router.history.push(_lastProps.location.pathname);
     }
@@ -162,28 +194,33 @@ export class Post extends Component<any, PostState> {
   scrollCommentIntoView() {
     var elmnt = document.getElementById(`comment-${this.state.commentId}`);
     elmnt.scrollIntoView();
-    elmnt.classList.add('mark');
+    elmnt.classList.add("mark");
     this.state.scrolled = true;
     this.markScrolledAsRead(this.state.commentId);
   }
 
+  // TODO this needs some re-work
   markScrolledAsRead(commentId: number) {
-    let found = this.state.postRes.comments.find(c => c.id == commentId);
-    let parent = this.state.postRes.comments.find(c => found.parent_id == c.id);
+    let found = this.state.postRes.comments.find(
+      c => c.comment.id == commentId
+    );
+    let parent = this.state.postRes.comments.find(
+      c => found.comment.parent_id == c.comment.id
+    );
     let parent_user_id = parent
-      ? parent.creator_id
-      : this.state.postRes.post.creator_id;
+      ? parent.creator.id
+      : this.state.postRes.post_view.creator.id;
 
     if (
       UserService.Instance.user &&
       UserService.Instance.user.id == parent_user_id
     ) {
-      let form: MarkCommentAsReadForm = {
-        edit_id: found.id,
+      let form: MarkCommentAsRead = {
+        comment_id: found.comment.id,
         read: true,
-        auth: null,
+        auth: authField(),
       };
-      WebSocketService.Instance.markCommentAsRead(form);
+      WebSocketService.Instance.send(wsClient.markCommentAsRead(form));
       UserService.Instance.unreadCountSub.next(
         UserService.Instance.unreadCountSub.value - 1
       );
@@ -191,53 +228,55 @@ export class Post extends Component<any, PostState> {
   }
 
   get documentTitle(): string {
-    if (this.state.postRes) {
-      return `${this.state.postRes.post.name} - ${this.state.siteRes.site.name}`;
-    } else {
-      return 'Lemmy';
-    }
+    return `${this.state.postRes.post_view.post.name} - ${this.state.siteRes.site_view.site.name}`;
+  }
+
+  get imageTag(): string {
+    let post = this.state.postRes.post_view.post;
+    return (
+      post.thumbnail_url ||
+      (post.url ? (isImage(post.url) ? post.url : undefined) : undefined)
+    );
   }
 
-  get favIcon(): string {
-    return this.state.siteRes.site.icon
-      ? this.state.siteRes.site.icon
-      : favIconUrl;
+  get descriptionTag(): string {
+    let body = this.state.postRes.post_view.post.body;
+    return body ? previewLines(body) : undefined;
   }
 
   render() {
+    let pv = this.state.postRes?.post_view;
     return (
       <div class="container">
-        <Helmet title={this.documentTitle}>
-          <link
-            id="favicon"
-            rel="icon"
-            type="image/x-icon"
-            href={this.favIcon}
-          />
-        </Helmet>
         {this.state.loading ? (
           <h5>
-            <svg class="icon icon-spinner spin">
-              <use xlinkHref="#icon-spinner"></use>
-            </svg>
+            <Spinner />
           </h5>
         ) : (
           <div class="row">
             <div class="col-12 col-md-8 mb-3">
+              <HtmlTags
+                title={this.documentTitle}
+                path={this.context.router.route.match.url}
+                image={this.imageTag}
+                description={this.descriptionTag}
+              />
               <PostListing
-                communities={[this.state.postRes.community]}
-                post={this.state.postRes.post}
+                post_view={pv}
+                duplicates={this.state.crossPosts}
                 showBody
                 showCommunity
                 moderators={this.state.postRes.moderators}
                 admins={this.state.siteRes.admins}
-                enableDownvotes={this.state.siteRes.site.enable_downvotes}
-                enableNsfw={this.state.siteRes.site.enable_nsfw}
+                enableDownvotes={
+                  this.state.siteRes.site_view.site.enable_downvotes
+                }
+                enableNsfw={this.state.siteRes.site_view.site.enable_nsfw}
               />
               <div className="mb-2" />
               <CommentForm
                 postId={this.state.postId}
-                disabled={this.state.postRes.post.locked}
+                disabled={pv.post.locked}
               />
               {this.state.postRes.comments.length > 0 && this.sortRadios()}
               {this.state.commentViewType == CommentViewType.Tree &&
@@ -258,10 +297,10 @@ export class Post extends Component<any, PostState> {
         <div class="btn-group btn-group-toggle flex-wrap mr-3 mb-2">
           <label
             className={`btn btn-outline-secondary pointer ${
-              this.state.commentSort === CommentSortType.Hot && 'active'
+              this.state.commentSort === CommentSortType.Hot && "active"
             }`}
           >
-            {i18n.t('hot')}
+            {i18n.t("hot")}
             <input
               type="radio"
               value={CommentSortType.Hot}
@@ -271,10 +310,10 @@ export class Post extends Component<any, PostState> {
           </label>
           <label
             className={`btn btn-outline-secondary pointer ${
-              this.state.commentSort === CommentSortType.Top && 'active'
+              this.state.commentSort === CommentSortType.Top && "active"
             }`}
           >
-            {i18n.t('top')}
+            {i18n.t("top")}
             <input
               type="radio"
               value={CommentSortType.Top}
@@ -284,10 +323,10 @@ export class Post extends Component<any, PostState> {
           </label>
           <label
             className={`btn btn-outline-secondary pointer ${
-              this.state.commentSort === CommentSortType.New && 'active'
+              this.state.commentSort === CommentSortType.New && "active"
             }`}
           >
-            {i18n.t('new')}
+            {i18n.t("new")}
             <input
               type="radio"
               value={CommentSortType.New}
@@ -297,10 +336,10 @@ export class Post extends Component<any, PostState> {
           </label>
           <label
             className={`btn btn-outline-secondary pointer ${
-              this.state.commentSort === CommentSortType.Old && 'active'
+              this.state.commentSort === CommentSortType.Old && "active"
             }`}
           >
-            {i18n.t('old')}
+            {i18n.t("old")}
             <input
               type="radio"
               value={CommentSortType.Old}
@@ -312,10 +351,10 @@ export class Post extends Component<any, PostState> {
         <div class="btn-group btn-group-toggle flex-wrap mb-2">
           <label
             className={`btn btn-outline-secondary pointer ${
-              this.state.commentViewType === CommentViewType.Chat && 'active'
+              this.state.commentViewType === CommentViewType.Chat && "active"
             }`}
           >
-            {i18n.t('chat')}
+            {i18n.t("chat")}
             <input
               type="radio"
               value={CommentViewType.Chat}
@@ -329,18 +368,18 @@ export class Post extends Component<any, PostState> {
   }
 
   commentsFlat() {
+    // These are already sorted by new
     return (
       <div>
         <CommentNodes
           nodes={commentsToFlatNodes(this.state.postRes.comments)}
           noIndent
-          locked={this.state.postRes.post.locked}
+          locked={this.state.postRes.post_view.post.locked}
           moderators={this.state.postRes.moderators}
           admins={this.state.siteRes.admins}
-          postCreatorId={this.state.postRes.post.creator_id}
+          postCreatorId={this.state.postRes.post_view.creator.id}
           showContext
-          enableDownvotes={this.state.siteRes.site.enable_downvotes}
-          sort={this.state.commentSort}
+          enableDownvotes={this.state.siteRes.site_view.site.enable_downvotes}
         />
       </div>
     );
@@ -350,13 +389,12 @@ export class Post extends Component<any, PostState> {
     return (
       <div class="mb-3">
         <Sidebar
-          community={this.state.postRes.community}
+          community_view={this.state.postRes.community_view}
           moderators={this.state.postRes.moderators}
           admins={this.state.siteRes.admins}
           online={this.state.postRes.online}
-          enableNsfw={this.state.siteRes.site.enable_nsfw}
+          enableNsfw={this.state.siteRes.site_view.site.enable_nsfw}
           showIcon
-          categories={this.state.categories}
         />
       </div>
     );
@@ -365,202 +403,165 @@ export class Post extends Component<any, PostState> {
   handleCommentSortChange(i: Post, event: any) {
     i.state.commentSort = Number(event.target.value);
     i.state.commentViewType = CommentViewType.Tree;
+    i.state.commentTree = buildCommentsTree(
+      i.state.postRes.comments,
+      i.state.commentSort
+    );
     i.setState(i.state);
   }
 
   handleCommentViewTypeChange(i: Post, event: any) {
     i.state.commentViewType = Number(event.target.value);
     i.state.commentSort = CommentSortType.New;
+    i.state.commentTree = buildCommentsTree(
+      i.state.postRes.comments,
+      i.state.commentSort
+    );
     i.setState(i.state);
   }
 
-  buildCommentsTree(): CommentNodeI[] {
-    let map = new Map<number, CommentNodeI>();
-    for (let comment of this.state.postRes.comments) {
-      let node: CommentNodeI = {
-        comment: comment,
-        children: [],
-      };
-      map.set(comment.id, { ...node });
-    }
-    let tree: CommentNodeI[] = [];
-    for (let comment of this.state.postRes.comments) {
-      let child = map.get(comment.id);
-      if (comment.parent_id) {
-        let parent_ = map.get(comment.parent_id);
-        parent_.children.push(child);
-      } else {
-        tree.push(child);
-      }
-
-      this.setDepth(child);
-    }
-
-    return tree;
-  }
-
-  setDepth(node: CommentNodeI, i: number = 0): void {
-    for (let child of node.children) {
-      child.comment.depth = i;
-      this.setDepth(child, i + 1);
-    }
-  }
-
   commentsTree() {
-    let nodes = this.buildCommentsTree();
     return (
       <div>
         <CommentNodes
-          nodes={nodes}
-          locked={this.state.postRes.post.locked}
+          nodes={this.state.commentTree}
+          locked={this.state.postRes.post_view.post.locked}
           moderators={this.state.postRes.moderators}
           admins={this.state.siteRes.admins}
-          postCreatorId={this.state.postRes.post.creator_id}
-          sort={this.state.commentSort}
-          enableDownvotes={this.state.siteRes.site.enable_downvotes}
+          postCreatorId={this.state.postRes.post_view.creator.id}
+          enableDownvotes={this.state.siteRes.site_view.site.enable_downvotes}
         />
       </div>
     );
   }
 
-  parseMessage(msg: WebSocketJsonResponse) {
+  parseMessage(msg: any) {
+    let op = wsUserOp(msg);
     console.log(msg);
-    let res = wsJsonToRes(msg);
     if (msg.error) {
-      toast(i18n.t(msg.error), 'danger');
+      toast(i18n.t(msg.error), "danger");
       return;
     } else if (msg.reconnect) {
-      WebSocketService.Instance.getPost({
-        id: Number(this.props.match.params.id),
-      });
-    } else if (res.op == UserOperation.GetPost) {
-      let data = res.data as GetPostResponse;
+      let postId = Number(this.props.match.params.id);
+      WebSocketService.Instance.send(wsClient.postJoin({ post_id: postId }));
+      WebSocketService.Instance.send(
+        wsClient.getPost({
+          id: postId,
+          auth: authField(false),
+        })
+      );
+    } else if (op == UserOperation.GetPost) {
+      let data = wsJsonToRes<GetPostResponse>(msg).data;
       this.state.postRes = data;
+      this.state.commentTree = buildCommentsTree(
+        this.state.postRes.comments,
+        this.state.commentSort
+      );
       this.state.loading = false;
 
       // Get cross-posts
-      if (this.state.postRes.post.url) {
-        let form: SearchForm = {
-          q: this.state.postRes.post.url,
-          type_: SearchType.Url,
-          sort: SortType.TopAll,
-          page: 1,
-          limit: 6,
-        };
-        WebSocketService.Instance.search(form);
-      }
-
+      this.fetchCrossPosts();
       this.setState(this.state);
       setupTippy();
-    } else if (res.op == UserOperation.CreateComment) {
-      let data = res.data as CommentResponse;
+      restoreScrollPosition(this.context);
+    } else if (op == UserOperation.CreateComment) {
+      let data = wsJsonToRes<CommentResponse>(msg).data;
 
-      // Necessary since it might be a user reply
+      // Necessary since it might be a user reply, which has the recipients, to avoid double
       if (data.recipient_ids.length == 0) {
-        this.state.postRes.comments.unshift(data.comment);
+        this.state.postRes.comments.unshift(data.comment_view);
+        insertCommentIntoTree(this.state.commentTree, data.comment_view);
+        this.state.postRes.post_view.counts.comments++;
         this.setState(this.state);
+        setupTippy();
       }
     } else if (
-      res.op == UserOperation.EditComment ||
-      res.op == UserOperation.DeleteComment ||
-      res.op == UserOperation.RemoveComment
+      op == UserOperation.EditComment ||
+      op == UserOperation.DeleteComment ||
+      op == UserOperation.RemoveComment
     ) {
-      let data = res.data as CommentResponse;
-      editCommentRes(data, this.state.postRes.comments);
+      let data = wsJsonToRes<CommentResponse>(msg).data;
+      editCommentRes(data.comment_view, this.state.postRes.comments);
       this.setState(this.state);
-    } else if (res.op == UserOperation.SaveComment) {
-      let data = res.data as CommentResponse;
-      saveCommentRes(data, this.state.postRes.comments);
+    } else if (op == UserOperation.SaveComment) {
+      let data = wsJsonToRes<CommentResponse>(msg).data;
+      saveCommentRes(data.comment_view, this.state.postRes.comments);
       this.setState(this.state);
       setupTippy();
-    } else if (res.op == UserOperation.CreateCommentLike) {
-      let data = res.data as CommentResponse;
-      createCommentLikeRes(data, this.state.postRes.comments);
+    } else if (op == UserOperation.CreateCommentLike) {
+      let data = wsJsonToRes<CommentResponse>(msg).data;
+      createCommentLikeRes(data.comment_view, this.state.postRes.comments);
       this.setState(this.state);
-    } else if (res.op == UserOperation.CreatePostLike) {
-      let data = res.data as PostResponse;
-      createPostLikeRes(data, this.state.postRes.post);
+    } else if (op == UserOperation.CreatePostLike) {
+      let data = wsJsonToRes<PostResponse>(msg).data;
+      createPostLikeRes(data.post_view, this.state.postRes.post_view);
       this.setState(this.state);
     } else if (
-      res.op == UserOperation.EditPost ||
-      res.op == UserOperation.DeletePost ||
-      res.op == UserOperation.RemovePost ||
-      res.op == UserOperation.LockPost ||
-      res.op == UserOperation.StickyPost
+      op == UserOperation.EditPost ||
+      op == UserOperation.DeletePost ||
+      op == UserOperation.RemovePost ||
+      op == UserOperation.LockPost ||
+      op == UserOperation.StickyPost ||
+      op == UserOperation.SavePost
     ) {
-      let data = res.data as PostResponse;
-      this.state.postRes.post = data.post;
-      this.setState(this.state);
-      setupTippy();
-    } else if (res.op == UserOperation.SavePost) {
-      let data = res.data as PostResponse;
-      this.state.postRes.post = data.post;
+      let data = wsJsonToRes<PostResponse>(msg).data;
+      this.state.postRes.post_view = data.post_view;
       this.setState(this.state);
       setupTippy();
     } else if (
-      res.op == UserOperation.EditCommunity ||
-      res.op == UserOperation.DeleteCommunity ||
-      res.op == UserOperation.RemoveCommunity
+      op == UserOperation.EditCommunity ||
+      op == UserOperation.DeleteCommunity ||
+      op == UserOperation.RemoveCommunity ||
+      op == UserOperation.FollowCommunity
     ) {
-      let data = res.data as CommunityResponse;
-      this.state.postRes.community = data.community;
-      this.state.postRes.post.community_id = data.community.id;
-      this.state.postRes.post.community_name = data.community.name;
+      let data = wsJsonToRes<CommunityResponse>(msg).data;
+      this.state.postRes.community_view = data.community_view;
+      this.state.postRes.post_view.community = data.community_view.community;
       this.setState(this.state);
-    } else if (res.op == UserOperation.FollowCommunity) {
-      let data = res.data as CommunityResponse;
-      this.state.postRes.community.subscribed = data.community.subscribed;
-      this.state.postRes.community.number_of_subscribers =
-        data.community.number_of_subscribers;
       this.setState(this.state);
-    } else if (res.op == UserOperation.BanFromCommunity) {
-      let data = res.data as BanFromCommunityResponse;
+    } else if (op == UserOperation.BanFromCommunity) {
+      let data = wsJsonToRes<BanFromCommunityResponse>(msg).data;
       this.state.postRes.comments
-        .filter(c => c.creator_id == data.user.id)
-        .forEach(c => (c.banned_from_community = data.banned));
-      if (this.state.postRes.post.creator_id == data.user.id) {
-        this.state.postRes.post.banned_from_community = data.banned;
+        .filter(c => c.creator.id == data.user_view.user.id)
+        .forEach(c => (c.creator_banned_from_community = data.banned));
+      if (this.state.postRes.post_view.creator.id == data.user_view.user.id) {
+        this.state.postRes.post_view.creator_banned_from_community =
+          data.banned;
       }
       this.setState(this.state);
-    } else if (res.op == UserOperation.AddModToCommunity) {
-      let data = res.data as AddModToCommunityResponse;
+    } else if (op == UserOperation.AddModToCommunity) {
+      let data = wsJsonToRes<AddModToCommunityResponse>(msg).data;
       this.state.postRes.moderators = data.moderators;
       this.setState(this.state);
-    } else if (res.op == UserOperation.BanUser) {
-      let data = res.data as BanUserResponse;
+    } else if (op == UserOperation.BanUser) {
+      let data = wsJsonToRes<BanUserResponse>(msg).data;
       this.state.postRes.comments
-        .filter(c => c.creator_id == data.user.id)
-        .forEach(c => (c.banned = data.banned));
-      if (this.state.postRes.post.creator_id == data.user.id) {
-        this.state.postRes.post.banned = data.banned;
+        .filter(c => c.creator.id == data.user_view.user.id)
+        .forEach(c => (c.creator.banned = data.banned));
+      if (this.state.postRes.post_view.creator.id == data.user_view.user.id) {
+        this.state.postRes.post_view.creator.banned = data.banned;
       }
       this.setState(this.state);
-    } else if (res.op == UserOperation.AddAdmin) {
-      let data = res.data as AddAdminResponse;
+    } else if (op == UserOperation.AddAdmin) {
+      let data = wsJsonToRes<AddAdminResponse>(msg).data;
       this.state.siteRes.admins = data.admins;
       this.setState(this.state);
-    } else if (res.op == UserOperation.Search) {
-      let data = res.data as SearchResponse;
+    } else if (op == UserOperation.Search) {
+      let data = wsJsonToRes<SearchResponse>(msg).data;
       this.state.crossPosts = data.posts.filter(
-        p => p.id != Number(this.props.match.params.id)
+        p => p.post.id != Number(this.props.match.params.id)
       );
-      if (this.state.crossPosts.length) {
-        this.state.postRes.post.duplicates = this.state.crossPosts;
-      }
       this.setState(this.state);
-    } else if (res.op == UserOperation.TransferSite) {
-      let data = res.data as GetSiteResponse;
+    } else if (op == UserOperation.TransferSite) {
+      let data = wsJsonToRes<GetSiteResponse>(msg).data;
       this.state.siteRes = data;
       this.setState(this.state);
-    } else if (res.op == UserOperation.TransferCommunity) {
-      let data = res.data as GetCommunityResponse;
-      this.state.postRes.community = data.community;
+    } else if (op == UserOperation.TransferCommunity) {
+      let data = wsJsonToRes<GetCommunityResponse>(msg).data;
+      this.state.postRes.community_view = data.community_view;
+      this.state.postRes.post_view.community = data.community_view.community;
       this.state.postRes.moderators = data.moderators;
       this.setState(this.state);
-    } else if (res.op == UserOperation.ListCategories) {
-      let data = res.data as ListCategoriesResponse;
-      this.state.categories = data.categories;
-      this.setState(this.state);
     }
   }
 }