From ea317af2697647aad51a7f141c41899edaef28d2 Mon Sep 17 00:00:00 2001
From: Dessalines <tyhou13@gmx.com>
Date: Wed, 23 Dec 2020 20:58:27 -0500
Subject: [PATCH] First pass at v2_api

---
 package.json                                  |   2 +-
 src/client/index.tsx                          |   4 +-
 src/server/index.tsx                          |  10 +-
 src/shared/components/admin-settings.tsx      |  71 +--
 src/shared/components/app.tsx                 |  17 +-
 src/shared/components/comment-form.tsx        |  82 +--
 src/shared/components/comment-node.tsx        | 322 +++++-----
 src/shared/components/comment-nodes.tsx       |  13 +-
 src/shared/components/communities.tsx         |  85 +--
 src/shared/components/community-form.tsx      |  72 +--
 src/shared/components/community-link.tsx      |  13 +-
 src/shared/components/community.tsx           | 202 +++----
 src/shared/components/create-community.tsx    |  29 +-
 src/shared/components/create-post.tsx         |  47 +-
 .../components/create-private-message.tsx     |  42 +-
 src/shared/components/inbox.tsx               | 362 ++++++-----
 src/shared/components/instances.tsx           |   4 +-
 src/shared/components/login.tsx               |  66 ++-
 src/shared/components/main.tsx                | 255 ++++----
 src/shared/components/modlog.tsx              | 560 +++++++++---------
 src/shared/components/navbar.tsx              | 120 ++--
 src/shared/components/password_change.tsx     |  22 +-
 src/shared/components/post-form.tsx           | 118 ++--
 src/shared/components/post-listing.tsx        | 392 ++++++------
 src/shared/components/post-listings.tsx       |  47 +-
 src/shared/components/post.tsx                | 264 ++++-----
 .../components/private-message-form.tsx       |  69 +--
 src/shared/components/private-message.tsx     |  84 +--
 src/shared/components/search.tsx              | 102 ++--
 src/shared/components/setup.tsx               |  23 +-
 src/shared/components/sidebar.tsx             | 128 ++--
 src/shared/components/site-form.tsx           |  12 +-
 src/shared/components/theme.tsx               |   4 +-
 src/shared/components/user-details.tsx        | 123 ++--
 src/shared/components/user-listing.tsx        |  14 +-
 src/shared/components/user.tsx                | 206 +++----
 src/shared/interfaces.ts                      |  23 +-
 src/shared/services/UserService.ts            |  15 +-
 src/shared/services/WebSocketService.ts       | 369 +-----------
 src/shared/utils.ts                           | 284 +++++----
 yarn.lock                                     |   8 +-
 41 files changed, 2218 insertions(+), 2467 deletions(-)

diff --git a/package.json b/package.json
index c5493f5..e5ecf1e 100644
--- a/package.json
+++ b/package.json
@@ -67,7 +67,7 @@
     "eslint": "^7.15.0",
     "eslint-plugin-jane": "^9.0.4",
     "husky": "^4.3.5",
-    "lemmy-js-client": "^1.0.16",
+    "lemmy-js-client": "1.0.17-beta5",
     "lint-staged": "^10.5.3",
     "mini-css-extract-plugin": "^1.3.2",
     "node-fetch": "^2.6.1",
diff --git a/src/client/index.tsx b/src/client/index.tsx
index b7f216d..b07d180 100644
--- a/src/client/index.tsx
+++ b/src/client/index.tsx
@@ -3,12 +3,12 @@ import { BrowserRouter } from 'inferno-router';
 import { initializeSite } from '../shared/initialize';
 import { App } from '../shared/components/app';
 
-const site = window.isoData.site;
+const site = window.isoData.site_res;
 initializeSite(site);
 
 const wrapper = (
   <BrowserRouter>
-    <App site={window.isoData.site} />
+    <App siteRes={window.isoData.site_res} />
   </BrowserRouter>
 );
 
diff --git a/src/server/index.tsx b/src/server/index.tsx
index cc8c3b0..a2bcd4a 100644
--- a/src/server/index.tsx
+++ b/src/server/index.tsx
@@ -8,8 +8,7 @@ import { App } from '../shared/components/app';
 import { InitialFetchRequest, IsoData } from '../shared/interfaces';
 import { routes } from '../shared/routes';
 import IsomorphicCookie from 'isomorphic-cookie';
-import { setAuth } from '../shared/utils';
-import { GetSiteForm, LemmyHttp } from 'lemmy-js-client';
+import { GetSite, LemmyHttp } from 'lemmy-js-client';
 import process from 'process';
 import { Helmet } from 'inferno-helmet';
 import { initializeSite } from '../shared/initialize';
@@ -30,8 +29,7 @@ server.get('/*', async (req, res) => {
   const context = {} as any;
   let auth: string = IsomorphicCookie.load('jwt', req);
 
-  let getSiteForm: GetSiteForm = {};
-  setAuth(getSiteForm, auth);
+  let getSiteForm: GetSite = { auth };
 
   let promises: Promise<any>[] = [];
 
@@ -70,14 +68,14 @@ server.get('/*', async (req, res) => {
 
   let isoData: IsoData = {
     path: req.path,
-    site,
+    site_res: site,
     routeData,
     lang,
   };
 
   const wrapper = (
     <StaticRouter location={req.url} context={isoData}>
-      <App site={isoData.site} />
+      <App siteRes={isoData.site_res} />
     </StaticRouter>
   );
   if (context.url) {
diff --git a/src/shared/components/admin-settings.tsx b/src/shared/components/admin-settings.tsx
index 6340d4f..b9f93f4 100644
--- a/src/shared/components/admin-settings.tsx
+++ b/src/shared/components/admin-settings.tsx
@@ -4,12 +4,11 @@ import {
   UserOperation,
   SiteResponse,
   GetSiteResponse,
-  SiteConfigForm,
+  SaveSiteConfig,
   GetSiteConfigResponse,
-  WebSocketJsonResponse,
   GetSiteConfig,
 } from 'lemmy-js-client';
-import { WebSocketService } from '../services';
+import { UserService, WebSocketService } from '../services';
 import {
   wsJsonToRes,
   capitalizeFirstLetter,
@@ -18,7 +17,7 @@ import {
   setIsoData,
   wsSubscribe,
   isBrowser,
-  setAuth,
+  wsUserOp,
 } from '../utils';
 import autosize from 'autosize';
 import { SiteForm } from './site-form';
@@ -30,7 +29,7 @@ import { InitialFetchRequest } from 'shared/interfaces';
 interface AdminSettingsState {
   siteRes: GetSiteResponse;
   siteConfigRes: GetSiteConfigResponse;
-  siteConfigForm: SiteConfigForm;
+  siteConfigForm: SaveSiteConfig;
   loading: boolean;
   siteConfigLoading: boolean;
 }
@@ -40,10 +39,10 @@ export class AdminSettings extends Component<any, AdminSettingsState> {
   private isoData = setIsoData(this.context);
   private subscription: Subscription;
   private emptyState: AdminSettingsState = {
-    siteRes: this.isoData.site,
+    siteRes: this.isoData.site_res,
     siteConfigForm: {
       config_hjson: null,
-      auth: null,
+      auth: UserService.Instance.authField(),
     },
     siteConfigRes: {
       config_hjson: null,
@@ -67,13 +66,14 @@ export class AdminSettings extends Component<any, AdminSettingsState> {
       this.state.siteConfigLoading = false;
       this.state.loading = false;
     } else {
-      WebSocketService.Instance.getSiteConfig();
+      WebSocketService.Instance.client.getSiteConfig({
+        auth: UserService.Instance.authField(),
+      });
     }
   }
 
   static fetchInitialData(req: InitialFetchRequest): Promise<any>[] {
-    let form: GetSiteConfig = {};
-    setAuth(form, req.auth);
+    let form: GetSiteConfig = { auth: req.auth };
     return [req.client.getSiteConfig(form)];
   }
 
@@ -91,7 +91,9 @@ export class AdminSettings extends Component<any, AdminSettingsState> {
   }
 
   get documentTitle(): string {
-    return `${i18n.t('admin_settings')} - ${this.state.siteRes.site.name}`;
+    return `${i18n.t('admin_settings')} - ${
+      this.state.siteRes.site_view.site.name
+    }`;
   }
 
   render() {
@@ -110,8 +112,8 @@ export class AdminSettings extends Component<any, AdminSettingsState> {
         ) : (
           <div class="row">
             <div class="col-12 col-md-6">
-              {this.state.siteRes.site.id && (
-                <SiteForm site={this.state.siteRes.site} />
+              {this.state.siteRes.site_view.site.id && (
+                <SiteForm site={this.state.siteRes.site_view.site} />
               )}
               {this.admins()}
               {this.bannedUsers()}
@@ -130,16 +132,7 @@ export class AdminSettings extends Component<any, AdminSettingsState> {
         <ul class="list-unstyled">
           {this.state.siteRes.admins.map(admin => (
             <li class="list-inline-item">
-              <UserListing
-                user={{
-                  name: admin.name,
-                  preferred_username: admin.preferred_username,
-                  avatar: admin.avatar,
-                  id: admin.id,
-                  local: admin.local,
-                  actor_id: admin.actor_id,
-                }}
-              />
+              <UserListing user={admin.user} />
             </li>
           ))}
         </ul>
@@ -154,16 +147,7 @@ export class AdminSettings extends Component<any, AdminSettingsState> {
         <ul class="list-unstyled">
           {this.state.siteRes.banned.map(banned => (
             <li class="list-inline-item">
-              <UserListing
-                user={{
-                  name: banned.name,
-                  preferred_username: banned.preferred_username,
-                  avatar: banned.avatar,
-                  id: banned.id,
-                  local: banned.local,
-                  actor_id: banned.actor_id,
-                }}
-              />
+              <UserListing user={banned.user} />
             </li>
           ))}
         </ul>
@@ -214,7 +198,7 @@ export class AdminSettings extends Component<any, AdminSettingsState> {
   handleSiteConfigSubmit(i: AdminSettings, event: any) {
     event.preventDefault();
     i.state.siteConfigLoading = true;
-    WebSocketService.Instance.saveSiteConfig(i.state.siteConfigForm);
+    WebSocketService.Instance.client.saveSiteConfig(i.state.siteConfigForm);
     i.setState(i.state);
   }
 
@@ -223,9 +207,8 @@ export class AdminSettings extends Component<any, AdminSettingsState> {
     i.setState(i.state);
   }
 
-  parseMessage(msg: WebSocketJsonResponse) {
-    console.log(msg);
-    let res = wsJsonToRes(msg);
+  parseMessage(msg: any) {
+    let op = wsUserOp(msg);
     if (msg.error) {
       toast(i18n.t(msg.error), 'danger');
       this.context.router.history.push('/');
@@ -233,21 +216,21 @@ export class AdminSettings extends Component<any, AdminSettingsState> {
       this.setState(this.state);
       return;
     } else if (msg.reconnect) {
-    } else if (res.op == UserOperation.EditSite) {
-      let data = res.data as SiteResponse;
-      this.state.siteRes.site = data.site;
+    } else if (op == UserOperation.EditSite) {
+      let data = wsJsonToRes<SiteResponse>(msg).data;
+      this.state.siteRes.site_view = data.site_view;
       this.setState(this.state);
       toast(i18n.t('site_saved'));
-    } else if (res.op == UserOperation.GetSiteConfig) {
-      let data = res.data as GetSiteConfigResponse;
+    } else if (op == UserOperation.GetSiteConfig) {
+      let data = wsJsonToRes<GetSiteConfigResponse>(msg).data;
       this.state.siteConfigRes = data;
       this.state.loading = false;
       this.state.siteConfigForm.config_hjson = this.state.siteConfigRes.config_hjson;
       this.setState(this.state);
       var textarea: any = document.getElementById(this.siteConfigTextAreaId);
       autosize(textarea);
-    } else if (res.op == UserOperation.SaveSiteConfig) {
-      let data = res.data as GetSiteConfigResponse;
+    } else if (op == UserOperation.SaveSiteConfig) {
+      let data = wsJsonToRes<GetSiteConfigResponse>(msg).data;
       this.state.siteConfigRes = data;
       this.state.siteConfigForm.config_hjson = this.state.siteConfigRes.config_hjson;
       this.state.siteConfigLoading = false;
diff --git a/src/shared/components/app.tsx b/src/shared/components/app.tsx
index 6594645..26a2886 100644
--- a/src/shared/components/app.tsx
+++ b/src/shared/components/app.tsx
@@ -13,7 +13,7 @@ import { GetSiteResponse } from 'lemmy-js-client';
 import './styles.scss';
 
 export interface AppProps {
-  site: GetSiteResponse;
+  siteRes: GetSiteResponse;
 }
 
 export class App extends Component<AppProps, any> {
@@ -21,24 +21,25 @@ export class App extends Component<AppProps, any> {
     super(props, context);
   }
   render() {
+    let siteRes = this.props.siteRes;
     return (
       <>
         <Provider i18next={i18n}>
           <div>
-            <Theme user={this.props.site.my_user} />
-            {this.props.site &&
-              this.props.site.site &&
-              this.props.site.site.icon && (
+            <Theme user={siteRes.my_user} />
+            {siteRes &&
+              siteRes.site_view.site &&
+              this.props.siteRes.site_view.site.icon && (
                 <Helmet>
                   <link
                     id="favicon"
                     rel="icon"
                     type="image/x-icon"
-                    href={this.props.site.site.icon}
+                    href={this.props.siteRes.site_view.site.icon}
                   />
                 </Helmet>
               )}
-            <Navbar site={this.props.site} />
+            <Navbar site_res={this.props.siteRes} />
             <div class="mt-4 p-0 fl-1">
               <Switch>
                 {routes.map(({ path, exact, component: C, ...rest }) => (
@@ -53,7 +54,7 @@ export class App extends Component<AppProps, any> {
               </Switch>
               <Symbols />
             </div>
-            <Footer site={this.props.site} />
+            <Footer site={this.props.siteRes} />
           </div>
         </Provider>
       </>
diff --git a/src/shared/components/comment-form.tsx b/src/shared/components/comment-form.tsx
index b032a06..5a0d404 100644
--- a/src/shared/components/comment-form.tsx
+++ b/src/shared/components/comment-form.tsx
@@ -2,13 +2,18 @@ import { Component } from 'inferno';
 import { Link } from 'inferno-router';
 import { Subscription } from 'rxjs';
 import {
-  CommentNode as CommentNodeI,
-  CommentForm as CommentFormI,
-  WebSocketJsonResponse,
+  CreateComment,
+  EditComment,
   UserOperation,
   CommentResponse,
 } from 'lemmy-js-client';
-import { capitalizeFirstLetter, wsJsonToRes, wsSubscribe } from '../utils';
+import { CommentNode as CommentNodeI } from '../interfaces';
+import {
+  capitalizeFirstLetter,
+  wsJsonToRes,
+  wsSubscribe,
+  wsUserOp,
+} from '../utils';
 import { WebSocketService, UserService } from '../services';
 import { i18n } from '../i18next';
 import { T } from 'inferno-i18next';
@@ -24,30 +29,21 @@ interface CommentFormProps {
 }
 
 interface CommentFormState {
-  commentForm: CommentFormI;
   buttonTitle: string;
   finished: boolean;
+  formId: string;
 }
 
 export class CommentForm extends Component<CommentFormProps, CommentFormState> {
   private subscription: Subscription;
   private emptyState: CommentFormState = {
-    commentForm: {
-      auth: null,
-      content: null,
-      post_id: this.props.node
-        ? this.props.node.comment.post_id
-        : this.props.postId,
-      creator_id: UserService.Instance.user
-        ? UserService.Instance.user.id
-        : null,
-    },
     buttonTitle: !this.props.node
       ? capitalizeFirstLetter(i18n.t('post'))
       : this.props.edit
       ? capitalizeFirstLetter(i18n.t('save'))
       : capitalizeFirstLetter(i18n.t('reply')),
     finished: false,
+    formId: null,
   };
 
   constructor(props: any, context: any) {
@@ -58,18 +54,6 @@ export class CommentForm extends Component<CommentFormProps, CommentFormState> {
 
     this.state = this.emptyState;
 
-    if (this.props.node) {
-      if (this.props.edit) {
-        this.state.commentForm.edit_id = this.props.node.comment.id;
-        this.state.commentForm.parent_id = this.props.node.comment.parent_id;
-        this.state.commentForm.content = this.props.node.comment.content;
-        this.state.commentForm.creator_id = this.props.node.comment.creator_id;
-      } else {
-        // A reply gets a new parent id
-        this.state.commentForm.parent_id = this.props.node.comment.id;
-      }
-    }
-
     this.parseMessage = this.parseMessage.bind(this);
     this.subscription = wsSubscribe(this.parseMessage);
   }
@@ -83,7 +67,11 @@ export class CommentForm extends Component<CommentFormProps, CommentFormState> {
       <div class="mb-3">
         {UserService.Instance.user ? (
           <MarkdownTextArea
-            initialContent={this.state.commentForm.content}
+            initialContent={
+              this.props.node
+                ? this.props.node.comment_view.comment.content
+                : null
+            }
             buttonTitle={this.state.buttonTitle}
             finished={this.state.finished}
             replyType={!!this.props.node}
@@ -110,12 +98,28 @@ export class CommentForm extends Component<CommentFormProps, CommentFormState> {
   }
 
   handleCommentSubmit(msg: { val: string; formId: string }) {
-    this.state.commentForm.content = msg.val;
-    this.state.commentForm.form_id = msg.formId;
+    let content = msg.val;
+    this.state.formId = msg.formId;
+
+    let node = this.props.node;
+
     if (this.props.edit) {
-      WebSocketService.Instance.editComment(this.state.commentForm);
+      let form: EditComment = {
+        content,
+        form_id: this.state.formId,
+        edit_id: node.comment_view.comment.id,
+        auth: UserService.Instance.authField(),
+      };
+      WebSocketService.Instance.client.editComment(form);
     } else {
-      WebSocketService.Instance.createComment(this.state.commentForm);
+      let form: CreateComment = {
+        content,
+        form_id: this.state.formId,
+        post_id: node ? node.comment_view.post.id : this.props.postId,
+        parent_id: node ? node.comment_view.comment.parent_id : null,
+        auth: UserService.Instance.authField(),
+      };
+      WebSocketService.Instance.client.createComment(form);
     }
     this.setState(this.state);
   }
@@ -124,22 +128,22 @@ export class CommentForm extends Component<CommentFormProps, CommentFormState> {
     this.props.onReplyCancel();
   }
 
-  parseMessage(msg: WebSocketJsonResponse) {
-    let res = wsJsonToRes(msg);
+  parseMessage(msg: any) {
+    let op = wsUserOp(msg);
 
     // Only do the showing and hiding if logged in
     if (UserService.Instance.user) {
       if (
-        res.op == UserOperation.CreateComment ||
-        res.op == UserOperation.EditComment
+        op == UserOperation.CreateComment ||
+        op == UserOperation.EditComment
       ) {
-        let data = res.data as CommentResponse;
+        let data = wsJsonToRes<CommentResponse>(msg).data;
 
         // This only finishes this form, if the randomly generated form_id matches the one received
-        if (this.state.commentForm.form_id == data.form_id) {
+        if (this.state.formId == data.form_id) {
           this.setState({ finished: true });
 
-          // Necessary because it broke tribute for some reaso
+          // Necessary because it broke tribute for some reason
           this.setState({ finished: false });
         }
       }
diff --git a/src/shared/components/comment-node.tsx b/src/shared/components/comment-node.tsx
index 030515c..3d69812 100644
--- a/src/shared/components/comment-node.tsx
+++ b/src/shared/components/comment-node.tsx
@@ -1,24 +1,29 @@
 import { Component, linkEvent } from 'inferno';
 import { Link } from 'inferno-router';
 import {
-  CommentNode as CommentNodeI,
-  CommentLikeForm,
-  DeleteCommentForm,
-  RemoveCommentForm,
-  MarkCommentAsReadForm,
-  MarkUserMentionAsReadForm,
-  SaveCommentForm,
-  BanFromCommunityForm,
-  BanUserForm,
-  CommunityUser,
-  UserView,
-  AddModToCommunityForm,
-  AddAdminForm,
-  TransferCommunityForm,
-  TransferSiteForm,
+  CreateCommentLike,
+  DeleteComment,
+  RemoveComment,
+  MarkCommentAsRead,
+  MarkUserMentionAsRead,
+  SaveComment,
+  BanFromCommunity,
+  BanUser,
+  CommunityModeratorView,
+  UserViewSafe,
+  AddModToCommunity,
+  AddAdmin,
+  TransferCommunity,
+  TransferSite,
   SortType,
+  CommentView,
+  UserMentionView,
 } from 'lemmy-js-client';
-import { CommentSortType, BanType } from '../interfaces';
+import {
+  CommentSortType,
+  CommentNode as CommentNodeI,
+  BanType,
+} from '../interfaces';
 import { WebSocketService, UserService } from '../services';
 import {
   mdToHtml,
@@ -70,8 +75,8 @@ interface CommentNodeProps {
   locked?: boolean;
   markable?: boolean;
   showContext?: boolean;
-  moderators: CommunityUser[];
-  admins: UserView[];
+  moderators: CommunityModeratorView[];
+  admins: UserViewSafe[];
   // TODO is this necessary, can't I get it from the node itself?
   postCreatorId?: number;
   showCommunity?: boolean;
@@ -98,12 +103,12 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
     showConfirmTransferCommunity: false,
     showConfirmAppointAsMod: false,
     showConfirmAppointAsAdmin: false,
-    my_vote: this.props.node.comment.my_vote,
-    score: this.props.node.comment.score,
-    upvotes: this.props.node.comment.upvotes,
-    downvotes: this.props.node.comment.downvotes,
-    borderColor: this.props.node.comment.depth
-      ? colorList[this.props.node.comment.depth % colorList.length]
+    my_vote: this.props.node.comment_view.my_vote,
+    score: this.props.node.comment_view.counts.score,
+    upvotes: this.props.node.comment_view.counts.upvotes,
+    downvotes: this.props.node.comment_view.counts.downvotes,
+    borderColor: this.props.node.depth
+      ? colorList[this.props.node.depth % colorList.length]
       : colorList[0],
     readLoading: false,
     saveLoading: false,
@@ -118,11 +123,13 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
     this.handleCommentDownvote = this.handleCommentDownvote.bind(this);
   }
 
+  // TODO see if there's a better way to do this, and all willReceiveProps
   componentWillReceiveProps(nextProps: CommentNodeProps) {
-    this.state.my_vote = nextProps.node.comment.my_vote;
-    this.state.upvotes = nextProps.node.comment.upvotes;
-    this.state.downvotes = nextProps.node.comment.downvotes;
-    this.state.score = nextProps.node.comment.score;
+    let cv = nextProps.node.comment_view;
+    this.state.my_vote = cv.my_vote;
+    this.state.upvotes = cv.counts.upvotes;
+    this.state.downvotes = cv.counts.downvotes;
+    this.state.score = cv.counts.score;
     this.state.readLoading = false;
     this.state.saveLoading = false;
     this.setState(this.state);
@@ -130,43 +137,30 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
 
   render() {
     let node = this.props.node;
+    let cv = this.props.node.comment_view;
     return (
       <div
         className={`comment ${
-          node.comment.parent_id && !this.props.noIndent ? 'ml-1' : ''
+          cv.comment.parent_id && !this.props.noIndent ? 'ml-1' : ''
         }`}
       >
         <div
-          id={`comment-${node.comment.id}`}
+          id={`comment-${cv.comment.id}`}
           className={`details comment-node py-2 ${
             !this.props.noBorder ? 'border-top border-light' : ''
           } ${this.isCommentNew ? 'mark' : ''}`}
           style={
             !this.props.noIndent &&
-            this.props.node.comment.parent_id &&
+            cv.comment.parent_id &&
             `border-left: 2px ${this.state.borderColor} solid !important`
           }
         >
           <div
-            class={`${
-              !this.props.noIndent &&
-              this.props.node.comment.parent_id &&
-              'ml-2'
-            }`}
+            class={`${!this.props.noIndent && cv.comment.parent_id && 'ml-2'}`}
           >
             <div class="d-flex flex-wrap align-items-center text-muted small">
               <span class="mr-2">
-                <UserListing
-                  user={{
-                    name: node.comment.creator_name,
-                    preferred_username: node.comment.creator_preferred_username,
-                    avatar: node.comment.creator_avatar,
-                    id: node.comment.creator_id,
-                    local: node.comment.creator_local,
-                    actor_id: node.comment.creator_actor_id,
-                    published: node.comment.creator_published,
-                  }}
-                />
+                <UserListing user={cv.creator} />
               </span>
 
               {this.isMod && (
@@ -184,7 +178,7 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
                   {i18n.t('creator')}
                 </div>
               )}
-              {(node.comment.banned_from_community || node.comment.banned) && (
+              {(cv.creator_banned_from_community || cv.creator.banned) && (
                 <div className="badge badge-danger mr-2">
                   {i18n.t('banned')}
                 </div>
@@ -192,18 +186,10 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
               {this.props.showCommunity && (
                 <>
                   <span class="mx-1">{i18n.t('to')}</span>
-                  <CommunityLink
-                    community={{
-                      name: node.comment.community_name,
-                      id: node.comment.community_id,
-                      local: node.comment.community_local,
-                      actor_id: node.comment.community_actor_id,
-                      icon: node.comment.community_icon,
-                    }}
-                  />
+                  <CommunityLink community={cv.community} />
                   <span class="mx-2">•</span>
-                  <Link className="mr-2" to={`/post/${node.comment.post_id}`}>
-                    {node.comment.post_name}
+                  <Link className="mr-2" to={`/post/${cv.post.id}`}>
+                    {cv.post.name}
                   </Link>
                 </>
               )}
@@ -224,7 +210,7 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
               </a>
               <span className="mr-1">•</span>
               <span>
-                <MomentTime data={node.comment} />
+                <MomentTime data={cv.comment} />
               </span>
             </div>
             {/* end of user row */}
@@ -256,7 +242,7 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
                       class="btn btn-link btn-animate text-muted"
                       onClick={linkEvent(this, this.handleMarkRead)}
                       data-tippy-content={
-                        node.comment.read
+                        cv.comment.read
                           ? i18n.t('mark_as_unread')
                           : i18n.t('mark_as_read')
                       }
@@ -266,7 +252,7 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
                       ) : (
                         <svg
                           class={`icon icon-inline ${
-                            node.comment.read && 'text-success'
+                            cv.comment.read && 'text-success'
                           }`}
                         >
                           <use xlinkHref="#icon-check"></use>
@@ -333,7 +319,7 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
                             <button class="btn btn-link btn-animate">
                               <Link
                                 className="text-muted"
-                                to={`/create_private_message/recipient/${node.comment.creator_id}`}
+                                to={`/create_private_message/recipient/${cv.creator.id}`}
                                 title={i18n.t('message').toLowerCase()}
                               >
                                 <svg class="icon">
@@ -350,9 +336,7 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
                               this.handleSaveCommentClick
                             )}
                             data-tippy-content={
-                              node.comment.saved
-                                ? i18n.t('unsave')
-                                : i18n.t('save')
+                              cv.saved ? i18n.t('unsave') : i18n.t('save')
                             }
                           >
                             {this.state.saveLoading ? (
@@ -360,7 +344,7 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
                             ) : (
                               <svg
                                 class={`icon icon-inline ${
-                                  node.comment.saved && 'text-warning'
+                                  cv.saved && 'text-warning'
                                 }`}
                               >
                                 <use xlinkHref="#icon-star"></use>
@@ -398,14 +382,14 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
                                   this.handleDeleteClick
                                 )}
                                 data-tippy-content={
-                                  !node.comment.deleted
+                                  !cv.comment.deleted
                                     ? i18n.t('delete')
                                     : i18n.t('restore')
                                 }
                               >
                                 <svg
                                   class={`icon icon-inline ${
-                                    node.comment.deleted && 'text-danger'
+                                    cv.comment.deleted && 'text-danger'
                                   }`}
                                 >
                                   <use xlinkHref="#icon-trash"></use>
@@ -416,7 +400,7 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
                           {/* Admins and mods can remove comments */}
                           {(this.canMod || this.canAdmin) && (
                             <>
-                              {!node.comment.removed ? (
+                              {!cv.comment.removed ? (
                                 <button
                                   class="btn btn-link btn-animate text-muted"
                                   onClick={linkEvent(
@@ -443,7 +427,7 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
                           {this.canMod && (
                             <>
                               {!this.isMod &&
-                                (!node.comment.banned_from_community ? (
+                                (!cv.creator_banned_from_community ? (
                                   <button
                                     class="btn btn-link btn-animate text-muted"
                                     onClick={linkEvent(
@@ -464,8 +448,8 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
                                     {i18n.t('unban')}
                                   </button>
                                 ))}
-                              {!node.comment.banned_from_community &&
-                                node.comment.creator_local &&
+                              {!cv.creator_banned_from_community &&
+                                cv.creator.local &&
                                 (!this.state.showConfirmAppointAsMod ? (
                                   <button
                                     class="btn btn-link btn-animate text-muted"
@@ -508,7 +492,7 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
                           {/* Community creators and admins can transfer community to another mod */}
                           {(this.amCommunityCreator || this.canAdmin) &&
                             this.isMod &&
-                            node.comment.creator_local &&
+                            cv.creator.local &&
                             (!this.state.showConfirmTransferCommunity ? (
                               <button
                                 class="btn btn-link btn-animate text-muted"
@@ -549,7 +533,7 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
                           {this.canAdmin && (
                             <>
                               {!this.isAdmin &&
-                                (!node.comment.banned ? (
+                                (!cv.creator.banned ? (
                                   <button
                                     class="btn btn-link btn-animate text-muted"
                                     onClick={linkEvent(
@@ -570,8 +554,8 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
                                     {i18n.t('unban_from_site')}
                                   </button>
                                 ))}
-                              {!node.comment.banned &&
-                                node.comment.creator_local &&
+                              {!cv.creator.banned &&
+                                cv.creator.local &&
                                 (!this.state.showConfirmAppointAsAdmin ? (
                                   <button
                                     class="btn btn-link btn-animate text-muted"
@@ -614,7 +598,7 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
                           {/* Site Creator can transfer to another admin */}
                           {this.amSiteCreator &&
                             this.isAdmin &&
-                            node.comment.creator_local &&
+                            cv.creator.local &&
                             (!this.state.showConfirmTransferSite ? (
                               <button
                                 class="btn btn-link btn-animate text-muted"
@@ -711,7 +695,7 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
             {/* </div> */}
             <div class="form-group row">
               <button type="submit" class="btn btn-secondary">
-                {i18n.t('ban')} {node.comment.creator_name}
+                {i18n.t('ban')} {cv.creator.name}
               </button>
             </div>
           </form>
@@ -743,11 +727,11 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
   }
 
   get linkBtn() {
-    let node = this.props.node;
+    let cv = this.props.node.comment_view;
     return (
       <Link
         className="btn btn-link btn-animate text-muted"
-        to={`/post/${node.comment.post_id}/comment/${node.comment.id}`}
+        to={`/post/${cv.post.id}/comment/${cv.comment.id}`}
         title={this.props.showContext ? i18n.t('show_context') : i18n.t('link')}
       >
         <svg class="icon icon-inline">
@@ -768,7 +752,7 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
   get myComment(): boolean {
     return (
       UserService.Instance.user &&
-      this.props.node.comment.creator_id == UserService.Instance.user.id
+      this.props.node.comment_view.creator.id == UserService.Instance.user.id
     );
   }
 
@@ -776,8 +760,8 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
     return (
       this.props.moderators &&
       isMod(
-        this.props.moderators.map(m => m.user_id),
-        this.props.node.comment.creator_id
+        this.props.moderators.map(m => m.moderator.id),
+        this.props.node.comment_view.creator.id
       )
     );
   }
@@ -786,26 +770,26 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
     return (
       this.props.admins &&
       isMod(
-        this.props.admins.map(a => a.id),
-        this.props.node.comment.creator_id
+        this.props.admins.map(a => a.user.id),
+        this.props.node.comment_view.creator.id
       )
     );
   }
 
   get isPostCreator(): boolean {
-    return this.props.node.comment.creator_id == this.props.postCreatorId;
+    return this.props.node.comment_view.creator.id == this.props.postCreatorId;
   }
 
   get canMod(): boolean {
     if (this.props.admins && this.props.moderators) {
       let adminsThenMods = this.props.admins
-        .map(a => a.id)
-        .concat(this.props.moderators.map(m => m.user_id));
+        .map(a => a.user.id)
+        .concat(this.props.moderators.map(m => m.moderator.id));
 
       return canMod(
         UserService.Instance.user,
         adminsThenMods,
-        this.props.node.comment.creator_id
+        this.props.node.comment_view.creator.id
       );
     } else {
       return false;
@@ -817,8 +801,8 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
       this.props.admins &&
       canMod(
         UserService.Instance.user,
-        this.props.admins.map(a => a.id),
-        this.props.node.comment.creator_id
+        this.props.admins.map(a => a.user.id),
+        this.props.node.comment_view.creator.id
       )
     );
   }
@@ -827,8 +811,8 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
     return (
       this.props.moderators &&
       UserService.Instance.user &&
-      this.props.node.comment.creator_id != UserService.Instance.user.id &&
-      UserService.Instance.user.id == this.props.moderators[0].user_id
+      this.props.node.comment_view.creator.id != UserService.Instance.user.id &&
+      UserService.Instance.user.id == this.props.moderators[0].moderator.id
     );
   }
 
@@ -836,18 +820,18 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
     return (
       this.props.admins &&
       UserService.Instance.user &&
-      this.props.node.comment.creator_id != UserService.Instance.user.id &&
-      UserService.Instance.user.id == this.props.admins[0].id
+      this.props.node.comment_view.creator.id != UserService.Instance.user.id &&
+      UserService.Instance.user.id == this.props.admins[0].user.id
     );
   }
 
   get commentUnlessRemoved(): string {
-    let node = this.props.node;
-    return node.comment.removed
+    let comment = this.props.node.comment_view.comment;
+    return comment.removed
       ? `*${i18n.t('removed')}*`
-      : node.comment.deleted
+      : comment.deleted
       ? `*${i18n.t('deleted')}*`
-      : node.comment.content;
+      : comment.content;
   }
 
   handleReplyClick(i: CommentNode) {
@@ -861,25 +845,25 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
   }
 
   handleDeleteClick(i: CommentNode) {
-    let deleteForm: DeleteCommentForm = {
-      edit_id: i.props.node.comment.id,
-      deleted: !i.props.node.comment.deleted,
-      auth: null,
+    let comment = i.props.node.comment_view.comment;
+    let deleteForm: DeleteComment = {
+      edit_id: comment.id,
+      deleted: !comment.deleted,
+      auth: UserService.Instance.authField(),
     };
-    WebSocketService.Instance.deleteComment(deleteForm);
+    WebSocketService.Instance.client.deleteComment(deleteForm);
   }
 
   handleSaveCommentClick(i: CommentNode) {
-    let saved =
-      i.props.node.comment.saved == undefined
-        ? true
-        : !i.props.node.comment.saved;
-    let form: SaveCommentForm = {
-      comment_id: i.props.node.comment.id,
-      save: saved,
+    let cv = i.props.node.comment_view;
+    let save = cv.saved == undefined ? true : !cv.saved;
+    let form: SaveComment = {
+      comment_id: cv.comment.id,
+      save,
+      auth: UserService.Instance.authField(),
     };
 
-    WebSocketService.Instance.saveComment(form);
+    WebSocketService.Instance.client.saveComment(form);
 
     i.state.saveLoading = true;
     i.setState(this.state);
@@ -908,12 +892,13 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
 
     this.state.my_vote = new_vote;
 
-    let form: CommentLikeForm = {
-      comment_id: i.comment.id,
+    let form: CreateCommentLike = {
+      comment_id: i.comment_view.comment.id,
       score: this.state.my_vote,
+      auth: UserService.Instance.authField(),
     };
 
-    WebSocketService.Instance.likeComment(form);
+    WebSocketService.Instance.client.likeComment(form);
     this.setState(this.state);
     setupTippy();
   }
@@ -935,12 +920,13 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
 
     this.state.my_vote = new_vote;
 
-    let form: CommentLikeForm = {
-      comment_id: i.comment.id,
+    let form: CreateCommentLike = {
+      comment_id: i.comment_view.comment.id,
       score: this.state.my_vote,
+      auth: UserService.Instance.authField(),
     };
 
-    WebSocketService.Instance.likeComment(form);
+    WebSocketService.Instance.client.likeComment(form);
     this.setState(this.state);
     setupTippy();
   }
@@ -961,34 +947,40 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
   }
 
   handleModRemoveSubmit(i: CommentNode) {
-    event.preventDefault();
-    let form: RemoveCommentForm = {
-      edit_id: i.props.node.comment.id,
-      removed: !i.props.node.comment.removed,
+    let comment = i.props.node.comment_view.comment;
+    let form: RemoveComment = {
+      edit_id: comment.id,
+      removed: !comment.removed,
       reason: i.state.removeReason,
-      auth: null,
+      auth: UserService.Instance.authField(),
     };
-    WebSocketService.Instance.removeComment(form);
+    WebSocketService.Instance.client.removeComment(form);
 
     i.state.showRemoveDialog = false;
     i.setState(i.state);
   }
 
+  isUserMentionType(
+    item: CommentView | UserMentionView
+  ): item is UserMentionView {
+    return (item as UserMentionView).user_mention.id !== undefined;
+  }
+
   handleMarkRead(i: CommentNode) {
-    // if it has a user_mention_id field, then its a mention
-    if (i.props.node.comment.user_mention_id) {
-      let form: MarkUserMentionAsReadForm = {
-        user_mention_id: i.props.node.comment.user_mention_id,
-        read: !i.props.node.comment.read,
+    if (i.isUserMentionType(i.props.node.comment_view)) {
+      let form: MarkUserMentionAsRead = {
+        user_mention_id: i.props.node.comment_view.user_mention.id,
+        read: !i.props.node.comment_view.user_mention.read,
+        auth: UserService.Instance.authField(),
       };
-      WebSocketService.Instance.markUserMentionAsRead(form);
+      WebSocketService.Instance.client.markUserMentionAsRead(form);
     } else {
-      let form: MarkCommentAsReadForm = {
-        edit_id: i.props.node.comment.id,
-        read: !i.props.node.comment.read,
-        auth: null,
+      let form: MarkCommentAsRead = {
+        comment_id: i.props.node.comment_view.comment.id,
+        read: !i.props.node.comment_view.comment.read,
+        auth: UserService.Instance.authField(),
       };
-      WebSocketService.Instance.markCommentAsRead(form);
+      WebSocketService.Instance.client.markCommentAsRead(form);
     }
 
     i.state.readLoading = true;
@@ -1030,37 +1022,39 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
   }
 
   handleModBanBothSubmit(i: CommentNode) {
-    event.preventDefault();
+    let cv = i.props.node.comment_view;
 
     if (i.state.banType == BanType.Community) {
       // If its an unban, restore all their data
-      let ban = !i.props.node.comment.banned_from_community;
+      let ban = !cv.creator_banned_from_community;
       if (ban == false) {
         i.state.removeData = false;
       }
-      let form: BanFromCommunityForm = {
-        user_id: i.props.node.comment.creator_id,
-        community_id: i.props.node.comment.community_id,
+      let form: BanFromCommunity = {
+        user_id: cv.creator.id,
+        community_id: cv.community.id,
         ban,
         remove_data: i.state.removeData,
         reason: i.state.banReason,
         expires: getUnixTime(i.state.banExpires),
+        auth: UserService.Instance.authField(),
       };
-      WebSocketService.Instance.banFromCommunity(form);
+      WebSocketService.Instance.client.banFromCommunity(form);
     } else {
       // If its an unban, restore all their data
-      let ban = !i.props.node.comment.banned;
+      let ban = !cv.creator.banned;
       if (ban == false) {
         i.state.removeData = false;
       }
-      let form: BanUserForm = {
-        user_id: i.props.node.comment.creator_id,
+      let form: BanUser = {
+        user_id: cv.creator.id,
         ban,
         remove_data: i.state.removeData,
         reason: i.state.banReason,
         expires: getUnixTime(i.state.banExpires),
+        auth: UserService.Instance.authField(),
       };
-      WebSocketService.Instance.banUser(form);
+      WebSocketService.Instance.client.banUser(form);
     }
 
     i.state.showBanDialog = false;
@@ -1078,12 +1072,14 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
   }
 
   handleAddModToCommunity(i: CommentNode) {
-    let form: AddModToCommunityForm = {
-      user_id: i.props.node.comment.creator_id,
-      community_id: i.props.node.comment.community_id,
+    let cv = i.props.node.comment_view;
+    let form: AddModToCommunity = {
+      user_id: cv.creator.id,
+      community_id: cv.community.id,
       added: !i.isMod,
+      auth: UserService.Instance.authField(),
     };
-    WebSocketService.Instance.addModToCommunity(form);
+    WebSocketService.Instance.client.addModToCommunity(form);
     i.state.showConfirmAppointAsMod = false;
     i.setState(i.state);
   }
@@ -1099,11 +1095,12 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
   }
 
   handleAddAdmin(i: CommentNode) {
-    let form: AddAdminForm = {
-      user_id: i.props.node.comment.creator_id,
+    let form: AddAdmin = {
+      user_id: i.props.node.comment_view.creator.id,
       added: !i.isAdmin,
+      auth: UserService.Instance.authField(),
     };
-    WebSocketService.Instance.addAdmin(form);
+    WebSocketService.Instance.client.addAdmin(form);
     i.state.showConfirmAppointAsAdmin = false;
     i.setState(i.state);
   }
@@ -1119,11 +1116,13 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
   }
 
   handleTransferCommunity(i: CommentNode) {
-    let form: TransferCommunityForm = {
-      community_id: i.props.node.comment.community_id,
-      user_id: i.props.node.comment.creator_id,
+    let cv = i.props.node.comment_view;
+    let form: TransferCommunity = {
+      community_id: cv.community.id,
+      user_id: cv.creator.id,
+      auth: UserService.Instance.authField(),
     };
-    WebSocketService.Instance.transferCommunity(form);
+    WebSocketService.Instance.client.transferCommunity(form);
     i.state.showConfirmTransferCommunity = false;
     i.setState(i.state);
   }
@@ -1139,17 +1138,18 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
   }
 
   handleTransferSite(i: CommentNode) {
-    let form: TransferSiteForm = {
-      user_id: i.props.node.comment.creator_id,
+    let form: TransferSite = {
+      user_id: i.props.node.comment_view.creator.id,
+      auth: UserService.Instance.authField(),
     };
-    WebSocketService.Instance.transferSite(form);
+    WebSocketService.Instance.client.transferSite(form);
     i.state.showConfirmTransferSite = false;
     i.setState(i.state);
   }
 
   get isCommentNew(): boolean {
     let now = moment.utc().subtract(10, 'minutes');
-    let then = moment.utc(this.props.node.comment.published);
+    let then = moment.utc(this.props.node.comment_view.comment.published);
     return now.isBefore(then);
   }
 
diff --git a/src/shared/components/comment-nodes.tsx b/src/shared/components/comment-nodes.tsx
index 3f99bf3..90a7783 100644
--- a/src/shared/components/comment-nodes.tsx
+++ b/src/shared/components/comment-nodes.tsx
@@ -1,9 +1,8 @@
 import { Component } from 'inferno';
-import { CommentSortType } from '../interfaces';
+import { CommentSortType, CommentNode as CommentNodeI } from '../interfaces';
 import {
-  CommentNode as CommentNodeI,
-  CommunityUser,
-  UserView,
+  CommunityModeratorView,
+  UserViewSafe,
   SortType,
 } from 'lemmy-js-client';
 import { commentSort, commentSortSortType } from '../utils';
@@ -13,8 +12,8 @@ interface CommentNodesState {}
 
 interface CommentNodesProps {
   nodes: CommentNodeI[];
-  moderators?: CommunityUser[];
-  admins?: UserView[];
+  moderators?: CommunityModeratorView[];
+  admins?: UserViewSafe[];
   postCreatorId?: number;
   noBorder?: boolean;
   noIndent?: boolean;
@@ -41,7 +40,7 @@ export class CommentNodes extends Component<
       <div className="comments">
         {this.sorter().map(node => (
           <CommentNode
-            key={node.comment.id}
+            key={node.comment_view.comment.id}
             node={node}
             noBorder={this.props.noBorder}
             noIndent={this.props.noIndent}
diff --git a/src/shared/components/communities.tsx b/src/shared/components/communities.tsx
index 523f9a9..148a413 100644
--- a/src/shared/components/communities.tsx
+++ b/src/shared/components/communities.tsx
@@ -3,24 +3,23 @@ import { HtmlTags } from './html-tags';
 import { Subscription } from 'rxjs';
 import {
   UserOperation,
-  Community,
+  CommunityView,
   ListCommunitiesResponse,
   CommunityResponse,
-  FollowCommunityForm,
-  ListCommunitiesForm,
+  FollowCommunity,
+  ListCommunities,
   SortType,
-  WebSocketJsonResponse,
-  Site,
+  SiteView,
 } from 'lemmy-js-client';
-import { WebSocketService } from '../services';
+import { UserService, WebSocketService } from '../services';
 import {
   wsJsonToRes,
   toast,
   getPageFromProps,
   isBrowser,
-  setAuth,
   setIsoData,
   wsSubscribe,
+  wsUserOp,
 } from '../utils';
 import { CommunityLink } from './community-link';
 import { i18n } from '../i18next';
@@ -29,10 +28,10 @@ import { InitialFetchRequest } from 'shared/interfaces';
 const communityLimit = 100;
 
 interface CommunitiesState {
-  communities: Community[];
+  communities: CommunityView[];
   page: number;
   loading: boolean;
-  site: Site;
+  site_view: SiteView;
 }
 
 interface CommunitiesProps {
@@ -46,7 +45,7 @@ export class Communities extends Component<any, CommunitiesState> {
     communities: [],
     loading: true,
     page: getPageFromProps(this.props),
-    site: this.isoData.site.site,
+    site_view: this.isoData.site_res.site_view,
   };
 
   constructor(props: any, context: any) {
@@ -60,7 +59,7 @@ export class Communities extends Component<any, CommunitiesState> {
     if (this.isoData.path == this.context.router.route.match.url) {
       this.state.communities = this.isoData.routeData[0].communities;
       this.state.communities.sort(
-        (a, b) => b.number_of_subscribers - a.number_of_subscribers
+        (a, b) => b.counts.subscribers - a.counts.subscribers
       );
       this.state.loading = false;
     } else {
@@ -88,7 +87,7 @@ export class Communities extends Component<any, CommunitiesState> {
   }
 
   get documentTitle(): string {
-    return `${i18n.t('communities')} - ${this.state.site.name}`;
+    return `${i18n.t('communities')} - ${this.state.site_view.site.name}`;
   }
 
   render() {
@@ -124,27 +123,25 @@ export class Communities extends Component<any, CommunitiesState> {
                   </tr>
                 </thead>
                 <tbody>
-                  {this.state.communities.map(community => (
+                  {this.state.communities.map(cv => (
                     <tr>
                       <td>
-                        <CommunityLink community={community} />
-                      </td>
-                      <td>{community.category_name}</td>
-                      <td class="text-right">
-                        {community.number_of_subscribers}
+                        <CommunityLink community={cv.community} />
                       </td>
+                      <td>{cv.category.name}</td>
+                      <td class="text-right">{cv.counts.subscribers}</td>
                       <td class="text-right d-none d-lg-table-cell">
-                        {community.number_of_posts}
+                        {cv.counts.posts}
                       </td>
                       <td class="text-right d-none d-lg-table-cell">
-                        {community.number_of_comments}
+                        {cv.counts.comments}
                       </td>
                       <td class="text-right">
-                        {community.subscribed ? (
+                        {cv.subscribed ? (
                           <span
                             class="pointer btn-link"
                             onClick={linkEvent(
-                              community.id,
+                              cv.community.id,
                               this.handleUnsubscribe
                             )}
                           >
@@ -154,7 +151,7 @@ export class Communities extends Component<any, CommunitiesState> {
                           <span
                             class="pointer btn-link"
                             onClick={linkEvent(
-                              community.id,
+                              cv.community.id,
                               this.handleSubscribe
                             )}
                           >
@@ -212,64 +209,68 @@ export class Communities extends Component<any, CommunitiesState> {
   }
 
   handleUnsubscribe(communityId: number) {
-    let form: FollowCommunityForm = {
+    let form: FollowCommunity = {
       community_id: communityId,
       follow: false,
+      auth: UserService.Instance.authField(),
     };
-    WebSocketService.Instance.followCommunity(form);
+    WebSocketService.Instance.client.followCommunity(form);
   }
 
   handleSubscribe(communityId: number) {
-    let form: FollowCommunityForm = {
+    let form: FollowCommunity = {
       community_id: communityId,
       follow: true,
+      auth: UserService.Instance.authField(),
     };
-    WebSocketService.Instance.followCommunity(form);
+    WebSocketService.Instance.client.followCommunity(form);
   }
 
   refetch() {
-    let listCommunitiesForm: ListCommunitiesForm = {
+    let listCommunitiesForm: ListCommunities = {
       sort: SortType.TopAll,
       limit: communityLimit,
       page: this.state.page,
+      auth: UserService.Instance.authField(false),
     };
 
-    WebSocketService.Instance.listCommunities(listCommunitiesForm);
+    WebSocketService.Instance.client.listCommunities(listCommunitiesForm);
   }
 
   static fetchInitialData(req: InitialFetchRequest): Promise<any>[] {
     let pathSplit = req.path.split('/');
     let page = pathSplit[3] ? Number(pathSplit[3]) : 1;
-    let listCommunitiesForm: ListCommunitiesForm = {
+    let listCommunitiesForm: ListCommunities = {
       sort: SortType.TopAll,
       limit: communityLimit,
       page,
+      auth: req.auth,
     };
-    setAuth(listCommunitiesForm, req.auth);
 
     return [req.client.listCommunities(listCommunitiesForm)];
   }
 
-  parseMessage(msg: WebSocketJsonResponse) {
-    console.log(msg);
-    let res = wsJsonToRes(msg);
+  parseMessage(msg: any) {
+    let op = wsUserOp(msg);
     if (msg.error) {
       toast(i18n.t(msg.error), 'danger');
       return;
-    } else if (res.op == UserOperation.ListCommunities) {
-      let data = res.data as ListCommunitiesResponse;
+    } else if (op == UserOperation.ListCommunities) {
+      let data = wsJsonToRes<ListCommunitiesResponse>(msg).data;
       this.state.communities = data.communities;
       this.state.communities.sort(
-        (a, b) => b.number_of_subscribers - a.number_of_subscribers
+        (a, b) => b.counts.subscribers - a.counts.subscribers
       );
       this.state.loading = false;
       window.scrollTo(0, 0);
       this.setState(this.state);
-    } else if (res.op == UserOperation.FollowCommunity) {
-      let data = res.data as CommunityResponse;
-      let found = this.state.communities.find(c => c.id == data.community.id);
-      found.subscribed = data.community.subscribed;
-      found.number_of_subscribers = data.community.number_of_subscribers;
+    } else if (op == UserOperation.FollowCommunity) {
+      let data = wsJsonToRes<CommunityResponse>(msg).data;
+      let found = this.state.communities.find(
+        c => c.community.id == data.community_view.community.id
+      );
+      found.subscribed = data.community_view.subscribed;
+      found.counts.subscribers = data.community_view.counts.subscribers;
       this.setState(this.state);
     }
   }
diff --git a/src/shared/components/community-form.tsx b/src/shared/components/community-form.tsx
index de7b6b2..4a3dad4 100644
--- a/src/shared/components/community-form.tsx
+++ b/src/shared/components/community-form.tsx
@@ -2,20 +2,21 @@ import { Component, linkEvent } from 'inferno';
 import { Prompt } from 'inferno-router';
 import { Subscription } from 'rxjs';
 import {
-  CommunityForm as CommunityFormI,
+  EditCommunity,
+  CreateCommunity,
   UserOperation,
   Category,
   CommunityResponse,
-  WebSocketJsonResponse,
-  Community,
+  CommunityView,
 } from 'lemmy-js-client';
-import { WebSocketService } from '../services';
+import { UserService, WebSocketService } from '../services';
 import {
   wsJsonToRes,
   capitalizeFirstLetter,
   toast,
   randomStr,
   wsSubscribe,
+  wsUserOp,
 } from '../utils';
 import { i18n } from '../i18next';
 
@@ -23,16 +24,16 @@ import { MarkdownTextArea } from './markdown-textarea';
 import { ImageUploadForm } from './image-upload-form';
 
 interface CommunityFormProps {
-  community?: Community; // If a community is given, that means this is an edit
+  community_view?: CommunityView; // If a community is given, that means this is an edit
   categories: Category[];
   onCancel?(): any;
-  onCreate?(community: Community): any;
-  onEdit?(community: Community): any;
+  onCreate?(community: CommunityView): any;
+  onEdit?(community: CommunityView): any;
   enableNsfw: boolean;
 }
 
 interface CommunityFormState {
-  communityForm: CommunityFormI;
+  communityForm: CreateCommunity;
   loading: boolean;
 }
 
@@ -51,6 +52,7 @@ export class CommunityForm extends Component<
       nsfw: false,
       icon: null,
       banner: null,
+      auth: UserService.Instance.authField(),
     },
     loading: false,
   };
@@ -70,17 +72,17 @@ export class CommunityForm extends Component<
     this.handleBannerUpload = this.handleBannerUpload.bind(this);
     this.handleBannerRemove = this.handleBannerRemove.bind(this);
 
-    if (this.props.community) {
+    let cv = this.props.community_view;
+    if (cv) {
       this.state.communityForm = {
-        name: this.props.community.name,
-        title: this.props.community.title,
-        category_id: this.props.community.category_id,
-        description: this.props.community.description,
-        edit_id: this.props.community.id,
-        nsfw: this.props.community.nsfw,
-        icon: this.props.community.icon,
-        banner: this.props.community.banner,
-        auth: null,
+        name: cv.community.name,
+        title: cv.community.title,
+        category_id: cv.category.id,
+        description: cv.community.description,
+        nsfw: cv.community.nsfw,
+        icon: cv.community.icon,
+        banner: cv.community.banner,
+        auth: UserService.Instance.authField(),
       };
     }
 
@@ -88,6 +90,7 @@ export class CommunityForm extends Component<
     this.subscription = wsSubscribe(this.parseMessage);
   }
 
+  // TODO this should be checked out
   componentDidUpdate() {
     if (
       !this.state.loading &&
@@ -119,7 +122,7 @@ export class CommunityForm extends Component<
           message={i18n.t('block_leaving')}
         />
         <form onSubmit={linkEvent(this, this.handleCreateCommunitySubmit)}>
-          {!this.props.community && (
+          {!this.props.community_view && (
             <div class="form-group row">
               <label class="col-12 col-form-label" htmlFor="community-name">
                 {i18n.t('name')}
@@ -250,13 +253,13 @@ export class CommunityForm extends Component<
                   <svg class="icon icon-spinner spin">
                     <use xlinkHref="#icon-spinner"></use>
                   </svg>
-                ) : this.props.community ? (
+                ) : this.props.community_view ? (
                   capitalizeFirstLetter(i18n.t('save'))
                 ) : (
                   capitalizeFirstLetter(i18n.t('create'))
                 )}
               </button>
-              {this.props.community && (
+              {this.props.community_view && (
                 <button
                   type="button"
                   class="btn btn-secondary"
@@ -275,10 +278,14 @@ export class CommunityForm extends Component<
   handleCreateCommunitySubmit(i: CommunityForm, event: any) {
     event.preventDefault();
     i.state.loading = true;
-    if (i.props.community) {
-      WebSocketService.Instance.editCommunity(i.state.communityForm);
+    if (i.props.community_view) {
+      let form: EditCommunity = {
+        ...i.state.communityForm,
+        edit_id: i.props.community_view.community.id,
+      };
+      WebSocketService.Instance.client.editCommunity(form);
     } else {
-      WebSocketService.Instance.createCommunity(i.state.communityForm);
+      WebSocketService.Instance.client.createCommunity(i.state.communityForm);
     }
     i.setState(i.state);
   }
@@ -332,22 +339,21 @@ export class CommunityForm extends Component<
     this.setState(this.state);
   }
 
-  parseMessage(msg: WebSocketJsonResponse) {
-    let res = wsJsonToRes(msg);
-    console.log(msg);
+  parseMessage(msg: any) {
+    let op = wsUserOp(msg);
     if (msg.error) {
       toast(i18n.t(msg.error), 'danger');
       this.state.loading = false;
       this.setState(this.state);
       return;
-    } else if (res.op == UserOperation.CreateCommunity) {
-      let data = res.data as CommunityResponse;
+    } else if (op == UserOperation.CreateCommunity) {
+      let data = wsJsonToRes<CommunityResponse>(msg).data;
       this.state.loading = false;
-      this.props.onCreate(data.community);
-    } else if (res.op == UserOperation.EditCommunity) {
-      let data = res.data as CommunityResponse;
+      this.props.onCreate(data.community_view);
+    } else if (op == UserOperation.EditCommunity) {
+      let data = wsJsonToRes<CommunityResponse>(msg).data;
       this.state.loading = false;
-      this.props.onEdit(data.community);
+      this.props.onEdit(data.community_view);
     }
   }
 }
diff --git a/src/shared/components/community-link.tsx b/src/shared/components/community-link.tsx
index 8bf73ca..b8f3bc0 100644
--- a/src/shared/components/community-link.tsx
+++ b/src/shared/components/community-link.tsx
@@ -1,19 +1,12 @@
 import { Component } from 'inferno';
 import { Link } from 'inferno-router';
-import { Community } from 'lemmy-js-client';
+import { CommunitySafe } from 'lemmy-js-client';
 import { hostname, showAvatars } from '../utils';
 import { PictrsImage } from './pictrs-image';
 
-interface CommunityOther {
-  name: string;
-  id?: number; // Necessary if its federated
-  icon?: string;
-  local?: boolean;
-  actor_id?: string;
-}
-
 interface CommunityLinkProps {
-  community: Community | CommunityOther;
+  // TODO figure this out better
+  community: CommunitySafe;
   realLink?: boolean;
   useApubName?: boolean;
   muted?: boolean;
diff --git a/src/shared/components/community.tsx b/src/shared/components/community.tsx
index c7d0a27..aa0cbf0 100644
--- a/src/shared/components/community.tsx
+++ b/src/shared/components/community.tsx
@@ -6,19 +6,18 @@ import {
   GetCommunityResponse,
   CommunityResponse,
   SortType,
-  Post,
-  GetPostsForm,
-  GetCommunityForm,
+  PostView,
+  GetPosts,
+  GetCommunity,
   ListingType,
   GetPostsResponse,
   PostResponse,
   AddModToCommunityResponse,
   BanFromCommunityResponse,
-  Comment,
-  GetCommentsForm,
+  CommentView,
+  GetComments,
   GetCommentsResponse,
   CommentResponse,
-  WebSocketJsonResponse,
   GetSiteResponse,
   Category,
   ListCategoriesResponse,
@@ -50,8 +49,8 @@ import {
   setIsoData,
   wsSubscribe,
   isBrowser,
-  setAuth,
   communityRSSUrl,
+  wsUserOp,
 } from '../utils';
 import { i18n } from '../i18next';
 
@@ -63,8 +62,8 @@ interface State {
   communityLoading: boolean;
   postsLoading: boolean;
   commentsLoading: boolean;
-  posts: Post[];
-  comments: Comment[];
+  posts: PostView[];
+  comments: CommentView[];
   dataType: DataType;
   sort: SortType;
   page: number;
@@ -98,7 +97,7 @@ export class Community extends Component<any, State> {
     dataType: getDataTypeFromProps(this.props),
     sort: getSortTypeFromProps(this.props),
     page: getPageFromProps(this.props),
-    siteRes: this.isoData.site,
+    siteRes: this.isoData.site_res,
     categories: [],
   };
 
@@ -127,17 +126,18 @@ export class Community extends Component<any, State> {
     } else {
       this.fetchCommunity();
       this.fetchData();
-      WebSocketService.Instance.listCategories();
+      WebSocketService.Instance.client.listCategories();
     }
     setupTippy();
   }
 
   fetchCommunity() {
-    let form: GetCommunityForm = {
+    let form: GetCommunity = {
       id: this.state.communityId ? this.state.communityId : null,
       name: this.state.communityName ? this.state.communityName : null,
+      auth: UserService.Instance.authField(false),
     };
-    WebSocketService.Instance.getCommunity(form);
+    WebSocketService.Instance.client.getCommunity(form);
   }
 
   componentWillUnmount() {
@@ -169,8 +169,8 @@ export class Community extends Component<any, State> {
       id = Number(idOrName);
     }
 
-    let communityForm: GetCommunityForm = id ? { id } : { name: name_ };
-    setAuth(communityForm, req.auth);
+    let communityForm: GetCommunity = id ? { id } : { name: name_ };
+    communityForm.auth = req.auth;
     promises.push(req.client.getCommunity(communityForm));
 
     let dataType: DataType = pathSplit[4]
@@ -186,24 +186,24 @@ export class Community extends Component<any, State> {
     let page = pathSplit[8] ? Number(pathSplit[8]) : 1;
 
     if (dataType == DataType.Post) {
-      let getPostsForm: GetPostsForm = {
+      let getPostsForm: GetPosts = {
         page,
         limit: fetchLimit,
         sort,
         type_: ListingType.Community,
+        auth: req.auth,
       };
       this.setIdOrName(getPostsForm, id, name_);
-      setAuth(getPostsForm, req.auth);
       promises.push(req.client.getPosts(getPostsForm));
     } else {
-      let getCommentsForm: GetCommentsForm = {
+      let getCommentsForm: GetComments = {
         page,
         limit: fetchLimit,
         sort,
         type_: ListingType.Community,
+        auth: req.auth,
       };
       this.setIdOrName(getCommentsForm, id, name_);
-      setAuth(getCommentsForm, req.auth);
       promises.push(req.client.getComments(getCommentsForm));
     }
 
@@ -232,10 +232,11 @@ export class Community extends Component<any, State> {
   }
 
   get documentTitle(): string {
-    return `${this.state.communityRes.community.title} - ${this.state.siteRes.site.name}`;
+    return `${this.state.communityRes.community_view.community.title} - ${this.state.siteRes.site_view.site.name}`;
   }
 
   render() {
+    let cv = this.state.communityRes.community_view;
     return (
       <div class="container">
         {this.state.communityLoading ? (
@@ -250,8 +251,8 @@ export class Community extends Component<any, State> {
               <HtmlTags
                 title={this.documentTitle}
                 path={this.context.router.route.match.url}
-                description={this.state.communityRes.community.description}
-                image={this.state.communityRes.community.icon}
+                description={cv.community.description}
+                image={cv.community.icon}
               />
               {this.communityInfo()}
               {this.selects()}
@@ -260,11 +261,11 @@ export class Community extends Component<any, State> {
             </div>
             <div class="col-12 col-md-4">
               <Sidebar
-                community={this.state.communityRes.community}
+                community_view={cv}
                 moderators={this.state.communityRes.moderators}
                 admins={this.state.siteRes.admins}
                 online={this.state.communityRes.online}
-                enableNsfw={this.state.siteRes.site.enable_nsfw}
+                enableNsfw={this.state.siteRes.site_view.site.enable_nsfw}
                 categories={this.state.categories}
               />
             </div>
@@ -275,6 +276,7 @@ export class Community extends Component<any, State> {
   }
 
   listings() {
+    let site = this.state.siteRes.site_view.site;
     return this.state.dataType == DataType.Post ? (
       this.state.postsLoading ? (
         <h5>
@@ -287,8 +289,8 @@ export class Community extends Component<any, State> {
           posts={this.state.posts}
           removeDuplicates
           sort={this.state.sort}
-          enableDownvotes={this.state.siteRes.site.enable_downvotes}
-          enableNsfw={this.state.siteRes.site.enable_nsfw}
+          enableDownvotes={site.enable_downvotes}
+          enableNsfw={site.enable_nsfw}
         />
       )
     ) : this.state.commentsLoading ? (
@@ -303,21 +305,19 @@ export class Community extends Component<any, State> {
         noIndent
         sortType={this.state.sort}
         showContext
-        enableDownvotes={this.state.siteRes.site.enable_downvotes}
+        enableDownvotes={site.enable_downvotes}
       />
     );
   }
 
   communityInfo() {
+    let community = this.state.communityRes.community_view.community;
     return (
       <div>
-        <BannerIconHeader
-          banner={this.state.communityRes.community.banner}
-          icon={this.state.communityRes.community.icon}
-        />
-        <h5 class="mb-0">{this.state.communityRes.community.title}</h5>
+        <BannerIconHeader banner={community.banner} icon={community.icon} />
+        <h5 class="mb-0">{community.title}</h5>
         <CommunityLink
-          community={this.state.communityRes.community}
+          community={community}
           realLink
           useApubName
           muted
@@ -342,7 +342,7 @@ export class Community extends Component<any, State> {
         </span>
         <a
           href={communityRSSUrl(
-            this.state.communityRes.community.actor_id,
+            this.state.communityRes.community_view.community.actor_id,
             this.state.sort
           )}
           target="_blank"
@@ -405,137 +405,141 @@ export class Community extends Component<any, State> {
     const sortStr = paramUpdates.sort || this.state.sort;
     const page = paramUpdates.page || this.state.page;
     this.props.history.push(
-      `/c/${this.state.communityRes.community.name}/data_type/${dataTypeStr}/sort/${sortStr}/page/${page}`
+      `/c/${this.state.communityRes.community_view.community.name}/data_type/${dataTypeStr}/sort/${sortStr}/page/${page}`
     );
   }
 
   fetchData() {
     if (this.state.dataType == DataType.Post) {
-      let getPostsForm: GetPostsForm = {
+      let form: GetPosts = {
         page: this.state.page,
         limit: fetchLimit,
         sort: this.state.sort,
         type_: ListingType.Community,
         community_id: this.state.communityId,
         community_name: this.state.communityName,
+        auth: UserService.Instance.authField(false),
       };
-      WebSocketService.Instance.getPosts(getPostsForm);
+      WebSocketService.Instance.client.getPosts(form);
     } else {
-      let getCommentsForm: GetCommentsForm = {
+      let form: GetComments = {
         page: this.state.page,
         limit: fetchLimit,
         sort: this.state.sort,
         type_: ListingType.Community,
         community_id: this.state.communityId,
         community_name: this.state.communityName,
+        auth: UserService.Instance.authField(false),
       };
-      WebSocketService.Instance.getComments(getCommentsForm);
+      WebSocketService.Instance.client.getComments(form);
     }
   }
 
-  parseMessage(msg: WebSocketJsonResponse) {
-    console.log(msg);
-    let res = wsJsonToRes(msg);
+  parseMessage(msg: any) {
+    let op = wsUserOp(msg);
     if (msg.error) {
       toast(i18n.t(msg.error), 'danger');
       this.context.router.history.push('/');
       return;
     } else if (msg.reconnect) {
-      WebSocketService.Instance.communityJoin({
-        community_id: this.state.communityRes.community.id,
+      WebSocketService.Instance.client.communityJoin({
+        community_id: this.state.communityRes.community_view.community.id,
       });
       this.fetchData();
-    } else if (res.op == UserOperation.GetCommunity) {
-      let data = res.data as GetCommunityResponse;
+    } else if (op == UserOperation.GetCommunity) {
+      let data = wsJsonToRes<GetCommunityResponse>(msg).data;
       this.state.communityRes = data;
       this.state.communityLoading = false;
       this.setState(this.state);
-      WebSocketService.Instance.communityJoin({
-        community_id: data.community.id,
+      // TODO why is there no auth in this form?
+      WebSocketService.Instance.client.communityJoin({
+        community_id: data.community_view.community.id,
       });
     } else if (
-      res.op == UserOperation.EditCommunity ||
-      res.op == UserOperation.DeleteCommunity ||
-      res.op == UserOperation.RemoveCommunity
+      op == UserOperation.EditCommunity ||
+      op == UserOperation.DeleteCommunity ||
+      op == UserOperation.RemoveCommunity
     ) {
-      let data = res.data as CommunityResponse;
-      this.state.communityRes.community = data.community;
+      let data = wsJsonToRes<CommunityResponse>(msg).data;
+      this.state.communityRes.community_view = data.community_view;
       this.setState(this.state);
-    } else if (res.op == UserOperation.FollowCommunity) {
-      let data = res.data as CommunityResponse;
-      this.state.communityRes.community.subscribed = data.community.subscribed;
-      this.state.communityRes.community.number_of_subscribers =
-        data.community.number_of_subscribers;
+    } 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;
       this.setState(this.state);
-    } else if (res.op == UserOperation.GetPosts) {
-      let data = res.data as GetPostsResponse;
+    } else if (op == UserOperation.GetPosts) {
+      let data = wsJsonToRes<GetPostsResponse>(msg).data;
       this.state.posts = data.posts;
       this.state.postsLoading = false;
       this.setState(this.state);
       setupTippy();
     } else if (
-      res.op == UserOperation.EditPost ||
-      res.op == UserOperation.DeletePost ||
-      res.op == UserOperation.RemovePost ||
-      res.op == UserOperation.LockPost ||
-      res.op == UserOperation.StickyPost ||
-      res.op == UserOperation.SavePost
+      op == UserOperation.EditPost ||
+      op == UserOperation.DeletePost ||
+      op == UserOperation.RemovePost ||
+      op == UserOperation.LockPost ||
+      op == UserOperation.StickyPost ||
+      op == UserOperation.SavePost
     ) {
-      let data = res.data as PostResponse;
-      editPostFindRes(data, this.state.posts);
+      let data = wsJsonToRes<PostResponse>(msg).data;
+      editPostFindRes(data.post_view, this.state.posts);
       this.setState(this.state);
-    } else if (res.op == UserOperation.CreatePost) {
-      let data = res.data as PostResponse;
-      this.state.posts.unshift(data.post);
-      notifyPost(data.post, this.context.router);
+    } else if (op == UserOperation.CreatePost) {
+      let data = wsJsonToRes<PostResponse>(msg).data;
+      this.state.posts.unshift(data.post_view);
+      notifyPost(data.post_view, this.context.router);
       this.setState(this.state);
-    } else if (res.op == UserOperation.CreatePostLike) {
-      let data = res.data as PostResponse;
-      createPostLikeFindRes(data, this.state.posts);
+    } else if (op == UserOperation.CreatePostLike) {
+      let data = wsJsonToRes<PostResponse>(msg).data;
+      createPostLikeFindRes(data.post_view, this.state.posts);
       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.communityRes.moderators = data.moderators;
       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;
 
+      // TODO this might be incorrect
       this.state.posts
-        .filter(p => p.creator_id == data.user.id)
-        .forEach(p => (p.banned = data.banned));
+        .filter(p => p.creator.id == data.user_view.user.id)
+        .forEach(p => (p.creator_banned_from_community = data.banned));
 
       this.setState(this.state);
-    } else if (res.op == UserOperation.GetComments) {
-      let data = res.data as GetCommentsResponse;
+    } else if (op == UserOperation.GetComments) {
+      let data = wsJsonToRes<GetCommentsResponse>(msg).data;
       this.state.comments = data.comments;
       this.state.commentsLoading = false;
       this.setState(this.state);
     } 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.comments);
+      let data = wsJsonToRes<CommentResponse>(msg).data;
+      editCommentRes(data.comment_view, this.state.comments);
       this.setState(this.state);
-    } else if (res.op == UserOperation.CreateComment) {
-      let data = res.data as CommentResponse;
+    } else if (op == UserOperation.CreateComment) {
+      let data = wsJsonToRes<CommentResponse>(msg).data;
 
       // Necessary since it might be a user reply
       if (data.recipient_ids.length == 0) {
-        this.state.comments.unshift(data.comment);
+        this.state.comments.unshift(data.comment_view);
         this.setState(this.state);
       }
-    } else if (res.op == UserOperation.SaveComment) {
-      let data = res.data as CommentResponse;
-      saveCommentRes(data, this.state.comments);
+    } else if (op == UserOperation.SaveComment) {
+      let data = wsJsonToRes<CommentResponse>(msg).data;
+      saveCommentRes(data.comment_view, this.state.comments);
       this.setState(this.state);
-    } else if (res.op == UserOperation.CreateCommentLike) {
-      let data = res.data as CommentResponse;
-      createCommentLikeRes(data, this.state.comments);
+    } else if (op == UserOperation.CreateCommentLike) {
+      let data = wsJsonToRes<CommentResponse>(msg).data;
+      createCommentLikeRes(data.comment_view, this.state.comments);
       this.setState(this.state);
-    } else if (res.op == UserOperation.ListCategories) {
-      let data = res.data as ListCategoriesResponse;
+    } else if (op == UserOperation.ListCategories) {
+      let data = wsJsonToRes<ListCategoriesResponse>(msg).data;
       this.state.categories = data.categories;
       this.setState(this.state);
     }
diff --git a/src/shared/components/create-community.tsx b/src/shared/components/create-community.tsx
index c96a826..b989183 100644
--- a/src/shared/components/create-community.tsx
+++ b/src/shared/components/create-community.tsx
@@ -3,10 +3,9 @@ import { Subscription } from 'rxjs';
 import { CommunityForm } from './community-form';
 import { HtmlTags } from './html-tags';
 import {
-  Community,
+  CommunityView,
   UserOperation,
-  WebSocketJsonResponse,
-  Site,
+  SiteView,
   ListCategoriesResponse,
   Category,
 } from 'lemmy-js-client';
@@ -16,13 +15,14 @@ import {
   wsJsonToRes,
   wsSubscribe,
   isBrowser,
+  wsUserOp,
 } from '../utils';
 import { WebSocketService, UserService } from '../services';
 import { i18n } from '../i18next';
 import { InitialFetchRequest } from 'shared/interfaces';
 
 interface CreateCommunityState {
-  site: Site;
+  site_view: SiteView;
   categories: Category[];
   loading: boolean;
 }
@@ -31,7 +31,7 @@ export class CreateCommunity extends Component<any, CreateCommunityState> {
   private isoData = setIsoData(this.context);
   private subscription: Subscription;
   private emptyState: CreateCommunityState = {
-    site: this.isoData.site.site,
+    site_view: this.isoData.site_res.site_view,
     categories: [],
     loading: true,
   };
@@ -53,7 +53,7 @@ export class CreateCommunity extends Component<any, CreateCommunityState> {
       this.state.categories = this.isoData.routeData[0].categories;
       this.state.loading = false;
     } else {
-      WebSocketService.Instance.listCategories();
+      WebSocketService.Instance.client.listCategories();
     }
   }
 
@@ -64,7 +64,7 @@ export class CreateCommunity extends Component<any, CreateCommunityState> {
   }
 
   get documentTitle(): string {
-    return `${i18n.t('create_community')} - ${this.state.site.name}`;
+    return `${i18n.t('create_community')} - ${this.state.site_view.site.name}`;
   }
 
   render() {
@@ -87,7 +87,7 @@ export class CreateCommunity extends Component<any, CreateCommunityState> {
               <CommunityForm
                 categories={this.state.categories}
                 onCreate={this.handleCommunityCreate}
-                enableNsfw={this.state.site.enable_nsfw}
+                enableNsfw={this.state.site_view.site.enable_nsfw}
               />
             </div>
           </div>
@@ -96,22 +96,21 @@ export class CreateCommunity extends Component<any, CreateCommunityState> {
     );
   }
 
-  handleCommunityCreate(community: Community) {
-    this.props.history.push(`/c/${community.name}`);
+  handleCommunityCreate(cv: CommunityView) {
+    this.props.history.push(`/c/${cv.community.name}`);
   }
 
   static fetchInitialData(req: InitialFetchRequest): Promise<any>[] {
     return [req.client.listCategories()];
   }
 
-  parseMessage(msg: WebSocketJsonResponse) {
-    console.log(msg);
-    let res = wsJsonToRes(msg);
+  parseMessage(msg: any) {
+    let op = wsUserOp(msg);
     if (msg.error) {
       // Toast errors are already handled by community-form
       return;
-    } else if (res.op == UserOperation.ListCategories) {
-      let data = res.data as ListCategoriesResponse;
+    } else if (op == UserOperation.ListCategories) {
+      let data = wsJsonToRes<ListCategoriesResponse>(msg).data;
       this.state.categories = data.categories;
       this.state.loading = false;
       this.setState(this.state);
diff --git a/src/shared/components/create-post.tsx b/src/shared/components/create-post.tsx
index a0eeb35..35fd6a8 100644
--- a/src/shared/components/create-post.tsx
+++ b/src/shared/components/create-post.tsx
@@ -4,29 +4,28 @@ import { PostForm } from './post-form';
 import { HtmlTags } from './html-tags';
 import {
   isBrowser,
-  setAuth,
   setIsoData,
   toast,
   wsJsonToRes,
   wsSubscribe,
+  wsUserOp,
 } from '../utils';
 import { UserService, WebSocketService } from '../services';
 import {
   UserOperation,
-  PostFormParams,
-  WebSocketJsonResponse,
   ListCommunitiesResponse,
-  Community,
-  Site,
-  ListCommunitiesForm,
+  CommunityView,
+  SiteView,
+  ListCommunities,
   SortType,
+  PostView,
 } from 'lemmy-js-client';
 import { i18n } from '../i18next';
-import { InitialFetchRequest } from 'shared/interfaces';
+import { InitialFetchRequest, PostFormParams } from 'shared/interfaces';
 
 interface CreatePostState {
-  site: Site;
-  communities: Community[];
+  site_view: SiteView;
+  communities: CommunityView[];
   loading: boolean;
 }
 
@@ -34,7 +33,7 @@ export class CreatePost extends Component<any, CreatePostState> {
   private isoData = setIsoData(this.context);
   private subscription: Subscription;
   private emptyState: CreatePostState = {
-    site: this.isoData.site.site,
+    site_view: this.isoData.site_res.site_view,
     communities: [],
     loading: true,
   };
@@ -62,11 +61,12 @@ export class CreatePost extends Component<any, CreatePostState> {
   }
 
   refetch() {
-    let listCommunitiesForm: ListCommunitiesForm = {
+    let listCommunitiesForm: ListCommunities = {
       sort: SortType.TopAll,
       limit: 9999,
+      auth: UserService.Instance.authField(false),
     };
-    WebSocketService.Instance.listCommunities(listCommunitiesForm);
+    WebSocketService.Instance.client.listCommunities(listCommunitiesForm);
   }
 
   componentWillUnmount() {
@@ -76,7 +76,7 @@ export class CreatePost extends Component<any, CreatePostState> {
   }
 
   get documentTitle(): string {
-    return `${i18n.t('create_post')} - ${this.state.site.name}`;
+    return `${i18n.t('create_post')} - ${this.state.site_view.site.name}`;
   }
 
   render() {
@@ -100,8 +100,8 @@ export class CreatePost extends Component<any, CreatePostState> {
                 communities={this.state.communities}
                 onCreate={this.handlePostCreate}
                 params={this.params}
-                enableDownvotes={this.state.site.enable_downvotes}
-                enableNsfw={this.state.site.enable_nsfw}
+                enableDownvotes={this.state.site_view.site.enable_downvotes}
+                enableNsfw={this.state.site_view.site.enable_nsfw}
               />
             </div>
           </div>
@@ -149,27 +149,26 @@ export class CreatePost extends Component<any, CreatePostState> {
     return null;
   }
 
-  handlePostCreate(id: number) {
-    this.props.history.push(`/post/${id}`);
+  handlePostCreate(post_view: PostView) {
+    this.props.history.push(`/post/${post_view.post.id}`);
   }
 
   static fetchInitialData(req: InitialFetchRequest): Promise<any>[] {
-    let listCommunitiesForm: ListCommunitiesForm = {
+    let listCommunitiesForm: ListCommunities = {
       sort: SortType.TopAll,
       limit: 9999,
+      auth: req.auth,
     };
-    setAuth(listCommunitiesForm, req.auth);
     return [req.client.listCommunities(listCommunitiesForm)];
   }
 
-  parseMessage(msg: WebSocketJsonResponse) {
-    console.log(msg);
-    let res = wsJsonToRes(msg);
+  parseMessage(msg: any) {
+    let op = wsUserOp(msg);
     if (msg.error) {
       toast(i18n.t(msg.error), 'danger');
       return;
-    } else if (res.op == UserOperation.ListCommunities) {
-      let data = res.data as ListCommunitiesResponse;
+    } else if (op == UserOperation.ListCommunities) {
+      let data = wsJsonToRes<ListCommunitiesResponse>(msg).data;
       this.state.communities = data.communities;
       this.state.loading = false;
       this.setState(this.state);
diff --git a/src/shared/components/create-private-message.tsx b/src/shared/components/create-private-message.tsx
index e08bc56..90e17c9 100644
--- a/src/shared/components/create-private-message.tsx
+++ b/src/shared/components/create-private-message.tsx
@@ -4,22 +4,21 @@ import { PrivateMessageForm } from './private-message-form';
 import { HtmlTags } from './html-tags';
 import { UserService, WebSocketService } from '../services';
 import {
-  Site,
-  WebSocketJsonResponse,
+  SiteView,
   UserOperation,
-  UserDetailsResponse,
-  UserView,
+  GetUserDetailsResponse,
+  UserViewSafe,
   SortType,
-  GetUserDetailsForm,
+  GetUserDetails,
 } from 'lemmy-js-client';
 import {
   getRecipientIdFromProps,
   isBrowser,
-  setAuth,
   setIsoData,
   toast,
   wsJsonToRes,
   wsSubscribe,
+  wsUserOp,
 } from '../utils';
 import { i18n } from '../i18next';
 import { InitialFetchRequest } from 'shared/interfaces';
@@ -27,8 +26,8 @@ import { InitialFetchRequest } from 'shared/interfaces';
 interface CreatePrivateMessageProps {}
 
 interface CreatePrivateMessageState {
-  site: Site;
-  recipient: UserView;
+  site_view: SiteView;
+  recipient: UserViewSafe;
   recipient_id: number;
   loading: boolean;
 }
@@ -40,7 +39,7 @@ export class CreatePrivateMessage extends Component<
   private isoData = setIsoData(this.context);
   private subscription: Subscription;
   private emptyState: CreatePrivateMessageState = {
-    site: this.isoData.site.site,
+    site_view: this.isoData.site_res.site_view,
     recipient: undefined,
     recipient_id: getRecipientIdFromProps(this.props),
     loading: true,
@@ -70,27 +69,30 @@ export class CreatePrivateMessage extends Component<
   }
 
   fetchUserDetails() {
-    let form: GetUserDetailsForm = {
+    let form: GetUserDetails = {
       user_id: this.state.recipient_id,
       sort: SortType.New,
       saved_only: false,
+      auth: UserService.Instance.authField(false),
     };
-    WebSocketService.Instance.getUserDetails(form);
+    WebSocketService.Instance.client.getUserDetails(form);
   }
 
   static fetchInitialData(req: InitialFetchRequest): Promise<any>[] {
     let user_id = Number(req.path.split('/').pop());
-    let form: GetUserDetailsForm = {
+    let form: GetUserDetails = {
       user_id,
       sort: SortType.New,
       saved_only: false,
+      auth: req.auth,
     };
-    setAuth(form, req.auth);
     return [req.client.getUserDetails(form)];
   }
 
   get documentTitle(): string {
-    return `${i18n.t('create_private_message')} - ${this.state.site.name}`;
+    return `${i18n.t('create_private_message')} - ${
+      this.state.site_view.site.name
+    }`;
   }
 
   componentWillUnmount() {
@@ -118,7 +120,7 @@ export class CreatePrivateMessage extends Component<
               <h5>{i18n.t('create_private_message')}</h5>
               <PrivateMessageForm
                 onCreate={this.handlePrivateMessageCreate}
-                recipient={this.state.recipient}
+                recipient={this.state.recipient.user}
               />
             </div>
           </div>
@@ -134,16 +136,16 @@ export class CreatePrivateMessage extends Component<
     this.context.router.history.push(`/`);
   }
 
-  parseMessage(msg: WebSocketJsonResponse) {
-    let res = wsJsonToRes(msg);
+  parseMessage(msg: any) {
+    let op = wsUserOp(msg);
     if (msg.error) {
       toast(i18n.t(msg.error), 'danger');
       this.state.loading = false;
       this.setState(this.state);
       return;
-    } else if (res.op == UserOperation.GetUserDetails) {
-      let data = res.data as UserDetailsResponse;
-      this.state.recipient = data.user;
+    } else if (op == UserOperation.GetUserDetails) {
+      let data = wsJsonToRes<GetUserDetailsResponse>(msg).data;
+      this.state.recipient = data.user_view;
       this.state.loading = false;
       this.setState(this.state);
     }
diff --git a/src/shared/components/inbox.tsx b/src/shared/components/inbox.tsx
index 04eb043..5d01fef 100644
--- a/src/shared/components/inbox.tsx
+++ b/src/shared/components/inbox.tsx
@@ -2,26 +2,25 @@ import { Component, linkEvent } from 'inferno';
 import { Subscription } from 'rxjs';
 import {
   UserOperation,
-  Comment,
+  CommentView,
   SortType,
-  GetRepliesForm,
+  GetReplies,
   GetRepliesResponse,
-  GetUserMentionsForm,
+  GetUserMentions,
   GetUserMentionsResponse,
   UserMentionResponse,
   CommentResponse,
-  WebSocketJsonResponse,
-  PrivateMessage as PrivateMessageI,
-  GetPrivateMessagesForm,
+  PrivateMessageView,
+  GetPrivateMessages,
   PrivateMessagesResponse,
   PrivateMessageResponse,
-  Site,
+  SiteView,
+  UserMentionView,
 } from 'lemmy-js-client';
 import { WebSocketService, UserService } from '../services';
 import {
   wsJsonToRes,
   fetchLimit,
-  isCommentType,
   toast,
   editCommentRes,
   saveCommentRes,
@@ -30,8 +29,8 @@ import {
   setupTippy,
   setIsoData,
   wsSubscribe,
-  setAuth,
   isBrowser,
+  wsUserOp,
 } from '../utils';
 import { CommentNodes } from './comment-nodes';
 import { PrivateMessage } from './private-message';
@@ -52,17 +51,27 @@ enum MessageType {
   Messages,
 }
 
-type ReplyType = Comment | PrivateMessageI;
+enum ReplyEnum {
+  Reply,
+  Mention,
+  Message,
+}
+type ReplyType = {
+  id: number;
+  type_: ReplyEnum;
+  view: CommentView | PrivateMessageView | UserMentionView;
+  published: string;
+};
 
 interface InboxState {
   unreadOrAll: UnreadOrAll;
   messageType: MessageType;
-  replies: Comment[];
-  mentions: Comment[];
-  messages: PrivateMessageI[];
+  replies: CommentView[];
+  mentions: UserMentionView[];
+  messages: PrivateMessageView[];
   sort: SortType;
   page: number;
-  site: Site;
+  site_view: SiteView;
   loading: boolean;
 }
 
@@ -77,7 +86,7 @@ export class Inbox extends Component<any, InboxState> {
     messages: [],
     sort: SortType.New,
     page: 1,
-    site: this.isoData.site.site,
+    site_view: this.isoData.site_res.site_view,
     loading: true,
   };
 
@@ -115,7 +124,7 @@ export class Inbox extends Component<any, InboxState> {
 
   get documentTitle(): string {
     return `@${UserService.Instance.user.name} ${i18n.t('inbox')} - ${
-      this.state.site.name
+      this.state.site_view.site.name
     }`;
   }
 
@@ -288,33 +297,71 @@ export class Inbox extends Component<any, InboxState> {
   }
 
   combined(): ReplyType[] {
-    return [
-      ...this.state.replies,
-      ...this.state.mentions,
-      ...this.state.messages,
-    ].sort((a, b) => b.published.localeCompare(a.published));
+    let id = 0;
+    let replies: ReplyType[] = this.state.replies.map(r => ({
+      id: id++,
+      type_: ReplyEnum.Reply,
+      view: r,
+      published: r.comment.published,
+    }));
+    let mentions: ReplyType[] = this.state.mentions.map(r => ({
+      id: id++,
+      type_: ReplyEnum.Mention,
+      view: r,
+      published: r.comment.published,
+    }));
+    let messages: ReplyType[] = this.state.messages.map(r => ({
+      id: id++,
+      type_: ReplyEnum.Message,
+      view: r,
+      published: r.private_message.published,
+    }));
+
+    return [...replies, ...mentions, ...messages].sort((a, b) =>
+      b.published.localeCompare(a.published)
+    );
+  }
+
+  renderReplyType(i: ReplyType) {
+    switch (i.type_) {
+      case ReplyEnum.Reply:
+        return (
+          <CommentNodes
+            key={i.id}
+            nodes={[{ comment_view: i.view as CommentView }]}
+            noIndent
+            markable
+            showCommunity
+            showContext
+            enableDownvotes={this.state.site_view.site.enable_downvotes}
+          />
+        );
+      case ReplyEnum.Mention:
+        return (
+          <CommentNodes
+            key={i.id}
+            nodes={[{ comment_view: i.view as UserMentionView }]}
+            noIndent
+            markable
+            showCommunity
+            showContext
+            enableDownvotes={this.state.site_view.site.enable_downvotes}
+          />
+        );
+      case ReplyEnum.Message:
+        return (
+          <PrivateMessage
+            key={i.id}
+            private_message_view={i.view as PrivateMessageView}
+          />
+        );
+      default:
+        return <div />;
+    }
   }
 
   all() {
-    return (
-      <div>
-        {this.combined().map(i =>
-          isCommentType(i) ? (
-            <CommentNodes
-              key={i.id}
-              nodes={[{ comment: i }]}
-              noIndent
-              markable
-              showCommunity
-              showContext
-              enableDownvotes={this.state.site.enable_downvotes}
-            />
-          ) : (
-            <PrivateMessage key={i.id} privateMessage={i} />
-          )
-        )}
-      </div>
-    );
+    return <div>{this.combined().map(i => this.renderReplyType(i))}</div>;
   }
 
   replies() {
@@ -326,7 +373,7 @@ export class Inbox extends Component<any, InboxState> {
           markable
           showCommunity
           showContext
-          enableDownvotes={this.state.site.enable_downvotes}
+          enableDownvotes={this.state.site_view.site.enable_downvotes}
         />
       </div>
     );
@@ -335,15 +382,15 @@ export class Inbox extends Component<any, InboxState> {
   mentions() {
     return (
       <div>
-        {this.state.mentions.map(mention => (
+        {this.state.mentions.map(umv => (
           <CommentNodes
-            key={mention.id}
-            nodes={[{ comment: mention }]}
+            key={umv.user_mention.id}
+            nodes={[{ comment_view: umv }]}
             noIndent
             markable
             showCommunity
             showContext
-            enableDownvotes={this.state.site.enable_downvotes}
+            enableDownvotes={this.state.site_view.site.enable_downvotes}
           />
         ))}
       </div>
@@ -353,8 +400,11 @@ export class Inbox extends Component<any, InboxState> {
   messages() {
     return (
       <div>
-        {this.state.messages.map(message => (
-          <PrivateMessage key={message.id} privateMessage={message} />
+        {this.state.messages.map(pmv => (
+          <PrivateMessage
+            key={pmv.private_message.id}
+            private_message_view={pmv}
+          />
         ))}
       </div>
     );
@@ -413,58 +463,61 @@ export class Inbox extends Component<any, InboxState> {
     let promises: Promise<any>[] = [];
 
     // It can be /u/me, or /username/1
-    let repliesForm: GetRepliesForm = {
+    let repliesForm: GetReplies = {
       sort: SortType.New,
       unread_only: true,
       page: 1,
       limit: fetchLimit,
+      auth: req.auth,
     };
-    setAuth(repliesForm, req.auth);
     promises.push(req.client.getReplies(repliesForm));
 
-    let userMentionsForm: GetUserMentionsForm = {
+    let userMentionsForm: GetUserMentions = {
       sort: SortType.New,
       unread_only: true,
       page: 1,
       limit: fetchLimit,
+      auth: req.auth,
     };
-    setAuth(userMentionsForm, req.auth);
     promises.push(req.client.getUserMentions(userMentionsForm));
 
-    let privateMessagesForm: GetPrivateMessagesForm = {
+    let privateMessagesForm: GetPrivateMessages = {
       unread_only: true,
       page: 1,
       limit: fetchLimit,
+      auth: req.auth,
     };
-    setAuth(privateMessagesForm, req.auth);
     promises.push(req.client.getPrivateMessages(privateMessagesForm));
 
     return promises;
   }
 
   refetch() {
-    let repliesForm: GetRepliesForm = {
+    let repliesForm: GetReplies = {
       sort: this.state.sort,
       unread_only: this.state.unreadOrAll == UnreadOrAll.Unread,
       page: this.state.page,
       limit: fetchLimit,
+      auth: UserService.Instance.authField(),
     };
-    WebSocketService.Instance.getReplies(repliesForm);
+    WebSocketService.Instance.client.getReplies(repliesForm);
 
-    let userMentionsForm: GetUserMentionsForm = {
+    let userMentionsForm: GetUserMentions = {
       sort: this.state.sort,
       unread_only: this.state.unreadOrAll == UnreadOrAll.Unread,
       page: this.state.page,
       limit: fetchLimit,
+      auth: UserService.Instance.authField(),
     };
-    WebSocketService.Instance.getUserMentions(userMentionsForm);
+    WebSocketService.Instance.client.getUserMentions(userMentionsForm);
 
-    let privateMessagesForm: GetPrivateMessagesForm = {
+    let privateMessagesForm: GetPrivateMessages = {
       unread_only: this.state.unreadOrAll == UnreadOrAll.Unread,
       page: this.state.page,
       limit: fetchLimit,
+      auth: UserService.Instance.authField(),
     };
-    WebSocketService.Instance.getPrivateMessages(privateMessagesForm);
+    WebSocketService.Instance.client.getPrivateMessages(privateMessagesForm);
   }
 
   handleSortChange(val: SortType) {
@@ -475,7 +528,9 @@ export class Inbox extends Component<any, InboxState> {
   }
 
   markAllAsRead(i: Inbox) {
-    WebSocketService.Instance.markAllAsRead();
+    WebSocketService.Instance.client.markAllAsRead({
+      auth: UserService.Instance.authField(),
+    });
     i.state.replies = [];
     i.state.mentions = [];
     i.state.messages = [];
@@ -484,148 +539,182 @@ export class Inbox extends Component<any, InboxState> {
     i.setState(i.state);
   }
 
-  parseMessage(msg: WebSocketJsonResponse) {
-    console.log(msg);
-    let res = wsJsonToRes(msg);
+  parseMessage(msg: any) {
+    let op = wsUserOp(msg);
     if (msg.error) {
       toast(i18n.t(msg.error), 'danger');
       return;
     } else if (msg.reconnect) {
       this.refetch();
-    } else if (res.op == UserOperation.GetReplies) {
-      let data = res.data as GetRepliesResponse;
+    } else if (op == UserOperation.GetReplies) {
+      let data = wsJsonToRes<GetRepliesResponse>(msg).data;
       this.state.replies = data.replies;
       this.state.loading = false;
       this.sendUnreadCount();
       window.scrollTo(0, 0);
       this.setState(this.state);
       setupTippy();
-    } else if (res.op == UserOperation.GetUserMentions) {
-      let data = res.data as GetUserMentionsResponse;
+    } else if (op == UserOperation.GetUserMentions) {
+      let data = wsJsonToRes<GetUserMentionsResponse>(msg).data;
       this.state.mentions = data.mentions;
       this.sendUnreadCount();
       window.scrollTo(0, 0);
       this.setState(this.state);
       setupTippy();
-    } else if (res.op == UserOperation.GetPrivateMessages) {
-      let data = res.data as PrivateMessagesResponse;
-      this.state.messages = data.messages;
+    } else if (op == UserOperation.GetPrivateMessages) {
+      let data = wsJsonToRes<PrivateMessagesResponse>(msg).data;
+      this.state.messages = data.private_messages;
       this.sendUnreadCount();
       window.scrollTo(0, 0);
       this.setState(this.state);
       setupTippy();
-    } else if (res.op == UserOperation.EditPrivateMessage) {
-      let data = res.data as PrivateMessageResponse;
-      let found: PrivateMessageI = this.state.messages.find(
-        m => m.id === data.message.id
+    } else if (op == UserOperation.EditPrivateMessage) {
+      let data = wsJsonToRes<PrivateMessageResponse>(msg).data;
+      let found: PrivateMessageView = this.state.messages.find(
+        m =>
+          m.private_message.id === data.private_message_view.private_message.id
       );
       if (found) {
-        found.content = data.message.content;
-        found.updated = data.message.updated;
+        found.private_message.content =
+          data.private_message_view.private_message.content;
+        found.private_message.updated =
+          data.private_message_view.private_message.updated;
       }
       this.setState(this.state);
-    } else if (res.op == UserOperation.DeletePrivateMessage) {
-      let data = res.data as PrivateMessageResponse;
-      let found: PrivateMessageI = this.state.messages.find(
-        m => m.id === data.message.id
+    } else if (op == UserOperation.DeletePrivateMessage) {
+      let data = wsJsonToRes<PrivateMessageResponse>(msg).data;
+      let found: PrivateMessageView = this.state.messages.find(
+        m =>
+          m.private_message.id === data.private_message_view.private_message.id
       );
       if (found) {
-        found.deleted = data.message.deleted;
-        found.updated = data.message.updated;
+        found.private_message.deleted =
+          data.private_message_view.private_message.deleted;
+        found.private_message.updated =
+          data.private_message_view.private_message.updated;
       }
       this.setState(this.state);
-    } else if (res.op == UserOperation.MarkPrivateMessageAsRead) {
-      let data = res.data as PrivateMessageResponse;
-      let found: PrivateMessageI = this.state.messages.find(
-        m => m.id === data.message.id
+    } else if (op == UserOperation.MarkPrivateMessageAsRead) {
+      let data = wsJsonToRes<PrivateMessageResponse>(msg).data;
+      let found: PrivateMessageView = this.state.messages.find(
+        m =>
+          m.private_message.id === data.private_message_view.private_message.id
       );
 
       if (found) {
-        found.updated = data.message.updated;
+        found.private_message.updated =
+          data.private_message_view.private_message.updated;
 
         // If youre in the unread view, just remove it from the list
-        if (this.state.unreadOrAll == UnreadOrAll.Unread && data.message.read) {
+        if (
+          this.state.unreadOrAll == UnreadOrAll.Unread &&
+          data.private_message_view.private_message.read
+        ) {
           this.state.messages = this.state.messages.filter(
-            r => r.id !== data.message.id
+            r =>
+              r.private_message.id !==
+              data.private_message_view.private_message.id
           );
         } else {
-          let found = this.state.messages.find(c => c.id == data.message.id);
-          found.read = data.message.read;
+          let found = this.state.messages.find(
+            c =>
+              c.private_message.id ==
+              data.private_message_view.private_message.id
+          );
+          found.private_message.read =
+            data.private_message_view.private_message.read;
         }
       }
       this.sendUnreadCount();
       this.setState(this.state);
-    } else if (res.op == UserOperation.MarkAllAsRead) {
+    } else if (op == UserOperation.MarkAllAsRead) {
       // Moved to be instant
     } 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.replies);
+      let data = wsJsonToRes<CommentResponse>(msg).data;
+      editCommentRes(data.comment_view, this.state.replies);
       this.setState(this.state);
-    } else if (res.op == UserOperation.MarkCommentAsRead) {
-      let data = res.data as CommentResponse;
+    } else if (op == UserOperation.MarkCommentAsRead) {
+      let data = wsJsonToRes<CommentResponse>(msg).data;
 
       // If youre in the unread view, just remove it from the list
-      if (this.state.unreadOrAll == UnreadOrAll.Unread && data.comment.read) {
+      if (
+        this.state.unreadOrAll == UnreadOrAll.Unread &&
+        data.comment_view.comment.read
+      ) {
         this.state.replies = this.state.replies.filter(
-          r => r.id !== data.comment.id
+          r => r.comment.id !== data.comment_view.comment.id
         );
       } else {
-        let found = this.state.replies.find(c => c.id == data.comment.id);
-        found.read = data.comment.read;
+        let found = this.state.replies.find(
+          c => c.comment.id == data.comment_view.comment.id
+        );
+        found.comment.read = data.comment_view.comment.read;
       }
       this.sendUnreadCount();
       this.setState(this.state);
       setupTippy();
-    } else if (res.op == UserOperation.MarkUserMentionAsRead) {
-      let data = res.data as UserMentionResponse;
-
-      let found = this.state.mentions.find(c => c.id == data.mention.id);
-      found.content = data.mention.content;
-      found.updated = data.mention.updated;
-      found.removed = data.mention.removed;
-      found.deleted = data.mention.deleted;
-      found.upvotes = data.mention.upvotes;
-      found.downvotes = data.mention.downvotes;
-      found.score = data.mention.score;
+    } else if (op == UserOperation.MarkUserMentionAsRead) {
+      let data = wsJsonToRes<UserMentionResponse>(msg).data;
+
+      // TODO this might not be correct, it might need to use the comment id
+      let found = this.state.mentions.find(
+        c => c.user_mention.id == data.user_mention_view.user_mention.id
+      );
+      found.comment.content = data.user_mention_view.comment.content;
+      found.comment.updated = data.user_mention_view.comment.updated;
+      found.comment.removed = data.user_mention_view.comment.removed;
+      found.comment.deleted = data.user_mention_view.comment.deleted;
+      found.counts.upvotes = data.user_mention_view.counts.upvotes;
+      found.counts.downvotes = data.user_mention_view.counts.downvotes;
+      found.counts.score = data.user_mention_view.counts.score;
 
       // If youre in the unread view, just remove it from the list
-      if (this.state.unreadOrAll == UnreadOrAll.Unread && data.mention.read) {
+      if (
+        this.state.unreadOrAll == UnreadOrAll.Unread &&
+        data.user_mention_view.user_mention.read
+      ) {
         this.state.mentions = this.state.mentions.filter(
-          r => r.id !== data.mention.id
+          r => r.user_mention.id !== data.user_mention_view.user_mention.id
         );
       } else {
-        let found = this.state.mentions.find(c => c.id == data.mention.id);
-        found.read = data.mention.read;
+        let found = this.state.mentions.find(
+          c => c.user_mention.id == data.user_mention_view.user_mention.id
+        );
+        // TODO test to make sure these mentions are getting marked as read
+        found.user_mention.read = data.user_mention_view.user_mention.read;
       }
       this.sendUnreadCount();
       this.setState(this.state);
-    } else if (res.op == UserOperation.CreateComment) {
-      let data = res.data as CommentResponse;
+    } else if (op == UserOperation.CreateComment) {
+      let data = wsJsonToRes<CommentResponse>(msg).data;
 
       if (data.recipient_ids.includes(UserService.Instance.user.id)) {
-        this.state.replies.unshift(data.comment);
+        this.state.replies.unshift(data.comment_view);
         this.setState(this.state);
-      } else if (data.comment.creator_id == UserService.Instance.user.id) {
+      } else if (data.comment_view.creator.id == UserService.Instance.user.id) {
+        // TODO this seems wrong, you should be using form_id
         toast(i18n.t('reply_sent'));
       }
-    } else if (res.op == UserOperation.CreatePrivateMessage) {
-      let data = res.data as PrivateMessageResponse;
-      if (data.message.recipient_id == UserService.Instance.user.id) {
-        this.state.messages.unshift(data.message);
+    } else if (op == UserOperation.CreatePrivateMessage) {
+      let data = wsJsonToRes<PrivateMessageResponse>(msg).data;
+      if (
+        data.private_message_view.recipient.id == UserService.Instance.user.id
+      ) {
+        this.state.messages.unshift(data.private_message_view);
         this.setState(this.state);
       }
-    } else if (res.op == UserOperation.SaveComment) {
-      let data = res.data as CommentResponse;
-      saveCommentRes(data, this.state.replies);
+    } else if (op == UserOperation.SaveComment) {
+      let data = wsJsonToRes<CommentResponse>(msg).data;
+      saveCommentRes(data.comment_view, this.state.replies);
       this.setState(this.state);
       setupTippy();
-    } else if (res.op == UserOperation.CreateCommentLike) {
-      let data = res.data as CommentResponse;
-      createCommentLikeRes(data, this.state.replies);
+    } else if (op == UserOperation.CreateCommentLike) {
+      let data = wsJsonToRes<CommentResponse>(msg).data;
+      createCommentLikeRes(data.comment_view, this.state.replies);
       this.setState(this.state);
     }
   }
@@ -636,13 +725,14 @@ export class Inbox extends Component<any, InboxState> {
 
   unreadCount(): number {
     return (
-      this.state.replies.filter(r => !r.read).length +
-      this.state.mentions.filter(r => !r.read).length +
+      this.state.replies.filter(r => !r.comment.read).length +
+      this.state.mentions.filter(r => !r.user_mention.read).length +
       this.state.messages.filter(
         r =>
           UserService.Instance.user &&
-          !r.read &&
-          r.creator_id !== UserService.Instance.user.id
+          !r.private_message.read &&
+          // TODO also seems very strang and wrong
+          r.creator.id !== UserService.Instance.user.id
       ).length
     );
   }
diff --git a/src/shared/components/instances.tsx b/src/shared/components/instances.tsx
index 7be27c7..85a3c45 100644
--- a/src/shared/components/instances.tsx
+++ b/src/shared/components/instances.tsx
@@ -11,7 +11,7 @@ interface InstancesState {
 export class Instances extends Component<any, InstancesState> {
   private isoData = setIsoData(this.context);
   private emptyState: InstancesState = {
-    siteRes: this.isoData.site,
+    siteRes: this.isoData.site_res,
   };
 
   constructor(props: any, context: any) {
@@ -20,7 +20,7 @@ export class Instances extends Component<any, InstancesState> {
   }
 
   get documentTitle(): string {
-    return `${i18n.t('instances')} - ${this.state.siteRes.site.name}`;
+    return `${i18n.t('instances')} - ${this.state.siteRes.site_view.site.name}`;
   }
 
   render() {
diff --git a/src/shared/components/login.tsx b/src/shared/components/login.tsx
index f3b0536..8ca9396 100644
--- a/src/shared/components/login.tsx
+++ b/src/shared/components/login.tsx
@@ -1,15 +1,14 @@
 import { Component, linkEvent } from 'inferno';
 import { Subscription } from 'rxjs';
 import {
-  LoginForm,
-  RegisterForm,
+  Login as LoginForm,
+  Register,
   LoginResponse,
   UserOperation,
-  PasswordResetForm,
+  PasswordReset,
   GetSiteResponse,
   GetCaptchaResponse,
-  WebSocketJsonResponse,
-  Site,
+  SiteView,
 } from 'lemmy-js-client';
 import { WebSocketService, UserService } from '../services';
 import {
@@ -19,18 +18,19 @@ import {
   wsSubscribe,
   isBrowser,
   setIsoData,
+  wsUserOp,
 } from '../utils';
 import { i18n } from '../i18next';
 import { HtmlTags } from './html-tags';
 
 interface State {
   loginForm: LoginForm;
-  registerForm: RegisterForm;
+  registerForm: Register;
   loginLoading: boolean;
   registerLoading: boolean;
   captcha: GetCaptchaResponse;
   captchaPlaying: boolean;
-  site: Site;
+  site_view: SiteView;
 }
 
 export class Login extends Component<any, State> {
@@ -55,7 +55,7 @@ export class Login extends Component<any, State> {
     registerLoading: false,
     captcha: undefined,
     captchaPlaying: false,
-    site: this.isoData.site.site,
+    site_view: this.isoData.site_res.site_view,
   };
 
   constructor(props: any, context: any) {
@@ -67,7 +67,7 @@ export class Login extends Component<any, State> {
     this.subscription = wsSubscribe(this.parseMessage);
 
     if (isBrowser()) {
-      WebSocketService.Instance.getCaptcha();
+      WebSocketService.Instance.client.getCaptcha();
     }
   }
 
@@ -78,7 +78,7 @@ export class Login extends Component<any, State> {
   }
 
   get documentTitle(): string {
-    return `${i18n.t('login')} - ${this.state.site.name}`;
+    return `${i18n.t('login')} - ${this.state.site_view.site.name}`;
   }
 
   render() {
@@ -280,7 +280,7 @@ export class Login extends Component<any, State> {
             </div>
           </div>
         )}
-        {this.state.site.enable_nsfw && (
+        {this.state.site_view.site.enable_nsfw && (
           <div class="form-group row">
             <div class="col-sm-10">
               <div class="form-check">
@@ -349,7 +349,7 @@ export class Login extends Component<any, State> {
     event.preventDefault();
     i.state.loginLoading = true;
     i.setState(i.state);
-    WebSocketService.Instance.login(i.state.loginForm);
+    WebSocketService.Instance.client.login(i.state.loginForm);
   }
 
   handleLoginUsernameChange(i: Login, event: any) {
@@ -366,7 +366,7 @@ export class Login extends Component<any, State> {
     event.preventDefault();
     i.state.registerLoading = true;
     i.setState(i.state);
-    WebSocketService.Instance.register(i.state.registerForm);
+    WebSocketService.Instance.client.register(i.state.registerForm);
   }
 
   handleRegisterUsernameChange(i: Login, event: any) {
@@ -404,15 +404,15 @@ export class Login extends Component<any, State> {
 
   handleRegenCaptcha(_i: Login, event: any) {
     event.preventDefault();
-    WebSocketService.Instance.getCaptcha();
+    WebSocketService.Instance.client.getCaptcha();
   }
 
   handlePasswordReset(i: Login, event: any) {
     event.preventDefault();
-    let resetForm: PasswordResetForm = {
+    let resetForm: PasswordReset = {
       email: i.state.loginForm.username_or_email,
     };
-    WebSocketService.Instance.passwordReset(resetForm);
+    WebSocketService.Instance.client.passwordReset(resetForm);
   }
 
   handleCaptchaPlay(i: Login, event: any) {
@@ -432,44 +432,48 @@ export class Login extends Component<any, State> {
     return `data:image/png;base64,${this.state.captcha.ok.png}`;
   }
 
-  parseMessage(msg: WebSocketJsonResponse) {
-    let res = wsJsonToRes(msg);
+  parseMessage(msg: any) {
+    let op = wsUserOp(msg);
     if (msg.error) {
       toast(i18n.t(msg.error), 'danger');
       this.state = this.emptyState;
       this.state.registerForm.captcha_answer = undefined;
       // Refetch another captcha
-      WebSocketService.Instance.getCaptcha();
+      WebSocketService.Instance.client.getCaptcha();
       this.setState(this.state);
       return;
     } else {
-      if (res.op == UserOperation.Login) {
-        let data = res.data as LoginResponse;
+      if (op == UserOperation.Login) {
+        let data = wsJsonToRes<LoginResponse>(msg).data;
         this.state = this.emptyState;
         this.setState(this.state);
         UserService.Instance.login(data);
-        WebSocketService.Instance.userJoin();
+        WebSocketService.Instance.client.userJoin({
+          auth: UserService.Instance.authField(),
+        });
         toast(i18n.t('logged_in'));
         this.props.history.push('/');
-      } else if (res.op == UserOperation.Register) {
-        let data = res.data as LoginResponse;
+      } else if (op == UserOperation.Register) {
+        let data = wsJsonToRes<LoginResponse>(msg).data;
         this.state = this.emptyState;
         this.setState(this.state);
         UserService.Instance.login(data);
-        WebSocketService.Instance.userJoin();
+        WebSocketService.Instance.client.userJoin({
+          auth: UserService.Instance.authField(),
+        });
         this.props.history.push('/communities');
-      } else if (res.op == UserOperation.GetCaptcha) {
-        let data = res.data as GetCaptchaResponse;
+      } else if (op == UserOperation.GetCaptcha) {
+        let data = wsJsonToRes<GetCaptchaResponse>(msg).data;
         if (data.ok) {
           this.state.captcha = data;
           this.state.registerForm.captcha_uuid = data.ok.uuid;
           this.setState(this.state);
         }
-      } else if (res.op == UserOperation.PasswordReset) {
+      } else if (op == UserOperation.PasswordReset) {
         toast(i18n.t('reset_password_mail_sent'));
-      } else if (res.op == UserOperation.GetSite) {
-        let data = res.data as GetSiteResponse;
-        this.state.site = data.site;
+      } else if (op == UserOperation.GetSite) {
+        let data = wsJsonToRes<GetSiteResponse>(msg).data;
+        this.state.site_view = data.site_view;
         this.setState(this.state);
       }
     }
diff --git a/src/shared/components/main.tsx b/src/shared/components/main.tsx
index 0814bdd..0bfe08d 100644
--- a/src/shared/components/main.tsx
+++ b/src/shared/components/main.tsx
@@ -3,26 +3,25 @@ import { Link } from 'inferno-router';
 import { Subscription } from 'rxjs';
 import {
   UserOperation,
-  CommunityUser,
+  CommunityFollowerView,
   GetFollowedCommunitiesResponse,
-  ListCommunitiesForm,
+  ListCommunities,
   ListCommunitiesResponse,
-  Community,
+  CommunityView,
   SortType,
   GetSiteResponse,
   ListingType,
   SiteResponse,
   GetPostsResponse,
   PostResponse,
-  Post,
-  GetPostsForm,
-  Comment,
-  GetCommentsForm,
+  PostView,
+  GetPosts,
+  CommentView,
+  GetComments,
   GetCommentsResponse,
   CommentResponse,
   AddAdminResponse,
   BanUserResponse,
-  WebSocketJsonResponse,
 } from 'lemmy-js-client';
 import { DataType, InitialFetchRequest } from '../interfaces';
 import { WebSocketService, UserService } from '../services';
@@ -55,20 +54,20 @@ import {
   setIsoData,
   wsSubscribe,
   isBrowser,
-  setAuth,
+  wsUserOp,
 } from '../utils';
 import { i18n } from '../i18next';
 import { T } from 'inferno-i18next';
 import { HtmlTags } from './html-tags';
 
 interface MainState {
-  subscribedCommunities: CommunityUser[];
-  trendingCommunities: Community[];
+  subscribedCommunities: CommunityFollowerView[];
+  trendingCommunities: CommunityView[];
   siteRes: GetSiteResponse;
   showEditSite: boolean;
   loading: boolean;
-  posts: Post[];
-  comments: Comment[];
+  posts: PostView[];
+  comments: CommentView[];
   listingType: ListingType;
   dataType: DataType;
   sort: SortType;
@@ -95,7 +94,7 @@ export class Main extends Component<any, MainState> {
   private emptyState: MainState = {
     subscribedCommunities: [],
     trendingCommunities: [],
-    siteRes: this.isoData.site,
+    siteRes: this.isoData.site_res,
     showEditSite: false,
     loading: true,
     posts: [],
@@ -134,7 +133,9 @@ export class Main extends Component<any, MainState> {
       this.fetchTrendingCommunities();
       this.fetchData();
       if (UserService.Instance.user) {
-        WebSocketService.Instance.getFollowedCommunities();
+        WebSocketService.Instance.client.getFollowedCommunities({
+          auth: UserService.Instance.authField(),
+        });
       }
     }
 
@@ -142,20 +143,21 @@ export class Main extends Component<any, MainState> {
   }
 
   fetchTrendingCommunities() {
-    let listCommunitiesForm: ListCommunitiesForm = {
+    let listCommunitiesForm: ListCommunities = {
       sort: SortType.Hot,
       limit: 6,
+      auth: UserService.Instance.authField(false),
     };
-    WebSocketService.Instance.listCommunities(listCommunitiesForm);
+    WebSocketService.Instance.client.listCommunities(listCommunitiesForm);
   }
 
   componentDidMount() {
     // This means it hasn't been set up yet
-    if (!this.state.siteRes.site) {
+    if (!this.state.siteRes.site_view) {
       this.context.router.history.push('/setup');
     }
 
-    WebSocketService.Instance.communityJoin({ community_id: 0 });
+    WebSocketService.Instance.client.communityJoin({ community_id: 0 });
   }
 
   componentWillUnmount() {
@@ -199,26 +201,26 @@ export class Main extends Component<any, MainState> {
     let promises: Promise<any>[] = [];
 
     if (dataType == DataType.Post) {
-      let getPostsForm: GetPostsForm = {
+      let getPostsForm: GetPosts = {
         page,
         limit: fetchLimit,
         sort,
         type_,
+        auth: req.auth,
       };
-      setAuth(getPostsForm, req.auth);
       promises.push(req.client.getPosts(getPostsForm));
     } else {
-      let getCommentsForm: GetCommentsForm = {
+      let getCommentsForm: GetComments = {
         page,
         limit: fetchLimit,
         sort,
         type_,
+        auth: req.auth,
       };
-      setAuth(getCommentsForm, req.auth);
       promises.push(req.client.getComments(getCommentsForm));
     }
 
-    let trendingCommunitiesForm: ListCommunitiesForm = {
+    let trendingCommunitiesForm: ListCommunities = {
       sort: SortType.Hot,
       limit: 6,
     };
@@ -245,7 +247,9 @@ export class Main extends Component<any, MainState> {
 
   get documentTitle(): string {
     return `${
-      this.state.siteRes.site ? this.state.siteRes.site.name : 'Lemmy'
+      this.state.siteRes.site_view
+        ? this.state.siteRes.site_view.site.name
+        : 'Lemmy'
     }`;
   }
 
@@ -256,7 +260,7 @@ export class Main extends Component<any, MainState> {
           title={this.documentTitle}
           path={this.context.router.route.match.url}
         />
-        {this.state.siteRes.site && (
+        {this.state.siteRes.site_view.site && (
           <div class="row">
             <main role="main" class="col-12 col-md-8">
               {this.posts()}
@@ -316,9 +320,9 @@ export class Main extends Component<any, MainState> {
           </T>
         </h5>
         <ul class="list-inline">
-          {this.state.trendingCommunities.map(community => (
+          {this.state.trendingCommunities.map(cv => (
             <li class="list-inline-item d-inline">
-              <CommunityLink community={community} />
+              <CommunityLink community={cv.community} />
             </li>
           ))}
         </ul>
@@ -338,17 +342,9 @@ export class Main extends Component<any, MainState> {
           </T>
         </h5>
         <ul class="list-inline mb-0">
-          {this.state.subscribedCommunities.map(community => (
+          {this.state.subscribedCommunities.map(cfv => (
             <li class="list-inline-item d-inline">
-              <CommunityLink
-                community={{
-                  name: community.community_name,
-                  id: community.community_id,
-                  local: community.community_local,
-                  actor_id: community.community_actor_id,
-                  icon: community.community_icon,
-                }}
-              />
+              <CommunityLink community={cfv.community} />
             </li>
           ))}
         </ul>
@@ -357,6 +353,7 @@ export class Main extends Component<any, MainState> {
   }
 
   sidebar() {
+    let site = this.state.siteRes.site_view.site;
     return (
       <div>
         {!this.state.showEditSite ? (
@@ -365,14 +362,11 @@ export class Main extends Component<any, MainState> {
               {this.siteName()}
               {this.adminButtons()}
             </div>
-            <BannerIconHeader banner={this.state.siteRes.site.banner} />
+            <BannerIconHeader banner={site.banner} />
             {this.siteInfo()}
           </div>
         ) : (
-          <SiteForm
-            site={this.state.siteRes.site}
-            onCancel={this.handleEditCancel}
-          />
+          <SiteForm site={site} onCancel={this.handleEditCancel} />
         )}
       </div>
     );
@@ -391,7 +385,8 @@ export class Main extends Component<any, MainState> {
   siteInfo() {
     return (
       <div>
-        {this.state.siteRes.site.description && this.siteDescription()}
+        {this.state.siteRes.site_view.site.description &&
+          this.siteDescription()}
         {this.badges()}
         {this.admins()}
       </div>
@@ -406,18 +401,9 @@ export class Main extends Component<any, MainState> {
     return (
       <ul class="mt-1 list-inline small mb-0">
         <li class="list-inline-item">{i18n.t('admins')}:</li>
-        {this.state.siteRes.admins.map(admin => (
+        {this.state.siteRes.admins.map(av => (
           <li class="list-inline-item">
-            <UserListing
-              user={{
-                name: admin.name,
-                preferred_username: admin.preferred_username,
-                avatar: admin.avatar,
-                local: admin.local,
-                actor_id: admin.actor_id,
-                id: admin.id,
-              }}
-            />
+            <UserListing user={av.user} />
           </li>
         ))}
       </ul>
@@ -425,6 +411,7 @@ export class Main extends Component<any, MainState> {
   }
 
   badges() {
+    let site_view = this.state.siteRes.site_view;
     return (
       <ul class="my-2 list-inline">
         <li className="list-inline-item badge badge-secondary">
@@ -432,22 +419,22 @@ export class Main extends Component<any, MainState> {
         </li>
         <li className="list-inline-item badge badge-secondary">
           {i18n.t('number_of_users', {
-            count: this.state.siteRes.site.number_of_users,
+            count: site_view.counts.users,
           })}
         </li>
         <li className="list-inline-item badge badge-secondary">
           {i18n.t('number_of_communities', {
-            count: this.state.siteRes.site.number_of_communities,
+            count: site_view.counts.communities,
           })}
         </li>
         <li className="list-inline-item badge badge-secondary">
           {i18n.t('number_of_posts', {
-            count: this.state.siteRes.site.number_of_posts,
+            count: site_view.counts.posts,
           })}
         </li>
         <li className="list-inline-item badge badge-secondary">
           {i18n.t('number_of_comments', {
-            count: this.state.siteRes.site.number_of_comments,
+            count: site_view.counts.comments,
           })}
         </li>
         <li className="list-inline-item">
@@ -483,7 +470,9 @@ export class Main extends Component<any, MainState> {
     return (
       <div
         className="md-div"
-        dangerouslySetInnerHTML={mdToHtml(this.state.siteRes.site.description)}
+        dangerouslySetInnerHTML={mdToHtml(
+          this.state.siteRes.site_view.site.description
+        )}
       />
     );
   }
@@ -509,14 +498,15 @@ export class Main extends Component<any, MainState> {
   }
 
   listings() {
+    let site = this.state.siteRes.site_view.site;
     return this.state.dataType == DataType.Post ? (
       <PostListings
         posts={this.state.posts}
         showCommunity
         removeDuplicates
         sort={this.state.sort}
-        enableDownvotes={this.state.siteRes.site.enable_downvotes}
-        enableNsfw={this.state.siteRes.site.enable_nsfw}
+        enableDownvotes={site.enable_downvotes}
+        enableNsfw={site.enable_nsfw}
       />
     ) : (
       <CommentNodes
@@ -525,7 +515,7 @@ export class Main extends Component<any, MainState> {
         showCommunity
         sortType={this.state.sort}
         showContext
-        enableDownvotes={this.state.siteRes.site.enable_downvotes}
+        enableDownvotes={site.enable_downvotes}
       />
     );
   }
@@ -615,8 +605,8 @@ export class Main extends Component<any, MainState> {
 
   get showLocal(): boolean {
     return (
-      this.isoData.site.federated_instances !== null &&
-      this.isoData.site.federated_instances.length > 0
+      this.isoData.site_res.federated_instances !== null &&
+      this.isoData.site_res.federated_instances.length > 0
     );
   }
 
@@ -624,7 +614,7 @@ export class Main extends Component<any, MainState> {
     return (
       UserService.Instance.user &&
       this.state.siteRes.admins
-        .map(a => a.id)
+        .map(a => a.user.id)
         .includes(UserService.Instance.user.id)
     );
   }
@@ -666,69 +656,70 @@ export class Main extends Component<any, MainState> {
 
   fetchData() {
     if (this.state.dataType == DataType.Post) {
-      let getPostsForm: GetPostsForm = {
+      let getPostsForm: GetPosts = {
         page: this.state.page,
         limit: fetchLimit,
         sort: this.state.sort,
         type_: this.state.listingType,
+        auth: UserService.Instance.authField(false),
       };
-      WebSocketService.Instance.getPosts(getPostsForm);
+      WebSocketService.Instance.client.getPosts(getPostsForm);
     } else {
-      let getCommentsForm: GetCommentsForm = {
+      let getCommentsForm: GetComments = {
         page: this.state.page,
         limit: fetchLimit,
         sort: this.state.sort,
         type_: this.state.listingType,
+        auth: UserService.Instance.authField(false),
       };
-      WebSocketService.Instance.getComments(getCommentsForm);
+      WebSocketService.Instance.client.getComments(getCommentsForm);
     }
   }
 
-  parseMessage(msg: WebSocketJsonResponse) {
-    console.log(msg);
-    let res = wsJsonToRes(msg);
+  parseMessage(msg: any) {
+    let op = wsUserOp(msg);
     if (msg.error) {
       toast(i18n.t(msg.error), 'danger');
       return;
     } else if (msg.reconnect) {
-      WebSocketService.Instance.communityJoin({ community_id: 0 });
+      WebSocketService.Instance.client.communityJoin({ community_id: 0 });
       this.fetchData();
-    } else if (res.op == UserOperation.GetFollowedCommunities) {
-      let data = res.data as GetFollowedCommunitiesResponse;
+    } else if (op == UserOperation.GetFollowedCommunities) {
+      let data = wsJsonToRes<GetFollowedCommunitiesResponse>(msg).data;
       this.state.subscribedCommunities = data.communities;
       this.setState(this.state);
-    } else if (res.op == UserOperation.ListCommunities) {
-      let data = res.data as ListCommunitiesResponse;
+    } else if (op == UserOperation.ListCommunities) {
+      let data = wsJsonToRes<ListCommunitiesResponse>(msg).data;
       this.state.trendingCommunities = data.communities;
       this.setState(this.state);
-    } else if (res.op == UserOperation.EditSite) {
-      let data = res.data as SiteResponse;
-      this.state.siteRes.site = data.site;
+    } else if (op == UserOperation.EditSite) {
+      let data = wsJsonToRes<SiteResponse>(msg).data;
+      this.state.siteRes.site_view = data.site_view;
       this.state.showEditSite = false;
       this.setState(this.state);
       toast(i18n.t('site_saved'));
-    } else if (res.op == UserOperation.GetPosts) {
-      let data = res.data as GetPostsResponse;
+    } else if (op == UserOperation.GetPosts) {
+      let data = wsJsonToRes<GetPostsResponse>(msg).data;
       this.state.posts = data.posts;
       this.state.loading = false;
       this.setState(this.state);
       setupTippy();
-    } else if (res.op == UserOperation.CreatePost) {
-      let data = res.data as PostResponse;
+    } else if (op == UserOperation.CreatePost) {
+      let data = wsJsonToRes<PostResponse>(msg).data;
 
       // If you're on subscribed, only push it if you're subscribed.
       if (this.state.listingType == ListingType.Subscribed) {
         if (
           this.state.subscribedCommunities
-            .map(c => c.community_id)
-            .includes(data.post.community_id)
+            .map(c => c.community.id)
+            .includes(data.post_view.community.id)
         ) {
-          this.state.posts.unshift(data.post);
-          notifyPost(data.post, this.context.router);
+          this.state.posts.unshift(data.post_view);
+          notifyPost(data.post_view, this.context.router);
         }
       } else {
         // NSFW posts
-        let nsfw = data.post.nsfw || data.post.community_nsfw;
+        let nsfw = data.post_view.post.nsfw || data.post_view.community.nsfw;
 
         // Don't push the post if its nsfw, and don't have that setting on
         if (
@@ -737,63 +728,65 @@ export class Main extends Component<any, MainState> {
             UserService.Instance.user &&
             UserService.Instance.user.show_nsfw)
         ) {
-          this.state.posts.unshift(data.post);
-          notifyPost(data.post, this.context.router);
+          this.state.posts.unshift(data.post_view);
+          notifyPost(data.post_view, this.context.router);
         }
       }
       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 ||
-      res.op == UserOperation.SavePost
+      op == UserOperation.EditPost ||
+      op == UserOperation.DeletePost ||
+      op == UserOperation.RemovePost ||
+      op == UserOperation.LockPost ||
+      op == UserOperation.StickyPost ||
+      op == UserOperation.SavePost
     ) {
-      let data = res.data as PostResponse;
-      editPostFindRes(data, this.state.posts);
+      let data = wsJsonToRes<PostResponse>(msg).data;
+      editPostFindRes(data.post_view, this.state.posts);
       this.setState(this.state);
-    } else if (res.op == UserOperation.CreatePostLike) {
-      let data = res.data as PostResponse;
-      createPostLikeFindRes(data, this.state.posts);
+    } else if (op == UserOperation.CreatePostLike) {
+      let data = wsJsonToRes<PostResponse>(msg).data;
+      createPostLikeFindRes(data.post_view, this.state.posts);
       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.BanUser) {
-      let data = res.data as BanUserResponse;
-      let found = this.state.siteRes.banned.find(u => (u.id = data.user.id));
+    } else if (op == UserOperation.BanUser) {
+      let data = wsJsonToRes<BanUserResponse>(msg).data;
+      let found = this.state.siteRes.banned.find(
+        u => (u.user.id = data.user_view.user.id)
+      );
 
       // Remove the banned if its found in the list, and the action is an unban
       if (found && !data.banned) {
         this.state.siteRes.banned = this.state.siteRes.banned.filter(
-          i => i.id !== data.user.id
+          i => i.user.id !== data.user_view.user.id
         );
       } else {
-        this.state.siteRes.banned.push(data.user);
+        this.state.siteRes.banned.push(data.user_view);
       }
 
       this.state.posts
-        .filter(p => p.creator_id == data.user.id)
-        .forEach(p => (p.banned = data.banned));
+        .filter(p => p.creator.id == data.user_view.user.id)
+        .forEach(p => (p.creator.banned = data.banned));
 
       this.setState(this.state);
-    } else if (res.op == UserOperation.GetComments) {
-      let data = res.data as GetCommentsResponse;
+    } else if (op == UserOperation.GetComments) {
+      let data = wsJsonToRes<GetCommentsResponse>(msg).data;
       this.state.comments = data.comments;
       this.state.loading = false;
       this.setState(this.state);
     } 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.comments);
+      let data = wsJsonToRes<CommentResponse>(msg).data;
+      editCommentRes(data.comment_view, this.state.comments);
       this.setState(this.state);
-    } else if (res.op == UserOperation.CreateComment) {
-      let data = res.data as CommentResponse;
+    } else if (op == UserOperation.CreateComment) {
+      let data = wsJsonToRes<CommentResponse>(msg).data;
 
       // Necessary since it might be a user reply
       if (data.recipient_ids.length == 0) {
@@ -801,23 +794,23 @@ export class Main extends Component<any, MainState> {
         if (this.state.listingType == ListingType.Subscribed) {
           if (
             this.state.subscribedCommunities
-              .map(c => c.community_id)
-              .includes(data.comment.community_id)
+              .map(c => c.community.id)
+              .includes(data.comment_view.community.id)
           ) {
-            this.state.comments.unshift(data.comment);
+            this.state.comments.unshift(data.comment_view);
           }
         } else {
-          this.state.comments.unshift(data.comment);
+          this.state.comments.unshift(data.comment_view);
         }
         this.setState(this.state);
       }
-    } else if (res.op == UserOperation.SaveComment) {
-      let data = res.data as CommentResponse;
-      saveCommentRes(data, this.state.comments);
+    } else if (op == UserOperation.SaveComment) {
+      let data = wsJsonToRes<CommentResponse>(msg).data;
+      saveCommentRes(data.comment_view, this.state.comments);
       this.setState(this.state);
-    } else if (res.op == UserOperation.CreateCommentLike) {
-      let data = res.data as CommentResponse;
-      createCommentLikeRes(data, this.state.comments);
+    } else if (op == UserOperation.CreateCommentLike) {
+      let data = wsJsonToRes<CommentResponse>(msg).data;
+      createCommentLikeRes(data.comment_view, this.state.comments);
       this.setState(this.state);
     }
   }
diff --git a/src/shared/components/modlog.tsx b/src/shared/components/modlog.tsx
index ded9a30..fcd18e5 100644
--- a/src/shared/components/modlog.tsx
+++ b/src/shared/components/modlog.tsx
@@ -3,51 +3,71 @@ import { Link } from 'inferno-router';
 import { Subscription } from 'rxjs';
 import {
   UserOperation,
-  GetModlogForm,
+  GetModlog,
   GetModlogResponse,
-  ModRemovePost,
-  ModLockPost,
-  ModStickyPost,
-  ModRemoveComment,
-  ModRemoveCommunity,
-  ModBanFromCommunity,
-  ModBan,
-  ModAddCommunity,
-  ModAdd,
-  WebSocketJsonResponse,
-  Site,
+  SiteView,
+  ModRemovePostView,
+  ModLockPostView,
+  ModStickyPostView,
+  ModRemoveCommentView,
+  ModRemoveCommunityView,
+  ModBanFromCommunityView,
+  ModBanView,
+  ModAddCommunityView,
+  ModAddView,
 } from 'lemmy-js-client';
 import { WebSocketService } from '../services';
 import {
   wsJsonToRes,
-  addTypeInfo,
   fetchLimit,
   toast,
   setIsoData,
   wsSubscribe,
   isBrowser,
+  wsUserOp,
 } from '../utils';
 import { MomentTime } from './moment-time';
 import { HtmlTags } from './html-tags';
 import moment from 'moment';
 import { i18n } from '../i18next';
 import { InitialFetchRequest } from 'shared/interfaces';
+import { UserListing } from './user-listing';
+import { CommunityLink } from './community-link';
+
+enum ModlogEnum {
+  ModRemovePost,
+  ModLockPost,
+  ModStickyPost,
+  ModRemoveComment,
+  ModRemoveCommunity,
+  ModBanFromCommunity,
+  ModAddCommunity,
+  ModAdd,
+  ModBan,
+}
+
+type ModlogType = {
+  id: number;
+  type_: ModlogEnum;
+  view:
+    | ModRemovePostView
+    | ModLockPostView
+    | ModStickyPostView
+    | ModRemoveCommentView
+    | ModRemoveCommunityView
+    | ModBanFromCommunityView
+    | ModBanView
+    | ModAddCommunityView
+    | ModAddView;
+  when_: string;
+};
 
 interface ModlogState {
-  combined: {
-    type_: string;
-    data:
-      | ModRemovePost
-      | ModLockPost
-      | ModStickyPost
-      | ModRemoveCommunity
-      | ModAdd
-      | ModBan;
-  }[];
+  res: GetModlogResponse;
   communityId?: number;
   communityName?: string;
   page: number;
-  site: Site;
+  site_view: SiteView;
   loading: boolean;
 }
 
@@ -55,10 +75,20 @@ export class Modlog extends Component<any, ModlogState> {
   private isoData = setIsoData(this.context);
   private subscription: Subscription;
   private emptyState: ModlogState = {
-    combined: [],
+    res: {
+      removed_posts: [],
+      locked_posts: [],
+      stickied_posts: [],
+      removed_comments: [],
+      removed_communities: [],
+      banned_from_community: [],
+      banned: [],
+      added_to_community: [],
+      added: [],
+    },
     page: 1,
     loading: true,
-    site: this.isoData.site.site,
+    site_view: this.isoData.site_res.site_view,
   };
 
   constructor(props: any, context: any) {
@@ -75,7 +105,7 @@ export class Modlog extends Component<any, ModlogState> {
     // Only fetch the data if coming from another route
     if (this.isoData.path == this.context.router.route.match.url) {
       let data = this.isoData.routeData[0];
-      this.setCombined(data);
+      this.state.res = data;
       this.state.loading = false;
     } else {
       this.refetch();
@@ -88,264 +118,233 @@ export class Modlog extends Component<any, ModlogState> {
     }
   }
 
-  setCombined(res: GetModlogResponse) {
-    let removed_posts = addTypeInfo(res.removed_posts, 'removed_posts');
-    let locked_posts = addTypeInfo(res.locked_posts, 'locked_posts');
-    let stickied_posts = addTypeInfo(res.stickied_posts, 'stickied_posts');
-    let removed_comments = addTypeInfo(
-      res.removed_comments,
-      'removed_comments'
-    );
-    let removed_communities = addTypeInfo(
-      res.removed_communities,
-      'removed_communities'
-    );
-    let banned_from_community = addTypeInfo(
-      res.banned_from_community,
-      'banned_from_community'
-    );
-    let added_to_community = addTypeInfo(
-      res.added_to_community,
-      'added_to_community'
+  buildCombined(res: GetModlogResponse): ModlogType[] {
+    let removed_posts: ModlogType[] = res.removed_posts.map(r => ({
+      id: r.mod_remove_post.id,
+      type_: ModlogEnum.ModRemovePost,
+      view: r,
+      when_: r.mod_remove_post.when_,
+    }));
+
+    let locked_posts: ModlogType[] = res.locked_posts.map(r => ({
+      id: r.mod_lock_post.id,
+      type_: ModlogEnum.ModLockPost,
+      view: r,
+      when_: r.mod_lock_post.when_,
+    }));
+
+    let stickied_posts: ModlogType[] = res.stickied_posts.map(r => ({
+      id: r.mod_sticky_post.id,
+      type_: ModlogEnum.ModStickyPost,
+      view: r,
+      when_: r.mod_sticky_post.when_,
+    }));
+
+    let removed_comments: ModlogType[] = res.removed_comments.map(r => ({
+      id: r.mod_remove_comment.id,
+      type_: ModlogEnum.ModRemoveComment,
+      view: r,
+      when_: r.mod_remove_comment.when_,
+    }));
+
+    let removed_communities: ModlogType[] = res.removed_communities.map(r => ({
+      id: r.mod_remove_community.id,
+      type_: ModlogEnum.ModRemoveCommunity,
+      view: r,
+      when_: r.mod_remove_community.when_,
+    }));
+
+    let banned_from_community: ModlogType[] = res.banned_from_community.map(
+      r => ({
+        id: r.mod_ban_from_community.id,
+        type_: ModlogEnum.ModBanFromCommunity,
+        view: r,
+        when_: r.mod_ban_from_community.when_,
+      })
     );
-    let added = addTypeInfo(res.added, 'added');
-    let banned = addTypeInfo(res.banned, 'banned');
-    this.state.combined = [];
-
-    this.state.combined.push(...removed_posts);
-    this.state.combined.push(...locked_posts);
-    this.state.combined.push(...stickied_posts);
-    this.state.combined.push(...removed_comments);
-    this.state.combined.push(...removed_communities);
-    this.state.combined.push(...banned_from_community);
-    this.state.combined.push(...added_to_community);
-    this.state.combined.push(...added);
-    this.state.combined.push(...banned);
-
-    if (this.state.communityId && this.state.combined.length > 0) {
-      this.state.communityName = (this.state.combined[0]
-        .data as ModRemovePost).community_name;
+
+    let added_to_community: ModlogType[] = res.added_to_community.map(r => ({
+      id: r.mod_add_community.id,
+      type_: ModlogEnum.ModAddCommunity,
+      view: r,
+      when_: r.mod_add_community.when_,
+    }));
+
+    let added: ModlogType[] = res.added.map(r => ({
+      id: r.mod_add.id,
+      type_: ModlogEnum.ModAdd,
+      view: r,
+      when_: r.mod_add.when_,
+    }));
+
+    let banned: ModlogType[] = res.banned.map(r => ({
+      id: r.mod_ban.id,
+      type_: ModlogEnum.ModBan,
+      view: r,
+      when_: r.mod_ban.when_,
+    }));
+
+    let combined: ModlogType[] = [];
+
+    combined.push(...removed_posts);
+    combined.push(...locked_posts);
+    combined.push(...stickied_posts);
+    combined.push(...removed_comments);
+    combined.push(...removed_communities);
+    combined.push(...banned_from_community);
+    combined.push(...added_to_community);
+    combined.push(...added);
+    combined.push(...banned);
+
+    if (this.state.communityId && combined.length > 0) {
+      this.state.communityName = (combined[0]
+        .view as ModRemovePostView).community.name;
     }
 
     // Sort them by time
-    this.state.combined.sort((a, b) =>
-      b.data.when_.localeCompare(a.data.when_)
-    );
+    combined.sort((a, b) => b.when_.localeCompare(a.when_));
+
+    return combined;
+  }
+
+  renderModlogType(i: ModlogType) {
+    switch (i.type_) {
+      case ModlogEnum.ModRemovePost:
+        let mrpv = i.view as ModRemovePostView;
+        return [
+          mrpv.mod_remove_post.removed ? 'Removed ' : 'Restored ',
+          <span>
+            Post <Link to={`/post/${mrpv.post.id}`}>{mrpv.post.name}</Link>
+          </span>,
+          mrpv.mod_remove_post.reason &&
+            ` reason: ${mrpv.mod_remove_post.reason}`,
+        ];
+      case ModlogEnum.ModLockPost:
+        let mlpv = i.view as ModLockPostView;
+        return [
+          mlpv.mod_lock_post.locked ? 'Locked ' : 'Unlocked ',
+          <span>
+            Post <Link to={`/post/${mlpv.post.id}`}>{mlpv.post.name}</Link>
+          </span>,
+        ];
+      case ModlogEnum.ModStickyPost:
+        let mspv = i.view as ModStickyPostView;
+        return [
+          mspv.mod_sticky_post.stickied ? 'Stickied ' : 'Unstickied ',
+          <span>
+            Post <Link to={`/post/${mspv.post.id}`}>{mspv.post.name}</Link>
+          </span>,
+        ];
+      case ModlogEnum.ModRemoveComment:
+        let mrc = i.view as ModRemoveCommentView;
+        return [
+          mrc.mod_remove_comment.removed ? 'Removed ' : 'Restored ',
+          <span>
+            Comment{' '}
+            <Link to={`/post/${mrc.post.id}/comment/${mrc.comment.id}`}>
+              {mrc.comment.content}
+            </Link>
+          </span>,
+          <span>
+            {' '}
+            by <UserListing user={mrc.commenter} />
+          </span>,
+          mrc.mod_remove_comment.reason &&
+            ` reason: ${mrc.mod_remove_comment.reason}`,
+        ];
+      case ModlogEnum.ModRemoveCommunity:
+        let mrco = i.view as ModRemoveCommunityView;
+        return [
+          mrco.mod_remove_community.removed ? 'Removed ' : 'Restored ',
+          <span>
+            Community <CommunityLink community={mrco.community} />
+          </span>,
+          mrco.mod_remove_community.reason &&
+            ` reason: ${mrco.mod_remove_community.reason}`,
+          mrco.mod_remove_community.expires &&
+            ` expires: ${moment
+              .utc(mrco.mod_remove_community.expires)
+              .fromNow()}`,
+        ];
+      case ModlogEnum.ModBanFromCommunity:
+        let mbfc = i.view as ModBanFromCommunityView;
+        return [
+          <span>
+            {mbfc.mod_ban_from_community.banned ? 'Banned ' : 'Unbanned '}{' '}
+          </span>,
+          <span>
+            <UserListing user={mbfc.banned_user} />
+          </span>,
+          <span> from the community </span>,
+          <span>
+            <CommunityLink community={mbfc.community} />
+          </span>,
+          <div>
+            {mbfc.mod_ban_from_community.reason &&
+              ` reason: ${mbfc.mod_ban_from_community.reason}`}
+          </div>,
+          <div>
+            {mbfc.mod_ban_from_community.expires &&
+              ` expires: ${moment
+                .utc(mbfc.mod_ban_from_community.expires)
+                .fromNow()}`}
+          </div>,
+        ];
+      case ModlogEnum.ModAddCommunity:
+        let mac = i.view as ModAddCommunityView;
+        return [
+          <span>
+            {mac.mod_add_community.removed ? 'Removed ' : 'Appointed '}{' '}
+          </span>,
+          <span>
+            <UserListing user={mac.modded_user} />
+          </span>,
+          <span> as a mod to the community </span>,
+          <span>
+            <CommunityLink community={mac.community} />
+          </span>,
+        ];
+      case ModlogEnum.ModBan:
+        let mb = i.view as ModBanView;
+        return [
+          <span>{mb.mod_ban.banned ? 'Banned ' : 'Unbanned '} </span>,
+          <span>
+            <UserListing user={mb.banned_user} />
+          </span>,
+          <div>{mb.mod_ban.reason && ` reason: ${mb.mod_ban.reason}`}</div>,
+          <div>
+            {mb.mod_ban.expires &&
+              ` expires: ${moment.utc(mb.mod_ban.expires).fromNow()}`}
+          </div>,
+        ];
+      case ModlogEnum.ModAdd:
+        let ma = i.view as ModAddView;
+        return [
+          <span>{ma.mod_add.removed ? 'Removed ' : 'Appointed '} </span>,
+          <span>
+            <UserListing user={ma.modded_user} />
+          </span>,
+          <span> as an admin </span>,
+        ];
+      default:
+        return <div />;
+    }
   }
 
   combined() {
+    let combined = this.buildCombined(this.state.res);
+
     return (
       <tbody>
-        {this.state.combined.map(i => (
+        {combined.map(i => (
           <tr>
             <td>
-              <MomentTime data={i.data} />
-            </td>
-            <td>
-              <Link to={`/u/${i.data.mod_user_name}`}>
-                {i.data.mod_user_name}
-              </Link>
+              <MomentTime data={i} />
             </td>
             <td>
-              {i.type_ == 'removed_posts' && (
-                <>
-                  {(i.data as ModRemovePost).removed ? 'Removed' : 'Restored'}
-                  <span>
-                    {' '}
-                    Post{' '}
-                    <Link to={`/post/${(i.data as ModRemovePost).post_id}`}>
-                      {(i.data as ModRemovePost).post_name}
-                    </Link>
-                  </span>
-                  <div>
-                    {(i.data as ModRemovePost).reason &&
-                      ` reason: ${(i.data as ModRemovePost).reason}`}
-                  </div>
-                </>
-              )}
-              {i.type_ == 'locked_posts' && (
-                <>
-                  {(i.data as ModLockPost).locked ? 'Locked' : 'Unlocked'}
-                  <span>
-                    {' '}
-                    Post{' '}
-                    <Link to={`/post/${(i.data as ModLockPost).post_id}`}>
-                      {(i.data as ModLockPost).post_name}
-                    </Link>
-                  </span>
-                </>
-              )}
-              {i.type_ == 'stickied_posts' && (
-                <>
-                  {(i.data as ModStickyPost).stickied
-                    ? 'Stickied'
-                    : 'Unstickied'}
-                  <span>
-                    {' '}
-                    Post{' '}
-                    <Link to={`/post/${(i.data as ModStickyPost).post_id}`}>
-                      {(i.data as ModStickyPost).post_name}
-                    </Link>
-                  </span>
-                </>
-              )}
-              {i.type_ == 'removed_comments' && (
-                <>
-                  {(i.data as ModRemoveComment).removed
-                    ? 'Removed'
-                    : 'Restored'}
-                  <span>
-                    {' '}
-                    Comment{' '}
-                    <Link
-                      to={`/post/${
-                        (i.data as ModRemoveComment).post_id
-                      }/comment/${(i.data as ModRemoveComment).comment_id}`}
-                    >
-                      {(i.data as ModRemoveComment).comment_content}
-                    </Link>
-                  </span>
-                  <span>
-                    {' '}
-                    by{' '}
-                    <Link
-                      to={`/u/${
-                        (i.data as ModRemoveComment).comment_user_name
-                      }`}
-                    >
-                      {(i.data as ModRemoveComment).comment_user_name}
-                    </Link>
-                  </span>
-                  <div>
-                    {(i.data as ModRemoveComment).reason &&
-                      ` reason: ${(i.data as ModRemoveComment).reason}`}
-                  </div>
-                </>
-              )}
-              {i.type_ == 'removed_communities' && (
-                <>
-                  {(i.data as ModRemoveCommunity).removed
-                    ? 'Removed'
-                    : 'Restored'}
-                  <span>
-                    {' '}
-                    Community{' '}
-                    <Link
-                      to={`/c/${(i.data as ModRemoveCommunity).community_name}`}
-                    >
-                      {(i.data as ModRemoveCommunity).community_name}
-                    </Link>
-                  </span>
-                  <div>
-                    {(i.data as ModRemoveCommunity).reason &&
-                      ` reason: ${(i.data as ModRemoveCommunity).reason}`}
-                  </div>
-                  <div>
-                    {(i.data as ModRemoveCommunity).expires &&
-                      ` expires: ${moment
-                        .utc((i.data as ModRemoveCommunity).expires)
-                        .fromNow()}`}
-                  </div>
-                </>
-              )}
-              {i.type_ == 'banned_from_community' && (
-                <>
-                  <span>
-                    {(i.data as ModBanFromCommunity).banned
-                      ? 'Banned '
-                      : 'Unbanned '}{' '}
-                  </span>
-                  <span>
-                    <Link
-                      to={`/u/${
-                        (i.data as ModBanFromCommunity).other_user_name
-                      }`}
-                    >
-                      {(i.data as ModBanFromCommunity).other_user_name}
-                    </Link>
-                  </span>
-                  <span> from the community </span>
-                  <span>
-                    <Link
-                      to={`/c/${
-                        (i.data as ModBanFromCommunity).community_name
-                      }`}
-                    >
-                      {(i.data as ModBanFromCommunity).community_name}
-                    </Link>
-                  </span>
-                  <div>
-                    {(i.data as ModBanFromCommunity).reason &&
-                      ` reason: ${(i.data as ModBanFromCommunity).reason}`}
-                  </div>
-                  <div>
-                    {(i.data as ModBanFromCommunity).expires &&
-                      ` expires: ${moment
-                        .utc((i.data as ModBanFromCommunity).expires)
-                        .fromNow()}`}
-                  </div>
-                </>
-              )}
-              {i.type_ == 'added_to_community' && (
-                <>
-                  <span>
-                    {(i.data as ModAddCommunity).removed
-                      ? 'Removed '
-                      : 'Appointed '}{' '}
-                  </span>
-                  <span>
-                    <Link
-                      to={`/u/${(i.data as ModAddCommunity).other_user_name}`}
-                    >
-                      {(i.data as ModAddCommunity).other_user_name}
-                    </Link>
-                  </span>
-                  <span> as a mod to the community </span>
-                  <span>
-                    <Link
-                      to={`/c/${(i.data as ModAddCommunity).community_name}`}
-                    >
-                      {(i.data as ModAddCommunity).community_name}
-                    </Link>
-                  </span>
-                </>
-              )}
-              {i.type_ == 'banned' && (
-                <>
-                  <span>
-                    {(i.data as ModBan).banned ? 'Banned ' : 'Unbanned '}{' '}
-                  </span>
-                  <span>
-                    <Link to={`/u/${(i.data as ModBan).other_user_name}`}>
-                      {(i.data as ModBan).other_user_name}
-                    </Link>
-                  </span>
-                  <div>
-                    {(i.data as ModBan).reason &&
-                      ` reason: ${(i.data as ModBan).reason}`}
-                  </div>
-                  <div>
-                    {(i.data as ModBan).expires &&
-                      ` expires: ${moment
-                        .utc((i.data as ModBan).expires)
-                        .fromNow()}`}
-                  </div>
-                </>
-              )}
-              {i.type_ == 'added' && (
-                <>
-                  <span>
-                    {(i.data as ModAdd).removed ? 'Removed ' : 'Appointed '}{' '}
-                  </span>
-                  <span>
-                    <Link to={`/u/${(i.data as ModAdd).other_user_name}`}>
-                      {(i.data as ModAdd).other_user_name}
-                    </Link>
-                  </span>
-                  <span> as an admin </span>
-                </>
-              )}
+              <UserListing user={i.view.moderator} />
             </td>
+            <td>{this.renderModlogType(i)}</td>
           </tr>
         ))}
       </tbody>
@@ -353,7 +352,7 @@ export class Modlog extends Component<any, ModlogState> {
   }
 
   get documentTitle(): string {
-    return `Modlog - ${this.state.site.name}`;
+    return `Modlog - ${this.state.site_view.site.name}`;
   }
 
   render() {
@@ -435,12 +434,12 @@ export class Modlog extends Component<any, ModlogState> {
   }
 
   refetch() {
-    let modlogForm: GetModlogForm = {
+    let modlogForm: GetModlog = {
       community_id: this.state.communityId,
       page: this.state.page,
       limit: fetchLimit,
     };
-    WebSocketService.Instance.getModlog(modlogForm);
+    WebSocketService.Instance.client.getModlog(modlogForm);
   }
 
   static fetchInitialData(req: InitialFetchRequest): Promise<any>[] {
@@ -448,7 +447,7 @@ export class Modlog extends Component<any, ModlogState> {
     let communityId = pathSplit[3];
     let promises: Promise<any>[] = [];
 
-    let modlogForm: GetModlogForm = {
+    let modlogForm: GetModlog = {
       page: 1,
       limit: fetchLimit,
     };
@@ -461,17 +460,16 @@ export class Modlog extends Component<any, ModlogState> {
     return promises;
   }
 
-  parseMessage(msg: WebSocketJsonResponse) {
-    console.log(msg);
-    let res = wsJsonToRes(msg);
+  parseMessage(msg: any) {
+    let op = wsUserOp(msg);
     if (msg.error) {
       toast(i18n.t(msg.error), 'danger');
       return;
-    } else if (res.op == UserOperation.GetModlog) {
-      let data = res.data as GetModlogResponse;
+    } else if (op == UserOperation.GetModlog) {
+      let data = wsJsonToRes<GetModlogResponse>(msg).data;
       this.state.loading = false;
       window.scrollTo(0, 0);
-      this.setCombined(data);
+      this.state.res = data;
       this.setState(this.state);
     }
   }
diff --git a/src/shared/components/navbar.tsx b/src/shared/components/navbar.tsx
index f5a4c14..e31d0d5 100644
--- a/src/shared/components/navbar.tsx
+++ b/src/shared/components/navbar.tsx
@@ -4,19 +4,18 @@ import { Subscription } from 'rxjs';
 import { WebSocketService, UserService } from '../services';
 import {
   UserOperation,
-  GetRepliesForm,
+  GetReplies,
   GetRepliesResponse,
-  GetUserMentionsForm,
+  GetUserMentions,
   GetUserMentionsResponse,
-  GetPrivateMessagesForm,
+  GetPrivateMessages,
   PrivateMessagesResponse,
   SortType,
   GetSiteResponse,
-  Comment,
+  CommentView,
   CommentResponse,
-  PrivateMessage,
   PrivateMessageResponse,
-  WebSocketJsonResponse,
+  PrivateMessageView,
 } from 'lemmy-js-client';
 import {
   wsJsonToRes,
@@ -30,20 +29,21 @@ import {
   isBrowser,
   wsSubscribe,
   supportLemmyUrl,
+  wsUserOp,
 } from '../utils';
 import { i18n } from '../i18next';
 import { PictrsImage } from './pictrs-image';
 
 interface NavbarProps {
-  site: GetSiteResponse;
+  site_res: GetSiteResponse;
 }
 
 interface NavbarState {
   isLoggedIn: boolean;
   expanded: boolean;
-  replies: Comment[];
-  mentions: Comment[];
-  messages: PrivateMessage[];
+  replies: CommentView[];
+  mentions: CommentView[];
+  messages: PrivateMessageView[];
   unreadCount: number;
   searchParam: string;
   toggleSearch: boolean;
@@ -56,7 +56,7 @@ export class Navbar extends Component<NavbarProps, NavbarState> {
   private unreadCountSub: Subscription;
   private searchTextField: RefObject<HTMLInputElement>;
   emptyState: NavbarState = {
-    isLoggedIn: !!this.props.site.my_user,
+    isLoggedIn: !!this.props.site_res.my_user,
     unreadCount: 0,
     replies: [],
     mentions: [],
@@ -88,7 +88,9 @@ export class Navbar extends Component<NavbarProps, NavbarState> {
         // i18n.changeLanguage('de');
       } else {
         this.requestNotificationPermission();
-        WebSocketService.Instance.userJoin();
+        WebSocketService.Instance.client.userJoin({
+          auth: UserService.Instance.authField(),
+        });
         this.fetchUnreads();
       }
 
@@ -96,7 +98,7 @@ export class Navbar extends Component<NavbarProps, NavbarState> {
         // A login
         if (res !== undefined) {
           this.requestNotificationPermission();
-          WebSocketService.Instance.getSite();
+          WebSocketService.Instance.client.getSite();
         } else {
           this.setState({ isLoggedIn: false });
         }
@@ -165,20 +167,23 @@ export class Navbar extends Component<NavbarProps, NavbarState> {
 
   // TODO class active corresponding to current page
   navbar() {
-    let user = this.props.site.my_user;
+    let user = this.props.site_res.my_user;
     return (
       <nav class="navbar navbar-expand-lg navbar-light shadow-sm p-0 px-3">
         <div class="container">
-          {this.props.site.site && (
+          {this.props.site_res.site_view && (
             <Link
-              title={this.props.site.version}
+              title={this.props.site_res.version}
               className="d-flex align-items-center navbar-brand mr-md-3"
               to="/"
             >
-              {this.props.site.site.icon && showAvatars() && (
-                <PictrsImage src={this.props.site.site.icon} icon />
+              {this.props.site_res.site_view.site.icon && showAvatars() && (
+                <PictrsImage
+                  src={this.props.site_res.site_view.site.icon}
+                  icon
+                />
               )}
-              {this.props.site.site.name}
+              {this.props.site_res.site_view.site.name}
             </Link>
           )}
           {this.state.isLoggedIn && (
@@ -362,8 +367,8 @@ export class Navbar extends Component<NavbarProps, NavbarState> {
     i.setState(i.state);
   }
 
-  parseMessage(msg: WebSocketJsonResponse) {
-    let res = wsJsonToRes(msg);
+  parseMessage(msg: any) {
+    let op = wsUserOp(msg);
     if (msg.error) {
       if (msg.error == 'not_logged_in') {
         UserService.Instance.logout();
@@ -371,62 +376,68 @@ export class Navbar extends Component<NavbarProps, NavbarState> {
       }
       return;
     } else if (msg.reconnect) {
-      WebSocketService.Instance.userJoin();
+      WebSocketService.Instance.client.userJoin({
+        auth: UserService.Instance.authField(),
+      });
       this.fetchUnreads();
-    } else if (res.op == UserOperation.GetReplies) {
-      let data = res.data as GetRepliesResponse;
-      let unreadReplies = data.replies.filter(r => !r.read);
+    } else if (op == UserOperation.GetReplies) {
+      let data = wsJsonToRes<GetRepliesResponse>(msg).data;
+      let unreadReplies = data.replies.filter(r => !r.comment.read);
 
       this.state.replies = unreadReplies;
       this.state.unreadCount = this.calculateUnreadCount();
       this.setState(this.state);
       this.sendUnreadCount();
-    } else if (res.op == UserOperation.GetUserMentions) {
-      let data = res.data as GetUserMentionsResponse;
-      let unreadMentions = data.mentions.filter(r => !r.read);
+    } else if (op == UserOperation.GetUserMentions) {
+      let data = wsJsonToRes<GetUserMentionsResponse>(msg).data;
+      let unreadMentions = data.mentions.filter(r => !r.comment.read);
 
       this.state.mentions = unreadMentions;
       this.state.unreadCount = this.calculateUnreadCount();
       this.setState(this.state);
       this.sendUnreadCount();
-    } else if (res.op == UserOperation.GetPrivateMessages) {
-      let data = res.data as PrivateMessagesResponse;
-      let unreadMessages = data.messages.filter(r => !r.read);
+    } else if (op == UserOperation.GetPrivateMessages) {
+      let data = wsJsonToRes<PrivateMessagesResponse>(msg).data;
+      let unreadMessages = data.private_messages.filter(
+        r => !r.private_message.read
+      );
 
       this.state.messages = unreadMessages;
       this.state.unreadCount = this.calculateUnreadCount();
       this.setState(this.state);
       this.sendUnreadCount();
-    } else if (res.op == UserOperation.GetSite) {
+    } else if (op == UserOperation.GetSite) {
       // This is only called on a successful login
-      let data = res.data as GetSiteResponse;
+      let data = wsJsonToRes<GetSiteResponse>(msg).data;
       UserService.Instance.user = data.my_user;
       setTheme(UserService.Instance.user.theme);
       i18n.changeLanguage(getLanguage());
       this.state.isLoggedIn = true;
       this.setState(this.state);
-    } else if (res.op == UserOperation.CreateComment) {
-      let data = res.data as CommentResponse;
+    } else if (op == UserOperation.CreateComment) {
+      let data = wsJsonToRes<CommentResponse>(msg).data;
 
       if (this.state.isLoggedIn) {
         if (data.recipient_ids.includes(UserService.Instance.user.id)) {
-          this.state.replies.push(data.comment);
+          this.state.replies.push(data.comment_view);
           this.state.unreadCount++;
           this.setState(this.state);
           this.sendUnreadCount();
-          notifyComment(data.comment, this.context.router);
+          notifyComment(data.comment_view, this.context.router);
         }
       }
-    } else if (res.op == UserOperation.CreatePrivateMessage) {
-      let data = res.data as PrivateMessageResponse;
+    } else if (op == UserOperation.CreatePrivateMessage) {
+      let data = wsJsonToRes<PrivateMessageResponse>(msg).data;
 
       if (this.state.isLoggedIn) {
-        if (data.message.recipient_id == UserService.Instance.user.id) {
-          this.state.messages.push(data.message);
+        if (
+          data.private_message_view.recipient.id == UserService.Instance.user.id
+        ) {
+          this.state.messages.push(data.private_message_view);
           this.state.unreadCount++;
           this.setState(this.state);
           this.sendUnreadCount();
-          notifyPrivateMessage(data.message, this.context.router);
+          notifyPrivateMessage(data.private_message_view, this.context.router);
         }
       }
     }
@@ -434,30 +445,33 @@ export class Navbar extends Component<NavbarProps, NavbarState> {
 
   fetchUnreads() {
     console.log('Fetching unreads...');
-    let repliesForm: GetRepliesForm = {
+    let repliesForm: GetReplies = {
       sort: SortType.New,
       unread_only: true,
       page: 1,
       limit: fetchLimit,
+      auth: UserService.Instance.authField(),
     };
 
-    let userMentionsForm: GetUserMentionsForm = {
+    let userMentionsForm: GetUserMentions = {
       sort: SortType.New,
       unread_only: true,
       page: 1,
       limit: fetchLimit,
+      auth: UserService.Instance.authField(),
     };
 
-    let privateMessagesForm: GetPrivateMessagesForm = {
+    let privateMessagesForm: GetPrivateMessages = {
       unread_only: true,
       page: 1,
       limit: fetchLimit,
+      auth: UserService.Instance.authField(),
     };
 
     if (this.currentLocation !== '/inbox') {
-      WebSocketService.Instance.getReplies(repliesForm);
-      WebSocketService.Instance.getUserMentions(userMentionsForm);
-      WebSocketService.Instance.getPrivateMessages(privateMessagesForm);
+      WebSocketService.Instance.client.getReplies(repliesForm);
+      WebSocketService.Instance.client.getUserMentions(userMentionsForm);
+      WebSocketService.Instance.client.getPrivateMessages(privateMessagesForm);
     }
   }
 
@@ -471,17 +485,17 @@ export class Navbar extends Component<NavbarProps, NavbarState> {
 
   calculateUnreadCount(): number {
     return (
-      this.state.replies.filter(r => !r.read).length +
-      this.state.mentions.filter(r => !r.read).length +
-      this.state.messages.filter(r => !r.read).length
+      this.state.replies.filter(r => !r.comment.read).length +
+      this.state.mentions.filter(r => !r.comment.read).length +
+      this.state.messages.filter(r => !r.private_message.read).length
     );
   }
 
   get canAdmin(): boolean {
     return (
       UserService.Instance.user &&
-      this.props.site.admins
-        .map(a => a.id)
+      this.props.site_res.admins
+        .map(a => a.user.id)
         .includes(UserService.Instance.user.id)
     );
   }
diff --git a/src/shared/components/password_change.tsx b/src/shared/components/password_change.tsx
index 1725906..4dcd6c6 100644
--- a/src/shared/components/password_change.tsx
+++ b/src/shared/components/password_change.tsx
@@ -3,9 +3,8 @@ import { Subscription } from 'rxjs';
 import {
   UserOperation,
   LoginResponse,
-  PasswordChangeForm,
-  WebSocketJsonResponse,
-  Site,
+  PasswordChange as PasswordChangeForm,
+  SiteView,
 } from 'lemmy-js-client';
 import { WebSocketService, UserService } from '../services';
 import {
@@ -15,6 +14,7 @@ import {
   setIsoData,
   isBrowser,
   wsSubscribe,
+  wsUserOp,
 } from '../utils';
 import { i18n } from '../i18next';
 import { HtmlTags } from './html-tags';
@@ -22,7 +22,7 @@ import { HtmlTags } from './html-tags';
 interface State {
   passwordChangeForm: PasswordChangeForm;
   loading: boolean;
-  site: Site;
+  site_view: SiteView;
 }
 
 export class PasswordChange extends Component<any, State> {
@@ -36,7 +36,7 @@ export class PasswordChange extends Component<any, State> {
       password_verify: undefined,
     },
     loading: false,
-    site: this.isoData.site.site,
+    site_view: this.isoData.site_res.site_view,
   };
 
   constructor(props: any, context: any) {
@@ -55,7 +55,7 @@ export class PasswordChange extends Component<any, State> {
   }
 
   get documentTitle(): string {
-    return `${i18n.t('password_change')} - ${this.state.site.name}`;
+    return `${i18n.t('password_change')} - ${this.state.site_view.site.name}`;
   }
 
   render() {
@@ -138,18 +138,18 @@ export class PasswordChange extends Component<any, State> {
     i.state.loading = true;
     i.setState(i.state);
 
-    WebSocketService.Instance.passwordChange(i.state.passwordChangeForm);
+    WebSocketService.Instance.client.passwordChange(i.state.passwordChangeForm);
   }
 
-  parseMessage(msg: WebSocketJsonResponse) {
-    let res = wsJsonToRes(msg);
+  parseMessage(msg: any) {
+    let op = wsUserOp(msg);
     if (msg.error) {
       toast(i18n.t(msg.error), 'danger');
       this.state.loading = false;
       this.setState(this.state);
       return;
-    } else if (res.op == UserOperation.PasswordChange) {
-      let data = res.data as LoginResponse;
+    } else if (op == UserOperation.PasswordChange) {
+      let data = wsJsonToRes<LoginResponse>(msg).data;
       this.state = this.emptyState;
       this.setState(this.state);
       UserService.Instance.login(data);
diff --git a/src/shared/components/post-form.tsx b/src/shared/components/post-form.tsx
index 5e683f1..c64104b 100644
--- a/src/shared/components/post-form.tsx
+++ b/src/shared/components/post-form.tsx
@@ -4,19 +4,19 @@ import { PostListings } from './post-listings';
 import { MarkdownTextArea } from './markdown-textarea';
 import { Subscription } from 'rxjs';
 import {
-  PostForm as PostFormI,
-  PostFormParams,
-  Post,
+  CreatePost,
+  EditPost,
+  PostView,
   PostResponse,
   UserOperation,
-  Community,
+  CommunityView,
   SortType,
-  SearchForm,
+  Search,
   SearchType,
   SearchResponse,
-  WebSocketJsonResponse,
 } from 'lemmy-js-client';
 import { WebSocketService, UserService } from '../services';
+import { PostFormParams } from '../interfaces';
 import {
   wsJsonToRes,
   getPageTitle,
@@ -33,6 +33,7 @@ import {
   validTitle,
   wsSubscribe,
   isBrowser,
+  wsUserOp,
 } from '../utils';
 
 var Choices;
@@ -46,24 +47,24 @@ import { pictrsUri } from '../env';
 const MAX_POST_TITLE_LENGTH = 200;
 
 interface PostFormProps {
-  post?: Post; // If a post is given, that means this is an edit
-  communities?: Community[];
+  post_view?: PostView; // If a post is given, that means this is an edit
+  communities?: CommunityView[];
   params?: PostFormParams;
   onCancel?(): any;
-  onCreate?(id: number): any;
-  onEdit?(post: Post): any;
+  onCreate?(post: PostView): any;
+  onEdit?(post: PostView): any;
   enableNsfw: boolean;
   enableDownvotes: boolean;
 }
 
 interface PostFormState {
-  postForm: PostFormI;
+  postForm: CreatePost;
   loading: boolean;
   imageLoading: boolean;
   previewMode: boolean;
   suggestedTitle: string;
-  suggestedPosts: Post[];
-  crossPosts: Post[];
+  suggestedPosts: PostView[];
+  crossPosts: PostView[];
 }
 
 export class PostForm extends Component<PostFormProps, PostFormState> {
@@ -72,10 +73,10 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
   private choices: any;
   private emptyState: PostFormState = {
     postForm: {
+      community_id: null,
       name: null,
       nsfw: false,
-      auth: null,
-      community_id: null,
+      auth: UserService.Instance.authField(),
     },
     loading: false,
     imageLoading: false,
@@ -93,16 +94,15 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
 
     this.state = this.emptyState;
 
-    if (this.props.post) {
+    // Means its an edit
+    if (this.props.post_view) {
       this.state.postForm = {
-        body: this.props.post.body,
-        // NOTE: debouncing breaks both these for some reason, unless you use defaultValue
-        name: this.props.post.name,
-        community_id: this.props.post.community_id,
-        edit_id: this.props.post.id,
-        url: this.props.post.url,
-        nsfw: this.props.post.nsfw,
-        auth: null,
+        body: this.props.post_view.post.body,
+        name: this.props.post_view.post.name,
+        community_id: this.props.post_view.community.id,
+        url: this.props.post_view.post.url,
+        nsfw: this.props.post_view.post.nsfw,
+        auth: UserService.Instance.authField(),
       };
     }
 
@@ -285,7 +285,7 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
               />
             </div>
           </div>
-          {!this.props.post && (
+          {!this.props.post_view && (
             <div class="form-group row">
               <label class="col-sm-2 col-form-label" htmlFor="post-community">
                 {i18n.t('community')}
@@ -298,11 +298,13 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
                   onInput={linkEvent(this, this.handlePostCommunityChange)}
                 >
                   <option>{i18n.t('select_a_community')}</option>
-                  {this.props.communities.map(community => (
-                    <option value={community.id}>
-                      {community.local
-                        ? community.name
-                        : `${hostname(community.actor_id)}/${community.name}`}
+                  {this.props.communities.map(cv => (
+                    <option value={cv.community.id}>
+                      {cv.community.local
+                        ? cv.community.name
+                        : `${hostname(cv.community.actor_id)}/${
+                            cv.community.name
+                          }`}
                     </option>
                   ))}
                 </select>
@@ -340,13 +342,13 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
                   <svg class="icon icon-spinner spin">
                     <use xlinkHref="#icon-spinner"></use>
                   </svg>
-                ) : this.props.post ? (
+                ) : this.props.post_view ? (
                   capitalizeFirstLetter(i18n.t('save'))
                 ) : (
                   capitalizeFirstLetter(i18n.t('create'))
                 )}
               </button>
-              {this.props.post && (
+              {this.props.post_view && (
                 <button
                   type="button"
                   class="btn btn-secondary"
@@ -370,10 +372,14 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
       i.state.postForm.url = undefined;
     }
 
-    if (i.props.post) {
-      WebSocketService.Instance.editPost(i.state.postForm);
+    if (i.props.post_view) {
+      let form: EditPost = {
+        ...i.state.postForm,
+        edit_id: i.props.post_view.post.id,
+      };
+      WebSocketService.Instance.client.editPost(form);
     } else {
-      WebSocketService.Instance.createPost(i.state.postForm);
+      WebSocketService.Instance.client.createPost(i.state.postForm);
     }
     i.state.loading = true;
     i.setState(i.state);
@@ -396,15 +402,16 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
 
   fetchPageTitle() {
     if (validURL(this.state.postForm.url)) {
-      let form: SearchForm = {
+      let form: Search = {
         q: this.state.postForm.url,
         type_: SearchType.Url,
         sort: SortType.TopAll,
         page: 1,
         limit: 6,
+        auth: UserService.Instance.authField(false),
       };
 
-      WebSocketService.Instance.search(form);
+      WebSocketService.Instance.client.search(form);
 
       // Fetch the page title
       getPageTitle(this.state.postForm.url).then(d => {
@@ -424,17 +431,18 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
   }
 
   fetchSimilarPosts() {
-    let form: SearchForm = {
+    let form: Search = {
       q: this.state.postForm.name,
       type_: SearchType.Posts,
       sort: SortType.TopAll,
       community_id: this.state.postForm.community_id,
       page: 1,
       limit: 6,
+      auth: UserService.Instance.authField(false),
     };
 
     if (this.state.postForm.name !== '') {
-      WebSocketService.Instance.search(form);
+      WebSocketService.Instance.client.search(form);
     } else {
       this.state.suggestedPosts = [];
     }
@@ -570,16 +578,16 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
       }
     }
 
-    if (this.props.post) {
-      this.state.postForm.community_id = this.props.post.community_id;
+    if (this.props.post_view) {
+      this.state.postForm.community_id = this.props.post_view.community.id;
     } else if (
       this.props.params &&
       (this.props.params.community_id || this.props.params.community_name)
     ) {
       if (this.props.params.community_name) {
         let foundCommunityId = this.props.communities.find(
-          r => r.name == this.props.params.community_name
-        ).id;
+          r => r.community.name == this.props.params.community_name
+        ).community.id;
         this.state.postForm.community_id = foundCommunityId;
       } else if (this.props.params.community_id) {
         this.state.postForm.community_id = this.props.params.community_id;
@@ -596,27 +604,27 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
     }
   }
 
-  parseMessage(msg: WebSocketJsonResponse) {
-    let res = wsJsonToRes(msg);
+  parseMessage(msg: any) {
+    let op = wsUserOp(msg);
     if (msg.error) {
       toast(i18n.t(msg.error), 'danger');
       this.state.loading = false;
       this.setState(this.state);
       return;
-    } else if (res.op == UserOperation.CreatePost) {
-      let data = res.data as PostResponse;
-      if (data.post.creator_id == UserService.Instance.user.id) {
+    } else if (op == UserOperation.CreatePost) {
+      let data = wsJsonToRes<PostResponse>(msg).data;
+      if (data.post_view.creator.id == UserService.Instance.user.id) {
         this.state.loading = false;
-        this.props.onCreate(data.post.id);
+        this.props.onCreate(data.post_view);
       }
-    } else if (res.op == UserOperation.EditPost) {
-      let data = res.data as PostResponse;
-      if (data.post.creator_id == UserService.Instance.user.id) {
+    } else if (op == UserOperation.EditPost) {
+      let data = wsJsonToRes<PostResponse>(msg).data;
+      if (data.post_view.creator.id == UserService.Instance.user.id) {
         this.state.loading = false;
-        this.props.onEdit(data.post);
+        this.props.onEdit(data.post_view);
       }
-    } else if (res.op == UserOperation.Search) {
-      let data = res.data as SearchResponse;
+    } else if (op == UserOperation.Search) {
+      let data = wsJsonToRes<SearchResponse>(msg).data;
 
       if (data.type_ == SearchType[SearchType.Posts]) {
         this.state.suggestedPosts = data.posts;
diff --git a/src/shared/components/post-listing.tsx b/src/shared/components/post-listing.tsx
index b9485b7..881b9b0 100644
--- a/src/shared/components/post-listing.tsx
+++ b/src/shared/components/post-listing.tsx
@@ -2,21 +2,21 @@ import { Component, linkEvent } from 'inferno';
 import { Link } from 'inferno-router';
 import { WebSocketService, UserService } from '../services';
 import {
-  Post,
-  CreatePostLikeForm,
-  DeletePostForm,
-  RemovePostForm,
-  LockPostForm,
-  StickyPostForm,
-  SavePostForm,
-  CommunityUser,
-  UserView,
-  BanFromCommunityForm,
-  BanUserForm,
-  AddModToCommunityForm,
-  AddAdminForm,
-  TransferSiteForm,
-  TransferCommunityForm,
+  PostView,
+  CreatePostLike,
+  DeletePost,
+  RemovePost,
+  LockPost,
+  StickyPost,
+  SavePost,
+  UserViewSafe,
+  BanFromCommunity,
+  BanUser,
+  AddModToCommunity,
+  AddAdmin,
+  TransferSite,
+  TransferCommunity,
+  CommunityModeratorView,
 } from 'lemmy-js-client';
 import { BanType } from '../interfaces';
 import { MomentTime } from './moment-time';
@@ -62,11 +62,12 @@ interface PostListingState {
 }
 
 interface PostListingProps {
-  post: Post;
+  post_view: PostView;
+  duplicates?: PostView[];
   showCommunity?: boolean;
   showBody?: boolean;
-  moderators?: CommunityUser[];
-  admins?: UserView[];
+  moderators?: CommunityModeratorView[];
+  admins?: UserViewSafe[];
   enableDownvotes: boolean;
   enableNsfw: boolean;
 }
@@ -87,10 +88,10 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
     viewSource: false,
     showAdvanced: false,
     showMoreMobile: false,
-    my_vote: this.props.post.my_vote,
-    score: this.props.post.score,
-    upvotes: this.props.post.upvotes,
-    downvotes: this.props.post.downvotes,
+    my_vote: this.props.post_view.my_vote,
+    score: this.props.post_view.counts.score,
+    upvotes: this.props.post_view.counts.upvotes,
+    downvotes: this.props.post_view.counts.downvotes,
   };
 
   constructor(props: any, context: any) {
@@ -104,11 +105,11 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
   }
 
   componentWillReceiveProps(nextProps: PostListingProps) {
-    this.state.my_vote = nextProps.post.my_vote;
-    this.state.upvotes = nextProps.post.upvotes;
-    this.state.downvotes = nextProps.post.downvotes;
-    this.state.score = nextProps.post.score;
-    if (this.props.post.id !== nextProps.post.id) {
+    this.state.my_vote = nextProps.post_view.my_vote;
+    this.state.upvotes = nextProps.post_view.counts.upvotes;
+    this.state.downvotes = nextProps.post_view.counts.downvotes;
+    this.state.score = nextProps.post_view.counts.score;
+    if (this.props.post_view.post.id !== nextProps.post_view.post.id) {
       this.state.imageExpanded = false;
     }
     this.setState(this.state);
@@ -125,7 +126,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
         ) : (
           <div class="col-12">
             <PostForm
-              post={this.props.post}
+              post_view={this.props.post_view}
               onEdit={this.handleEditPost}
               onCancel={this.handleEditCancel}
               enableNsfw={this.props.enableNsfw}
@@ -138,22 +139,21 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
   }
 
   body() {
+    let post = this.props.post_view.post;
     return (
       <div class="row">
         <div class="col-12">
-          {this.props.post.url &&
-            this.props.showBody &&
-            this.props.post.embed_title && (
-              <IFramelyCard post={this.props.post} />
-            )}
+          {post.url && this.props.showBody && post.embed_title && (
+            <IFramelyCard post={post} />
+          )}
           {this.props.showBody &&
-            this.props.post.body &&
+            post.body &&
             (this.state.viewSource ? (
-              <pre>{this.props.post.body}</pre>
+              <pre>{post.body}</pre>
             ) : (
               <div
                 className="md-div"
-                dangerouslySetInnerHTML={mdToHtml(this.props.post.body)}
+                dangerouslySetInnerHTML={mdToHtml(post.body)}
               />
             ))}
         </div>
@@ -162,18 +162,18 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
   }
 
   imgThumb(src: string) {
-    let post = this.props.post;
+    let post_view = this.props.post_view;
     return (
       <PictrsImage
         src={src}
         thumbnail
-        nsfw={post.nsfw || post.community_nsfw}
+        nsfw={post_view.post.nsfw || post_view.community.nsfw}
       />
     );
   }
 
   getImageSrc(): string {
-    let post = this.props.post;
+    let post = this.props.post_view.post;
     if (isImage(post.url)) {
       if (post.url.includes('pictrs')) {
         return post.url;
@@ -190,7 +190,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
   }
 
   thumbnail() {
-    let post = this.props.post;
+    let post = this.props.post_view.post;
 
     if (isImage(post.url)) {
       return (
@@ -270,21 +270,11 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
   }
 
   createdLine() {
-    let post = this.props.post;
+    let post_view = this.props.post_view;
     return (
       <ul class="list-inline mb-1 text-muted small">
         <li className="list-inline-item">
-          <UserListing
-            user={{
-              name: post.creator_name,
-              preferred_username: post.creator_preferred_username,
-              avatar: post.creator_avatar,
-              id: post.creator_id,
-              local: post.creator_local,
-              actor_id: post.creator_actor_id,
-              published: post.creator_published,
-            }}
-          />
+          <UserListing user={post_view.creator} />
 
           {this.isMod && (
             <span className="mx-1 badge badge-light">{i18n.t('mod')}</span>
@@ -292,36 +282,29 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
           {this.isAdmin && (
             <span className="mx-1 badge badge-light">{i18n.t('admin')}</span>
           )}
-          {(post.banned_from_community || post.banned) && (
+          {(post_view.creator_banned_from_community ||
+            post_view.creator.banned) && (
             <span className="mx-1 badge badge-danger">{i18n.t('banned')}</span>
           )}
           {this.props.showCommunity && (
             <span>
               <span class="mx-1"> {i18n.t('to')} </span>
-              <CommunityLink
-                community={{
-                  name: post.community_name,
-                  id: post.community_id,
-                  local: post.community_local,
-                  actor_id: post.community_actor_id,
-                  icon: post.community_icon,
-                }}
-              />
+              <CommunityLink community={post_view.community} />
             </span>
           )}
         </li>
         <li className="list-inline-item">•</li>
-        {post.url && !(hostname(post.url) == externalHost) && (
+        {post_view.post.url && !(hostname(post_view.post.url) == externalHost) && (
           <>
             <li className="list-inline-item">
               <a
                 className="text-muted font-italic"
-                href={post.url}
+                href={post_view.post.url}
                 target="_blank"
-                title={post.url}
+                title={post_view.post.url}
                 rel="noopener"
               >
-                {hostname(post.url)}
+                {hostname(post_view.post.url)}
               </a>
             </li>
             <li className="list-inline-item">•</li>
@@ -329,19 +312,21 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
         )}
         <li className="list-inline-item">
           <span>
-            <MomentTime data={post} />
+            <MomentTime data={post_view.post} />
           </span>
         </li>
-        {post.body && (
+        {post_view.post.body && (
           <>
             <li className="list-inline-item">•</li>
             <li className="list-inline-item">
               {/* Using a link with tippy doesn't work on touch devices unfortunately */}
               <Link
                 className="text-muted"
-                data-tippy-content={md.render(previewLines(post.body))}
+                data-tippy-content={md.render(
+                  previewLines(post_view.post.body)
+                )}
                 data-tippy-allowHtml={true}
-                to={`/post/${post.id}`}
+                to={`/post/${post_view.post.id}`}
               >
                 <svg class="mr-1 icon icon-inline">
                   <use xlinkHref="#icon-book-open"></use>
@@ -392,7 +377,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
   }
 
   postTitleLine() {
-    let post = this.props.post;
+    let post = this.props.post_view.post;
     return (
       <div className="post-title overflow-hidden">
         <h5>
@@ -415,7 +400,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
               {post.name}
             </Link>
           )}
-          {(isImage(post.url) || this.props.post.thumbnail_url) &&
+          {(isImage(post.url) || post.thumbnail_url) &&
             (!this.state.imageExpanded ? (
               <span
                 class="text-monospace unselectable pointer ml-2 text-muted small"
@@ -492,22 +477,22 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
   }
 
   commentsLine(mobile: boolean = false) {
-    let post = this.props.post;
+    let post_view = this.props.post_view;
     return (
       <div class="d-flex justify-content-between justify-content-lg-start flex-wrap text-muted font-weight-bold mb-1">
         <button class="btn btn-link text-muted p-0">
           <Link
             className="text-muted small"
             title={i18n.t('number_of_comments', {
-              count: post.number_of_comments,
+              count: post_view.counts.comments,
             })}
-            to={`/post/${post.id}`}
+            to={`/post/${post_view.post.id}`}
           >
             <svg class="mr-1 icon icon-inline">
               <use xlinkHref="#icon-message-square"></use>
             </svg>
             {i18n.t('number_of_comments', {
-              count: post.number_of_comments,
+              count: post_view.counts.comments,
             })}
           </Link>
         </button>
@@ -531,12 +516,14 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
                 class="btn btn-link btn-animate text-muted py-0"
                 onClick={linkEvent(this, this.handleSavePostClick)}
                 data-tippy-content={
-                  post.saved ? i18n.t('unsave') : i18n.t('save')
+                  post_view.saved ? i18n.t('unsave') : i18n.t('save')
                 }
               >
                 <small>
                   <svg
-                    class={`icon icon-inline ${post.saved && 'text-warning'}`}
+                    class={`icon icon-inline ${
+                      post_view.saved && 'text-warning'
+                    }`}
                   >
                     <use xlinkHref="#icon-star"></use>
                   </svg>
@@ -583,10 +570,12 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
               class="btn btn-link btn-animate text-muted py-0 pl-1 pr-0"
               onClick={linkEvent(this, this.handleSavePostClick)}
               data-tippy-content={
-                post.saved ? i18n.t('unsave') : i18n.t('save')
+                post_view.saved ? i18n.t('unsave') : i18n.t('save')
               }
             >
-              <svg class={`icon icon-inline ${post.saved && 'text-warning'}`}>
+              <svg
+                class={`icon icon-inline ${post_view.saved && 'text-warning'}`}
+              >
                 <use xlinkHref="#icon-star"></use>
               </svg>
             </button>
@@ -611,20 +600,18 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
 
   duplicatesLine() {
     return (
-      this.props.post.duplicates && (
+      this.props.duplicates && (
         <ul class="list-inline mb-1 small text-muted">
           <>
             <li className="list-inline-item mr-2">
               {i18n.t('cross_posted_to')}
             </li>
-            {this.props.post.duplicates.map(post => (
+            {this.props.duplicates.map(pv => (
               <li className="list-inline-item mr-2">
-                <Link to={`/post/${post.id}`}>
-                  {post.community_local
-                    ? post.community_name
-                    : `${post.community_name}@${hostname(
-                        post.community_actor_id
-                      )}`}
+                <Link to={`/post/${pv.post.id}`}>
+                  {pv.community.local
+                    ? pv.community.name
+                    : `${pv.community.name}@${hostname(pv.community.actor_id)}`}
                 </Link>
               </li>
             ))}
@@ -635,7 +622,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
   }
 
   postActions(mobile: boolean = false) {
-    let post = this.props.post;
+    let post_view = this.props.post_view;
     return (
       UserService.Instance.user && (
         <>
@@ -646,11 +633,13 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
                   class="btn btn-link btn-animate text-muted py-0 pl-0"
                   onClick={linkEvent(this, this.handleSavePostClick)}
                   data-tippy-content={
-                    post.saved ? i18n.t('unsave') : i18n.t('save')
+                    post_view.saved ? i18n.t('unsave') : i18n.t('save')
                   }
                 >
                   <svg
-                    class={`icon icon-inline ${post.saved && 'text-warning'}`}
+                    class={`icon icon-inline ${
+                      post_view.saved && 'text-warning'
+                    }`}
                   >
                     <use xlinkHref="#icon-star"></use>
                   </svg>
@@ -682,11 +671,13 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
                 class="btn btn-link btn-animate text-muted py-0"
                 onClick={linkEvent(this, this.handleDeleteClick)}
                 data-tippy-content={
-                  !post.deleted ? i18n.t('delete') : i18n.t('restore')
+                  !post_view.post.deleted ? i18n.t('delete') : i18n.t('restore')
                 }
               >
                 <svg
-                  class={`icon icon-inline ${post.deleted && 'text-danger'}`}
+                  class={`icon icon-inline ${
+                    post_view.post.deleted && 'text-danger'
+                  }`}
                 >
                   <use xlinkHref="#icon-trash"></use>
                 </svg>
@@ -706,7 +697,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
             </button>
           ) : (
             <>
-              {this.props.showBody && post.body && (
+              {this.props.showBody && post_view.post.body && (
                 <button
                   class="btn btn-link btn-animate text-muted py-0"
                   onClick={linkEvent(this, this.handleViewSource)}
@@ -727,11 +718,13 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
                     class="btn btn-link btn-animate text-muted py-0"
                     onClick={linkEvent(this, this.handleModLock)}
                     data-tippy-content={
-                      post.locked ? i18n.t('unlock') : i18n.t('lock')
+                      post_view.post.locked ? i18n.t('unlock') : i18n.t('lock')
                     }
                   >
                     <svg
-                      class={`icon icon-inline ${post.locked && 'text-danger'}`}
+                      class={`icon icon-inline ${
+                        post_view.post.locked && 'text-danger'
+                      }`}
                     >
                       <use xlinkHref="#icon-lock"></use>
                     </svg>
@@ -740,12 +733,14 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
                     class="btn btn-link btn-animate text-muted py-0"
                     onClick={linkEvent(this, this.handleModSticky)}
                     data-tippy-content={
-                      post.stickied ? i18n.t('unsticky') : i18n.t('sticky')
+                      post_view.post.stickied
+                        ? i18n.t('unsticky')
+                        : i18n.t('sticky')
                     }
                   >
                     <svg
                       class={`icon icon-inline ${
-                        post.stickied && 'text-success'
+                        post_view.post.stickied && 'text-success'
                       }`}
                     >
                       <use xlinkHref="#icon-pin"></use>
@@ -755,7 +750,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
               )}
               {/* Mods can ban from community, and appoint as mods to community */}
               {(this.canMod || this.canAdmin) &&
-                (!post.removed ? (
+                (!post_view.post.removed ? (
                   <button
                     class="btn btn-link btn-animate text-muted py-0"
                     onClick={linkEvent(this, this.handleModRemoveShow)}
@@ -773,7 +768,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
               {this.canMod && (
                 <>
                   {!this.isMod &&
-                    (!post.banned_from_community ? (
+                    (!post_view.creator_banned_from_community ? (
                       <button
                         class="btn btn-link btn-animate text-muted py-0"
                         onClick={linkEvent(
@@ -794,22 +789,23 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
                         {i18n.t('unban')}
                       </button>
                     ))}
-                  {!post.banned_from_community && post.creator_local && (
-                    <button
-                      class="btn btn-link btn-animate text-muted py-0"
-                      onClick={linkEvent(this, this.handleAddModToCommunity)}
-                    >
-                      {this.isMod
-                        ? i18n.t('remove_as_mod')
-                        : i18n.t('appoint_as_mod')}
-                    </button>
-                  )}
+                  {!post_view.creator_banned_from_community &&
+                    post_view.creator.local && (
+                      <button
+                        class="btn btn-link btn-animate text-muted py-0"
+                        onClick={linkEvent(this, this.handleAddModToCommunity)}
+                      >
+                        {this.isMod
+                          ? i18n.t('remove_as_mod')
+                          : i18n.t('appoint_as_mod')}
+                      </button>
+                    )}
                 </>
               )}
               {/* Community creators and admins can transfer community to another mod */}
               {(this.amCommunityCreator || this.canAdmin) &&
                 this.isMod &&
-                post.creator_local &&
+                post_view.creator.local &&
                 (!this.state.showConfirmTransferCommunity ? (
                   <button
                     class="btn btn-link btn-animate text-muted py-0"
@@ -846,7 +842,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
               {this.canAdmin && (
                 <>
                   {!this.isAdmin &&
-                    (!post.banned ? (
+                    (!post_view.creator.banned ? (
                       <button
                         class="btn btn-link btn-animate text-muted py-0"
                         onClick={linkEvent(this, this.handleModBanShow)}
@@ -861,7 +857,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
                         {i18n.t('unban_from_site')}
                       </button>
                     ))}
-                  {!post.banned && post.creator_local && (
+                  {!post_view.creator.banned && post_view.creator.local && (
                     <button
                       class="btn btn-link btn-animate text-muted py-0"
                       onClick={linkEvent(this, this.handleAddAdmin)}
@@ -916,7 +912,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
   }
 
   removeAndBanDialogs() {
-    let post = this.props.post;
+    let post = this.props.post_view;
     return (
       <>
         {this.state.showRemoveDialog && (
@@ -972,7 +968,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
             {/* </div> */}
             <div class="form-group row">
               <button type="submit" class="btn btn-secondary">
-                {i18n.t('ban')} {post.creator_name}
+                {i18n.t('ban')} {post.creator.name}
               </button>
             </div>
           </form>
@@ -982,7 +978,8 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
   }
 
   mobileThumbnail() {
-    return this.props.post.thumbnail_url || isImage(this.props.post.url) ? (
+    let post = this.props.post_view.post;
+    return post.thumbnail_url || isImage(post.url) ? (
       <div class="row">
         <div className={`${this.state.imageExpanded ? 'col-12' : 'col-8'}`}>
           {this.postTitleLine()}
@@ -998,13 +995,14 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
   }
 
   showMobilePreview() {
+    let post = this.props.post_view.post;
     return (
-      this.props.post.body &&
+      post.body &&
       !this.props.showBody && (
         <div
           className="md-div mb-1"
           dangerouslySetInnerHTML={{
-            __html: md.render(previewLines(this.props.post.body)),
+            __html: md.render(previewLines(post.body)),
           }}
         />
       )
@@ -1067,7 +1065,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
   private get myPost(): boolean {
     return (
       UserService.Instance.user &&
-      this.props.post.creator_id == UserService.Instance.user.id
+      this.props.post_view.creator.id == UserService.Instance.user.id
     );
   }
 
@@ -1075,8 +1073,8 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
     return (
       this.props.moderators &&
       isMod(
-        this.props.moderators.map(m => m.user_id),
-        this.props.post.creator_id
+        this.props.moderators.map(m => m.moderator.id),
+        this.props.post_view.creator.id
       )
     );
   }
@@ -1085,8 +1083,8 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
     return (
       this.props.admins &&
       isMod(
-        this.props.admins.map(a => a.id),
-        this.props.post.creator_id
+        this.props.admins.map(a => a.user.id),
+        this.props.post_view.creator.id
       )
     );
   }
@@ -1094,13 +1092,13 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
   get canMod(): boolean {
     if (this.props.admins && this.props.moderators) {
       let adminsThenMods = this.props.admins
-        .map(a => a.id)
-        .concat(this.props.moderators.map(m => m.user_id));
+        .map(a => a.user.id)
+        .concat(this.props.moderators.map(m => m.moderator.id));
 
       return canMod(
         UserService.Instance.user,
         adminsThenMods,
-        this.props.post.creator_id
+        this.props.post_view.creator.id
       );
     } else {
       return false;
@@ -1110,13 +1108,13 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
   get canModOnSelf(): boolean {
     if (this.props.admins && this.props.moderators) {
       let adminsThenMods = this.props.admins
-        .map(a => a.id)
-        .concat(this.props.moderators.map(m => m.user_id));
+        .map(a => a.user.id)
+        .concat(this.props.moderators.map(m => m.moderator.id));
 
       return canMod(
         UserService.Instance.user,
         adminsThenMods,
-        this.props.post.creator_id,
+        this.props.post_view.creator.id,
         true
       );
     } else {
@@ -1129,8 +1127,8 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
       this.props.admins &&
       canMod(
         UserService.Instance.user,
-        this.props.admins.map(a => a.id),
-        this.props.post.creator_id
+        this.props.admins.map(a => a.user.id),
+        this.props.post_view.creator.id
       )
     );
   }
@@ -1139,8 +1137,8 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
     return (
       this.props.moderators &&
       UserService.Instance.user &&
-      this.props.post.creator_id != UserService.Instance.user.id &&
-      UserService.Instance.user.id == this.props.moderators[0].user_id
+      this.props.post_view.creator.id != UserService.Instance.user.id &&
+      UserService.Instance.user.id == this.props.moderators[0].moderator.id
     );
   }
 
@@ -1148,8 +1146,8 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
     return (
       this.props.admins &&
       UserService.Instance.user &&
-      this.props.post.creator_id != UserService.Instance.user.id &&
-      UserService.Instance.user.id == this.props.admins[0].id
+      this.props.post_view.creator.id != UserService.Instance.user.id &&
+      UserService.Instance.user.id == this.props.admins[0].user.id
     );
   }
 
@@ -1174,12 +1172,13 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
 
     i.state.my_vote = new_vote;
 
-    let form: CreatePostLikeForm = {
-      post_id: i.props.post.id,
+    let form: CreatePostLike = {
+      post_id: i.props.post_view.post.id,
       score: i.state.my_vote,
+      auth: UserService.Instance.authField(),
     };
 
-    WebSocketService.Instance.likePost(form);
+    WebSocketService.Instance.client.likePost(form);
     i.setState(i.state);
     setupTippy();
   }
@@ -1205,12 +1204,13 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
 
     i.state.my_vote = new_vote;
 
-    let form: CreatePostLikeForm = {
-      post_id: i.props.post.id,
+    let form: CreatePostLike = {
+      post_id: i.props.post_view.post.id,
       score: i.state.my_vote,
+      auth: UserService.Instance.authField(),
     };
 
-    WebSocketService.Instance.likePost(form);
+    WebSocketService.Instance.client.likePost(form);
     i.setState(i.state);
     setupTippy();
   }
@@ -1232,33 +1232,35 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
   }
 
   handleDeleteClick(i: PostListing) {
-    let deleteForm: DeletePostForm = {
-      edit_id: i.props.post.id,
-      deleted: !i.props.post.deleted,
-      auth: null,
+    let deleteForm: DeletePost = {
+      edit_id: i.props.post_view.post.id,
+      deleted: !i.props.post_view.post.deleted,
+      auth: UserService.Instance.authField(),
     };
-    WebSocketService.Instance.deletePost(deleteForm);
+    WebSocketService.Instance.client.deletePost(deleteForm);
   }
 
   handleSavePostClick(i: PostListing) {
-    let saved = i.props.post.saved == undefined ? true : !i.props.post.saved;
-    let form: SavePostForm = {
-      post_id: i.props.post.id,
+    let saved =
+      i.props.post_view.saved == undefined ? true : !i.props.post_view.saved;
+    let form: SavePost = {
+      post_id: i.props.post_view.post.id,
       save: saved,
+      auth: UserService.Instance.authField(),
     };
 
-    WebSocketService.Instance.savePost(form);
+    WebSocketService.Instance.client.savePost(form);
   }
 
   get crossPostParams(): string {
-    let params = `?title=${this.props.post.name}`;
-    let post = this.props.post;
+    let post = this.props.post_view.post;
+    let params = `?title=${post.name}`;
 
     if (post.url) {
       params += `&url=${encodeURIComponent(post.url)}`;
     }
-    if (this.props.post.body) {
-      params += `&body=${this.props.post.body}`;
+    if (post.body) {
+      params += `&body=${post.body}`;
     }
     return params;
   }
@@ -1280,34 +1282,34 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
 
   handleModRemoveSubmit(i: PostListing) {
     event.preventDefault();
-    let form: RemovePostForm = {
-      edit_id: i.props.post.id,
-      removed: !i.props.post.removed,
+    let form: RemovePost = {
+      edit_id: i.props.post_view.post.id,
+      removed: !i.props.post_view.post.removed,
       reason: i.state.removeReason,
-      auth: null,
+      auth: UserService.Instance.authField(),
     };
-    WebSocketService.Instance.removePost(form);
+    WebSocketService.Instance.client.removePost(form);
 
     i.state.showRemoveDialog = false;
     i.setState(i.state);
   }
 
   handleModLock(i: PostListing) {
-    let form: LockPostForm = {
-      edit_id: i.props.post.id,
-      locked: !i.props.post.locked,
-      auth: null,
+    let form: LockPost = {
+      edit_id: i.props.post_view.post.id,
+      locked: !i.props.post_view.post.locked,
+      auth: UserService.Instance.authField(),
     };
-    WebSocketService.Instance.lockPost(form);
+    WebSocketService.Instance.client.lockPost(form);
   }
 
   handleModSticky(i: PostListing) {
-    let form: StickyPostForm = {
-      edit_id: i.props.post.id,
-      stickied: !i.props.post.stickied,
-      auth: null,
+    let form: StickyPost = {
+      edit_id: i.props.post_view.post.id,
+      stickied: !i.props.post_view.post.stickied,
+      auth: UserService.Instance.authField(),
     };
-    WebSocketService.Instance.stickyPost(form);
+    WebSocketService.Instance.client.stickyPost(form);
   }
 
   handleModBanFromCommunityShow(i: PostListing) {
@@ -1349,33 +1351,35 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
 
     if (i.state.banType == BanType.Community) {
       // If its an unban, restore all their data
-      let ban = !i.props.post.banned_from_community;
+      let ban = !i.props.post_view.creator_banned_from_community;
       if (ban == false) {
         i.state.removeData = false;
       }
-      let form: BanFromCommunityForm = {
-        user_id: i.props.post.creator_id,
-        community_id: i.props.post.community_id,
+      let form: BanFromCommunity = {
+        user_id: i.props.post_view.creator.id,
+        community_id: i.props.post_view.community.id,
         ban,
         remove_data: i.state.removeData,
         reason: i.state.banReason,
         expires: getUnixTime(i.state.banExpires),
+        auth: UserService.Instance.authField(),
       };
-      WebSocketService.Instance.banFromCommunity(form);
+      WebSocketService.Instance.client.banFromCommunity(form);
     } else {
       // If its an unban, restore all their data
-      let ban = !i.props.post.banned;
+      let ban = !i.props.post_view.creator.banned;
       if (ban == false) {
         i.state.removeData = false;
       }
-      let form: BanUserForm = {
-        user_id: i.props.post.creator_id,
+      let form: BanUser = {
+        user_id: i.props.post_view.creator.id,
         ban,
         remove_data: i.state.removeData,
         reason: i.state.banReason,
         expires: getUnixTime(i.state.banExpires),
+        auth: UserService.Instance.authField(),
       };
-      WebSocketService.Instance.banUser(form);
+      WebSocketService.Instance.client.banUser(form);
     }
 
     i.state.showBanDialog = false;
@@ -1383,21 +1387,23 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
   }
 
   handleAddModToCommunity(i: PostListing) {
-    let form: AddModToCommunityForm = {
-      user_id: i.props.post.creator_id,
-      community_id: i.props.post.community_id,
+    let form: AddModToCommunity = {
+      user_id: i.props.post_view.creator.id,
+      community_id: i.props.post_view.community.id,
       added: !i.isMod,
+      auth: UserService.Instance.authField(),
     };
-    WebSocketService.Instance.addModToCommunity(form);
+    WebSocketService.Instance.client.addModToCommunity(form);
     i.setState(i.state);
   }
 
   handleAddAdmin(i: PostListing) {
-    let form: AddAdminForm = {
-      user_id: i.props.post.creator_id,
+    let form: AddAdmin = {
+      user_id: i.props.post_view.creator.id,
       added: !i.isAdmin,
+      auth: UserService.Instance.authField(),
     };
-    WebSocketService.Instance.addAdmin(form);
+    WebSocketService.Instance.client.addAdmin(form);
     i.setState(i.state);
   }
 
@@ -1412,11 +1418,12 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
   }
 
   handleTransferCommunity(i: PostListing) {
-    let form: TransferCommunityForm = {
-      community_id: i.props.post.community_id,
-      user_id: i.props.post.creator_id,
+    let form: TransferCommunity = {
+      community_id: i.props.post_view.community.id,
+      user_id: i.props.post_view.creator.id,
+      auth: UserService.Instance.authField(),
     };
-    WebSocketService.Instance.transferCommunity(form);
+    WebSocketService.Instance.client.transferCommunity(form);
     i.state.showConfirmTransferCommunity = false;
     i.setState(i.state);
   }
@@ -1432,10 +1439,11 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
   }
 
   handleTransferSite(i: PostListing) {
-    let form: TransferSiteForm = {
-      user_id: i.props.post.creator_id,
+    let form: TransferSite = {
+      user_id: i.props.post_view.creator.id,
+      auth: UserService.Instance.authField(),
     };
-    WebSocketService.Instance.transferSite(form);
+    WebSocketService.Instance.client.transferSite(form);
     i.state.showConfirmTransferSite = false;
     i.setState(i.state);
   }
diff --git a/src/shared/components/post-listings.tsx b/src/shared/components/post-listings.tsx
index 47aab75..a7e0693 100644
--- a/src/shared/components/post-listings.tsx
+++ b/src/shared/components/post-listings.tsx
@@ -1,13 +1,13 @@
 import { Component } from 'inferno';
 import { Link } from 'inferno-router';
-import { Post, SortType } from 'lemmy-js-client';
+import { PostView, SortType } from 'lemmy-js-client';
 import { postSort } from '../utils';
 import { PostListing } from './post-listing';
 import { i18n } from '../i18next';
 import { T } from 'inferno-i18next';
 
 interface PostListingsProps {
-  posts: Post[];
+  posts: PostView[];
   showCommunity?: boolean;
   removeDuplicates?: boolean;
   sort?: SortType;
@@ -16,6 +16,8 @@ interface PostListingsProps {
 }
 
 export class PostListings extends Component<PostListingsProps, any> {
+  private duplicatesMap = new Map<number, PostView[]>();
+
   constructor(props: any, context: any) {
     super(props, context);
   }
@@ -24,10 +26,11 @@ export class PostListings extends Component<PostListingsProps, any> {
     return (
       <div>
         {this.props.posts.length > 0 ? (
-          this.outer().map(post => (
+          this.outer().map(post_view => (
             <>
               <PostListing
-                post={post}
+                post_view={post_view}
+                duplicates={this.duplicatesMap.get(post_view.post.id)}
                 showCommunity={this.props.showCommunity}
                 enableDownvotes={this.props.enableDownvotes}
                 enableNsfw={this.props.enableNsfw}
@@ -49,7 +52,7 @@ export class PostListings extends Component<PostListingsProps, any> {
     );
   }
 
-  outer(): Post[] {
+  outer(): PostView[] {
     let out = this.props.posts;
     if (this.props.removeDuplicates) {
       out = this.removeDuplicates(out);
@@ -62,23 +65,23 @@ export class PostListings extends Component<PostListingsProps, any> {
     return out;
   }
 
-  removeDuplicates(posts: Post[]): Post[] {
+  removeDuplicates(posts: PostView[]): PostView[] {
     // A map from post url to list of posts (dupes)
-    let urlMap = new Map<string, Post[]>();
+    let urlMap = new Map<string, PostView[]>();
 
     // Loop over the posts, find ones with same urls
-    for (let post of posts) {
+    for (let pv of posts) {
       if (
-        post.url &&
-        !post.deleted &&
-        !post.removed &&
-        !post.community_deleted &&
-        !post.community_removed
+        pv.post.url &&
+        !pv.post.deleted &&
+        !pv.post.removed &&
+        !pv.community.deleted &&
+        !pv.community.removed
       ) {
-        if (!urlMap.get(post.url)) {
-          urlMap.set(post.url, [post]);
+        if (!urlMap.get(pv.post.url)) {
+          urlMap.set(pv.post.url, [pv]);
         } else {
-          urlMap.get(post.url).push(post);
+          urlMap.get(pv.post.url).push(pv);
         }
       }
     }
@@ -89,18 +92,18 @@ export class PostListings extends Component<PostListingsProps, any> {
       if (e[1].length == 1) {
         urlMap.delete(e[0]);
       } else {
-        e[1].sort((a, b) => a.published.localeCompare(b.published));
+        e[1].sort((a, b) => a.post.published.localeCompare(b.post.published));
       }
     }
 
     for (let i = 0; i < posts.length; i++) {
-      let post = posts[i];
-      if (post.url) {
-        let found = urlMap.get(post.url);
+      let pv = posts[i];
+      if (pv.post.url) {
+        let found = urlMap.get(pv.post.url);
         if (found) {
           // If its the oldest, add
-          if (post.id == found[0].id) {
-            post.duplicates = found.slice(1);
+          if (pv.post.id == found[0].post.id) {
+            this.duplicatesMap.set(pv.post.id, found.slice(1));
           }
           // Otherwise, delete it
           else {
diff --git a/src/shared/components/post.tsx b/src/shared/components/post.tsx
index 017ff45..1d800fc 100644
--- a/src/shared/components/post.tsx
+++ b/src/shared/components/post.tsx
@@ -3,25 +3,23 @@ import { HtmlTags } from './html-tags';
 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';
@@ -29,6 +27,7 @@ import {
   CommentSortType,
   CommentViewType,
   InitialFetchRequest,
+  CommentNode as CommentNodeI,
 } from '../interfaces';
 import { WebSocketService, UserService } from '../services';
 import {
@@ -44,10 +43,10 @@ import {
   getIdFromProps,
   getCommentIdFromProps,
   wsSubscribe,
-  setAuth,
   isBrowser,
   previewLines,
   isImage,
+  wsUserOp,
 } from '../utils';
 import { PostListing } from './post-listing';
 import { Sidebar } from './sidebar';
@@ -64,7 +63,7 @@ interface PostState {
   commentViewType: CommentViewType;
   scrolled?: boolean;
   loading: boolean;
-  crossPosts: PostI[];
+  crossPosts: PostView[];
   siteRes: GetSiteResponse;
   categories: Category[];
 }
@@ -81,7 +80,7 @@ export class Post extends Component<any, PostState> {
     scrolled: false,
     loading: true,
     crossPosts: [],
-    siteRes: this.isoData.site,
+    siteRes: this.isoData.site_res,
     categories: [],
   };
 
@@ -104,15 +103,16 @@ export class Post extends Component<any, PostState> {
       }
     } else {
       this.fetchPost();
-      WebSocketService.Instance.listCategories();
+      WebSocketService.Instance.client.listCategories();
     }
   }
 
   fetchPost() {
-    let form: GetPostForm = {
+    let form: GetPost = {
       id: this.state.postId,
+      auth: UserService.Instance.authField(false),
     };
-    WebSocketService.Instance.getPost(form);
+    WebSocketService.Instance.client.getPost(form);
   }
 
   static fetchInitialData(req: InitialFetchRequest): Promise<any>[] {
@@ -121,10 +121,10 @@ export class Post extends Component<any, PostState> {
 
     let id = Number(pathSplit[2]);
 
-    let postForm: GetPostForm = {
+    let postForm: GetPost = {
       id,
+      auth: req.auth,
     };
-    setAuth(postForm, req.auth);
 
     promises.push(req.client.getPost(postForm));
     promises.push(req.client.listCategories());
@@ -138,7 +138,7 @@ export class Post extends Component<any, PostState> {
   }
 
   componentDidMount() {
-    WebSocketService.Instance.postJoin({ post_id: this.state.postId });
+    WebSocketService.Instance.client.postJoin({ post_id: this.state.postId });
     autosize(document.querySelectorAll('textarea'));
   }
 
@@ -172,23 +172,28 @@ export class Post extends Component<any, PostState> {
     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.creator.id,
         read: true,
-        auth: null,
+        auth: UserService.Instance.authField(),
       };
-      WebSocketService.Instance.markCommentAsRead(form);
+      WebSocketService.Instance.client.markCommentAsRead(form);
       UserService.Instance.unreadCountSub.next(
         UserService.Instance.unreadCountSub.value - 1
       );
@@ -196,27 +201,24 @@ export class Post extends Component<any, PostState> {
   }
 
   get documentTitle(): string {
-    return `${this.state.postRes.post.name} - ${this.state.siteRes.site.name}`;
+    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 (
-      this.state.postRes.post.thumbnail_url ||
-      (this.state.postRes.post.url
-        ? isImage(this.state.postRes.post.url)
-          ? this.state.postRes.post.url
-          : undefined
-        : undefined)
+      post.thumbnail_url ||
+      (post.url ? (isImage(post.url) ? post.url : undefined) : undefined)
     );
   }
 
   get descriptionTag(): string {
-    return this.state.postRes.post.body
-      ? previewLines(this.state.postRes.post.body)
-      : undefined;
+    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">
         {this.state.loading ? (
@@ -235,18 +237,21 @@ export class Post extends Component<any, PostState> {
                 description={this.descriptionTag}
               />
               <PostListing
-                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 &&
@@ -343,12 +348,12 @@ export class Post extends Component<any, PostState> {
         <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}
+          enableDownvotes={this.state.siteRes.site_view.site.enable_downvotes}
           sort={this.state.commentSort}
         />
       </div>
@@ -359,11 +364,11 @@ 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}
         />
@@ -385,18 +390,18 @@ export class Post extends Component<any, PostState> {
 
   buildCommentsTree(): CommentNodeI[] {
     let map = new Map<number, CommentNodeI>();
-    for (let comment of this.state.postRes.comments) {
+    for (let comment_view of this.state.postRes.comments) {
       let node: CommentNodeI = {
-        comment: comment,
+        comment_view: comment_view,
         children: [],
       };
-      map.set(comment.id, { ...node });
+      map.set(comment_view.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);
+    for (let comment_view of this.state.postRes.comments) {
+      let child = map.get(comment_view.comment.id);
+      if (comment_view.comment.parent_id) {
+        let parent_ = map.get(comment_view.comment.parent_id);
         parent_.children.push(child);
       } else {
         tree.push(child);
@@ -410,7 +415,7 @@ export class Post extends Component<any, PostState> {
 
   setDepth(node: CommentNodeI, i: number = 0): void {
     for (let child of node.children) {
-      child.comment.depth = i;
+      child.depth = i;
       this.setDepth(child, i + 1);
     }
   }
@@ -421,155 +426,146 @@ export class Post extends Component<any, PostState> {
       <div>
         <CommentNodes
           nodes={nodes}
-          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}
           sort={this.state.commentSort}
-          enableDownvotes={this.state.siteRes.site.enable_downvotes}
+          enableDownvotes={this.state.siteRes.site_view.site.enable_downvotes}
         />
       </div>
     );
   }
 
-  parseMessage(msg: WebSocketJsonResponse) {
-    console.log(msg);
-    let res = wsJsonToRes(msg);
+  parseMessage(msg: any) {
+    let op = wsUserOp(msg);
     if (msg.error) {
       toast(i18n.t(msg.error), 'danger');
       return;
     } else if (msg.reconnect) {
       let postId = Number(this.props.match.params.id);
-      WebSocketService.Instance.postJoin({ post_id: postId });
-      WebSocketService.Instance.getPost({
+      WebSocketService.Instance.client.postJoin({ post_id: postId });
+      WebSocketService.Instance.client.getPost({
         id: postId,
+        auth: UserService.Instance.authField(false),
       });
-    } else if (res.op == UserOperation.GetPost) {
-      let data = res.data as GetPostResponse;
+    } else if (op == UserOperation.GetPost) {
+      let data = wsJsonToRes<GetPostResponse>(msg).data;
       this.state.postRes = data;
       this.state.loading = false;
 
       // Get cross-posts
-      if (this.state.postRes.post.url) {
-        let form: SearchForm = {
-          q: this.state.postRes.post.url,
+      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: UserService.Instance.authField(false),
         };
-        WebSocketService.Instance.search(form);
+        WebSocketService.Instance.client.search(form);
       }
 
       this.setState(this.state);
       setupTippy();
-    } else if (res.op == UserOperation.CreateComment) {
-      let data = res.data as CommentResponse;
+    } else if (op == UserOperation.CreateComment) {
+      let data = wsJsonToRes<CommentResponse>(msg).data;
 
       // Necessary since it might be a user reply
       if (data.recipient_ids.length == 0) {
-        this.state.postRes.comments.unshift(data.comment);
+        this.state.postRes.comments.unshift(data.comment_view);
         this.setState(this.state);
       }
     } 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;
+    } else if (op == UserOperation.ListCategories) {
+      let data = wsJsonToRes<ListCategoriesResponse>(msg).data;
       this.state.categories = data.categories;
       this.setState(this.state);
     }
diff --git a/src/shared/components/private-message-form.tsx b/src/shared/components/private-message-form.tsx
index 6abc5f3..a49f6ed 100644
--- a/src/shared/components/private-message-form.tsx
+++ b/src/shared/components/private-message-form.tsx
@@ -2,15 +2,14 @@ import { Component, linkEvent } from 'inferno';
 import { Prompt } from 'inferno-router';
 import { Subscription } from 'rxjs';
 import {
-  PrivateMessageForm as PrivateMessageFormI,
-  EditPrivateMessageForm,
-  PrivateMessage,
+  CreatePrivateMessage,
+  EditPrivateMessage,
+  PrivateMessageView,
   PrivateMessageResponse,
-  UserView,
+  UserSafe,
   UserOperation,
-  WebSocketJsonResponse,
 } from 'lemmy-js-client';
-import { WebSocketService } from '../services';
+import { UserService, WebSocketService } from '../services';
 import {
   capitalizeFirstLetter,
   wsJsonToRes,
@@ -18,6 +17,7 @@ import {
   setupTippy,
   wsSubscribe,
   isBrowser,
+  wsUserOp,
 } from '../utils';
 import { UserListing } from './user-listing';
 import { MarkdownTextArea } from './markdown-textarea';
@@ -25,15 +25,15 @@ import { i18n } from '../i18next';
 import { T } from 'inferno-i18next';
 
 interface PrivateMessageFormProps {
-  recipient: UserView;
-  privateMessage?: PrivateMessage; // If a pm is given, that means this is an edit
+  recipient: UserSafe;
+  privateMessage?: PrivateMessageView; // If a pm is given, that means this is an edit
   onCancel?(): any;
-  onCreate?(message: PrivateMessage): any;
-  onEdit?(message: PrivateMessage): any;
+  onCreate?(message: PrivateMessageView): any;
+  onEdit?(message: PrivateMessageView): any;
 }
 
 interface PrivateMessageFormState {
-  privateMessageForm: PrivateMessageFormI;
+  privateMessageForm: CreatePrivateMessage;
   loading: boolean;
   previewMode: boolean;
   showDisclaimer: boolean;
@@ -48,6 +48,7 @@ export class PrivateMessageForm extends Component<
     privateMessageForm: {
       content: null,
       recipient_id: this.props.recipient.id,
+      auth: UserService.Instance.authField(),
     },
     loading: false,
     previewMode: false,
@@ -64,11 +65,9 @@ export class PrivateMessageForm extends Component<
     this.parseMessage = this.parseMessage.bind(this);
     this.subscription = wsSubscribe(this.parseMessage);
 
+    // Its an edit
     if (this.props.privateMessage) {
-      this.state.privateMessageForm = {
-        content: this.props.privateMessage.content,
-        recipient_id: this.props.privateMessage.recipient_id,
-      };
+      this.state.privateMessageForm.content = this.props.privateMessage.private_message.content;
     }
   }
 
@@ -106,16 +105,7 @@ export class PrivateMessageForm extends Component<
               </label>
 
               <div class="col-sm-10 form-control-plaintext">
-                <UserListing
-                  user={{
-                    name: this.props.recipient.name,
-                    preferred_username: this.props.recipient.preferred_username,
-                    avatar: this.props.recipient.avatar,
-                    id: this.props.recipient.id,
-                    local: this.props.recipient.local,
-                    actor_id: this.props.recipient.actor_id,
-                  }}
-                />
+                <UserListing user={this.props.recipient} />
               </div>
             </div>
           )}
@@ -198,13 +188,14 @@ export class PrivateMessageForm extends Component<
   handlePrivateMessageSubmit(i: PrivateMessageForm, event: any) {
     event.preventDefault();
     if (i.props.privateMessage) {
-      let editForm: EditPrivateMessageForm = {
-        edit_id: i.props.privateMessage.id,
+      let form: EditPrivateMessage = {
+        edit_id: i.props.privateMessage.private_message.id,
         content: i.state.privateMessageForm.content,
+        auth: UserService.Instance.authField(),
       };
-      WebSocketService.Instance.editPrivateMessage(editForm);
+      WebSocketService.Instance.client.editPrivateMessage(form);
     } else {
-      WebSocketService.Instance.createPrivateMessage(
+      WebSocketService.Instance.client.createPrivateMessage(
         i.state.privateMessageForm
       );
     }
@@ -232,25 +223,25 @@ export class PrivateMessageForm extends Component<
     i.setState(i.state);
   }
 
-  parseMessage(msg: WebSocketJsonResponse) {
-    let res = wsJsonToRes(msg);
+  parseMessage(msg: any) {
+    let op = wsUserOp(msg);
     if (msg.error) {
       toast(i18n.t(msg.error), 'danger');
       this.state.loading = false;
       this.setState(this.state);
       return;
     } else if (
-      res.op == UserOperation.EditPrivateMessage ||
-      res.op == UserOperation.DeletePrivateMessage ||
-      res.op == UserOperation.MarkPrivateMessageAsRead
+      op == UserOperation.EditPrivateMessage ||
+      op == UserOperation.DeletePrivateMessage ||
+      op == UserOperation.MarkPrivateMessageAsRead
     ) {
-      let data = res.data as PrivateMessageResponse;
+      let data = wsJsonToRes<PrivateMessageResponse>(msg).data;
       this.state.loading = false;
-      this.props.onEdit(data.message);
-    } else if (res.op == UserOperation.CreatePrivateMessage) {
-      let data = res.data as PrivateMessageResponse;
+      this.props.onEdit(data.private_message_view);
+    } else if (op == UserOperation.CreatePrivateMessage) {
+      let data = wsJsonToRes<PrivateMessageResponse>(msg).data;
       this.state.loading = false;
-      this.props.onCreate(data.message);
+      this.props.onCreate(data.private_message_view);
       this.setState(this.state);
     }
   }
diff --git a/src/shared/components/private-message.tsx b/src/shared/components/private-message.tsx
index a0e86a7..ffad47f 100644
--- a/src/shared/components/private-message.tsx
+++ b/src/shared/components/private-message.tsx
@@ -1,9 +1,9 @@
 import { Component, linkEvent } from 'inferno';
 import {
-  PrivateMessage as PrivateMessageI,
-  DeletePrivateMessageForm,
-  MarkPrivateMessageAsReadForm,
-  UserView,
+  PrivateMessageView,
+  DeletePrivateMessage,
+  MarkPrivateMessageAsRead,
+  UserSafe,
 } from 'lemmy-js-client';
 import { WebSocketService, UserService } from '../services';
 import { mdToHtml, toast } from '../utils';
@@ -20,7 +20,7 @@ interface PrivateMessageState {
 }
 
 interface PrivateMessageProps {
-  privateMessage: PrivateMessageI;
+  private_message_view: PrivateMessageView;
 }
 
 export class PrivateMessage extends Component<
@@ -48,41 +48,16 @@ export class PrivateMessage extends Component<
   get mine(): boolean {
     return (
       UserService.Instance.user &&
-      UserService.Instance.user.id == this.props.privateMessage.creator_id
+      UserService.Instance.user.id == this.props.private_message_view.creator.id
     );
   }
 
   render() {
-    let message = this.props.privateMessage;
-    let userOther: UserView = this.mine
-      ? {
-          name: message.recipient_name,
-          preferred_username: message.recipient_preferred_username,
-          id: message.recipient_id,
-          avatar: message.recipient_avatar,
-          local: message.recipient_local,
-          actor_id: message.recipient_actor_id,
-          published: message.published,
-          number_of_posts: 0,
-          post_score: 0,
-          number_of_comments: 0,
-          comment_score: 0,
-          banned: false,
-        }
-      : {
-          name: message.creator_name,
-          preferred_username: message.creator_preferred_username,
-          id: message.creator_id,
-          avatar: message.creator_avatar,
-          local: message.creator_local,
-          actor_id: message.creator_actor_id,
-          published: message.published,
-          number_of_posts: 0,
-          post_score: 0,
-          number_of_comments: 0,
-          comment_score: 0,
-          banned: false,
-        };
+    let message_view = this.props.private_message_view;
+    // TODO check this again
+    let userOther: UserSafe = this.mine
+      ? message_view.recipient
+      : message_view.creator;
 
     return (
       <div class="border-top border-light">
@@ -97,7 +72,7 @@ export class PrivateMessage extends Component<
             </li>
             <li className="list-inline-item">
               <span>
-                <MomentTime data={message} />
+                <MomentTime data={message_view.private_message} />
               </span>
             </li>
             <li className="list-inline-item">
@@ -120,7 +95,7 @@ export class PrivateMessage extends Component<
           {this.state.showEdit && (
             <PrivateMessageForm
               recipient={userOther}
-              privateMessage={message}
+              privateMessage={message_view}
               onEdit={this.handlePrivateMessageEdit}
               onCreate={this.handlePrivateMessageCreate}
               onCancel={this.handleReplyCancel}
@@ -144,14 +119,14 @@ export class PrivateMessage extends Component<
                         class="btn btn-link btn-animate text-muted"
                         onClick={linkEvent(this, this.handleMarkRead)}
                         data-tippy-content={
-                          message.read
+                          message_view.private_message.read
                             ? i18n.t('mark_as_unread')
                             : i18n.t('mark_as_read')
                         }
                       >
                         <svg
                           class={`icon icon-inline ${
-                            message.read && 'text-success'
+                            message_view.private_message.read && 'text-success'
                           }`}
                         >
                           <use xlinkHref="#icon-check"></use>
@@ -189,14 +164,15 @@ export class PrivateMessage extends Component<
                         class="btn btn-link btn-animate text-muted"
                         onClick={linkEvent(this, this.handleDeleteClick)}
                         data-tippy-content={
-                          !message.deleted
+                          !message_view.private_message.deleted
                             ? i18n.t('delete')
                             : i18n.t('restore')
                         }
                       >
                         <svg
                           class={`icon icon-inline ${
-                            message.deleted && 'text-danger'
+                            message_view.private_message.deleted &&
+                            'text-danger'
                           }`}
                         >
                           <use xlinkHref="#icon-trash"></use>
@@ -237,7 +213,7 @@ export class PrivateMessage extends Component<
   }
 
   get messageUnlessRemoved(): string {
-    let message = this.props.privateMessage;
+    let message = this.props.private_message_view.private_message;
     return message.deleted ? `*${i18n.t('deleted')}*` : message.content;
   }
 
@@ -252,11 +228,12 @@ export class PrivateMessage extends Component<
   }
 
   handleDeleteClick(i: PrivateMessage) {
-    let form: DeletePrivateMessageForm = {
-      edit_id: i.props.privateMessage.id,
-      deleted: !i.props.privateMessage.deleted,
+    let form: DeletePrivateMessage = {
+      edit_id: i.props.private_message_view.private_message.id,
+      deleted: !i.props.private_message_view.private_message.deleted,
+      auth: UserService.Instance.authField(),
     };
-    WebSocketService.Instance.deletePrivateMessage(form);
+    WebSocketService.Instance.client.deletePrivateMessage(form);
   }
 
   handleReplyCancel() {
@@ -266,11 +243,12 @@ export class PrivateMessage extends Component<
   }
 
   handleMarkRead(i: PrivateMessage) {
-    let form: MarkPrivateMessageAsReadForm = {
-      edit_id: i.props.privateMessage.id,
-      read: !i.props.privateMessage.read,
+    let form: MarkPrivateMessageAsRead = {
+      edit_id: i.props.private_message_view.private_message.id,
+      read: !i.props.private_message_view.private_message.read,
+      auth: UserService.Instance.authField(),
     };
-    WebSocketService.Instance.markPrivateMessageAsRead(form);
+    WebSocketService.Instance.client.markPrivateMessageAsRead(form);
   }
 
   handleMessageCollapse(i: PrivateMessage) {
@@ -288,10 +266,10 @@ export class PrivateMessage extends Component<
     this.setState(this.state);
   }
 
-  handlePrivateMessageCreate(message: PrivateMessageI) {
+  handlePrivateMessageCreate(message: PrivateMessageView) {
     if (
       UserService.Instance.user &&
-      message.creator_id == UserService.Instance.user.id
+      message.creator.id == UserService.Instance.user.id
     ) {
       this.state.showReply = false;
       this.setState(this.state);
diff --git a/src/shared/components/search.tsx b/src/shared/components/search.tsx
index 6796ad3..b4196f7 100644
--- a/src/shared/components/search.tsx
+++ b/src/shared/components/search.tsx
@@ -2,20 +2,19 @@ import { Component, linkEvent } from 'inferno';
 import { Subscription } from 'rxjs';
 import {
   UserOperation,
-  Post,
-  Comment,
-  Community,
-  UserView,
+  PostView,
+  CommentView,
+  CommunityView,
+  UserViewSafe,
   SortType,
-  SearchForm,
+  Search as SearchForm,
   SearchResponse,
   SearchType,
   PostResponse,
   CommentResponse,
-  WebSocketJsonResponse,
   Site,
 } from 'lemmy-js-client';
-import { WebSocketService } from '../services';
+import { UserService, WebSocketService } from '../services';
 import {
   wsJsonToRes,
   fetchLimit,
@@ -27,7 +26,7 @@ import {
   commentsToFlatNodes,
   setIsoData,
   wsSubscribe,
-  setAuth,
+  wsUserOp,
 } from '../utils';
 import { PostListing } from './post-listing';
 import { HtmlTags } from './html-tags';
@@ -80,7 +79,7 @@ export class Search extends Component<any, SearchState> {
       users: [],
     },
     loading: false,
-    site: this.isoData.site.site,
+    site: this.isoData.site_res.site_view.site,
   };
 
   static getSearchQueryFromProps(q: string): string {
@@ -142,8 +141,8 @@ export class Search extends Component<any, SearchState> {
       sort: this.getSortTypeFromProps(pathSplit[7]),
       page: this.getPageFromProps(pathSplit[9]),
       limit: fetchLimit,
+      auth: req.auth,
     };
-    setAuth(form, req.auth);
 
     if (form.q != '') {
       promises.push(req.client.search(form));
@@ -252,19 +251,24 @@ export class Search extends Component<any, SearchState> {
   all() {
     let combined: {
       type_: string;
-      data: Comment | Post | Community | UserView;
+      data: CommentView | PostView | CommunityView | UserViewSafe;
+      published: string;
     }[] = [];
     let comments = this.state.searchResponse.comments.map(e => {
-      return { type_: 'comments', data: e };
+      return { type_: 'comments', data: e, published: e.comment.published };
     });
     let posts = this.state.searchResponse.posts.map(e => {
-      return { type_: 'posts', data: e };
+      return { type_: 'posts', data: e, published: e.post.published };
     });
     let communities = this.state.searchResponse.communities.map(e => {
-      return { type_: 'communities', data: e };
+      return {
+        type_: 'communities',
+        data: e,
+        published: e.community.published,
+      };
     });
     let users = this.state.searchResponse.users.map(e => {
-      return { type_: 'users', data: e };
+      return { type_: 'users', data: e, published: e.user.published };
     });
 
     combined.push(...comments);
@@ -274,16 +278,16 @@ export class Search extends Component<any, SearchState> {
 
     // Sort it
     if (this.state.sort == SortType.New) {
-      combined.sort((a, b) => b.data.published.localeCompare(a.data.published));
+      combined.sort((a, b) => b.published.localeCompare(a.published));
     } else {
       combined.sort(
         (a, b) =>
-          ((b.data as Comment | Post).score |
-            (b.data as Community).number_of_subscribers |
-            (b.data as UserView).comment_score) -
-          ((a.data as Comment | Post).score |
-            (a.data as Community).number_of_subscribers |
-            (a.data as UserView).comment_score)
+          ((b.data as CommentView | PostView).counts.score |
+            (b.data as CommunityView).counts.subscribers |
+            (b.data as UserViewSafe).counts.comment_score) -
+          ((a.data as CommentView | PostView).counts.score |
+            (a.data as CommunityView).counts.subscribers |
+            (a.data as UserViewSafe).counts.comment_score)
       );
     }
 
@@ -294,8 +298,8 @@ export class Search extends Component<any, SearchState> {
             <div class="col-12">
               {i.type_ == 'posts' && (
                 <PostListing
-                  key={(i.data as Post).id}
-                  post={i.data as Post}
+                  key={(i.data as PostView).post.id}
+                  post_view={i.data as PostView}
                   showCommunity
                   enableDownvotes={this.state.site.enable_downvotes}
                   enableNsfw={this.state.site.enable_nsfw}
@@ -303,18 +307,18 @@ export class Search extends Component<any, SearchState> {
               )}
               {i.type_ == 'comments' && (
                 <CommentNodes
-                  key={(i.data as Comment).id}
-                  nodes={[{ comment: i.data as Comment }]}
+                  key={(i.data as CommentView).comment.id}
+                  nodes={[{ comment_view: i.data as CommentView }]}
                   locked
                   noIndent
                   enableDownvotes={this.state.site.enable_downvotes}
                 />
               )}
               {i.type_ == 'communities' && (
-                <div>{this.communityListing(i.data as Community)}</div>
+                <div>{this.communityListing(i.data as CommunityView)}</div>
               )}
               {i.type_ == 'users' && (
-                <div>{this.userListing(i.data as UserView)}</div>
+                <div>{this.userListing(i.data as UserViewSafe)}</div>
               )}
             </div>
           </div>
@@ -341,7 +345,7 @@ export class Search extends Component<any, SearchState> {
           <div class="row">
             <div class="col-12">
               <PostListing
-                post={post}
+                post_view={post}
                 showCommunity
                 enableDownvotes={this.state.site.enable_downvotes}
                 enableNsfw={this.state.site.enable_nsfw}
@@ -365,28 +369,28 @@ export class Search extends Component<any, SearchState> {
     );
   }
 
-  communityListing(community: Community) {
+  communityListing(community_view: CommunityView) {
     return (
       <>
         <span>
-          <CommunityLink community={community} />
+          <CommunityLink community={community_view.community} />
         </span>
-        <span>{` - ${community.title} - 
+        <span>{` - ${community_view.community.title} - 
         ${i18n.t('number_of_subscribers', {
-          count: community.number_of_subscribers,
+          count: community_view.counts.subscribers,
         })}
       `}</span>
       </>
     );
   }
 
-  userListing(user: UserView) {
+  userListing(user_view: UserViewSafe) {
     return [
       <span>
-        <UserListing user={user} showApubName />
+        <UserListing user={user_view.user} showApubName />
       </span>,
       <span>{` - ${i18n.t('number_of_comments', {
-        count: user.number_of_comments,
+        count: user_view.counts.comment_count,
       })}`}</span>,
     ];
   }
@@ -452,10 +456,11 @@ export class Search extends Component<any, SearchState> {
       sort: this.state.sort,
       page: this.state.page,
       limit: fetchLimit,
+      auth: UserService.Instance.authField(false),
     };
 
     if (this.state.q != '') {
-      WebSocketService.Instance.search(form);
+      WebSocketService.Instance.client.search(form);
     }
   }
 
@@ -495,25 +500,28 @@ export class Search extends Component<any, SearchState> {
     );
   }
 
-  parseMessage(msg: WebSocketJsonResponse) {
+  parseMessage(msg: any) {
     console.log(msg);
-    let res = wsJsonToRes(msg);
+    let op = wsUserOp(msg);
     if (msg.error) {
       toast(i18n.t(msg.error), 'danger');
       return;
-    } 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.searchResponse = data;
       this.state.loading = false;
       window.scrollTo(0, 0);
       this.setState(this.state);
-    } else if (res.op == UserOperation.CreateCommentLike) {
-      let data = res.data as CommentResponse;
-      createCommentLikeRes(data, this.state.searchResponse.comments);
+    } else if (op == UserOperation.CreateCommentLike) {
+      let data = wsJsonToRes<CommentResponse>(msg).data;
+      createCommentLikeRes(
+        data.comment_view,
+        this.state.searchResponse.comments
+      );
       this.setState(this.state);
-    } else if (res.op == UserOperation.CreatePostLike) {
-      let data = res.data as PostResponse;
-      createPostLikeFindRes(data, this.state.searchResponse.posts);
+    } else if (op == UserOperation.CreatePostLike) {
+      let data = wsJsonToRes<PostResponse>(msg).data;
+      createPostLikeFindRes(data.post_view, this.state.searchResponse.posts);
       this.setState(this.state);
     }
   }
diff --git a/src/shared/components/setup.tsx b/src/shared/components/setup.tsx
index 33fa443..c139988 100644
--- a/src/shared/components/setup.tsx
+++ b/src/shared/components/setup.tsx
@@ -2,19 +2,14 @@ import { Component, linkEvent } from 'inferno';
 import { Helmet } from 'inferno-helmet';
 import { Subscription } from 'rxjs';
 import { retryWhen, delay, take } from 'rxjs/operators';
-import {
-  RegisterForm,
-  LoginResponse,
-  UserOperation,
-  WebSocketJsonResponse,
-} from 'lemmy-js-client';
+import { Register, LoginResponse, UserOperation } from 'lemmy-js-client';
 import { WebSocketService, UserService } from '../services';
-import { wsJsonToRes, toast } from '../utils';
+import { wsUserOp, wsJsonToRes, toast } from '../utils';
 import { SiteForm } from './site-form';
 import { i18n } from '../i18next';
 
 interface State {
-  userForm: RegisterForm;
+  userForm: Register;
   doneRegisteringUser: boolean;
   userLoading: boolean;
 }
@@ -168,7 +163,7 @@ export class Setup extends Component<any, State> {
     i.state.userLoading = true;
     i.setState(i.state);
     event.preventDefault();
-    WebSocketService.Instance.register(i.state.userForm);
+    WebSocketService.Instance.client.register(i.state.userForm);
   }
 
   handleRegisterUsernameChange(i: Setup, event: any) {
@@ -191,20 +186,20 @@ export class Setup extends Component<any, State> {
     i.setState(i.state);
   }
 
-  parseMessage(msg: WebSocketJsonResponse) {
-    let res = wsJsonToRes(msg);
+  parseMessage(msg: any) {
+    let op = wsUserOp(msg);
     if (msg.error) {
       toast(i18n.t(msg.error), 'danger');
       this.state.userLoading = false;
       this.setState(this.state);
       return;
-    } else if (res.op == UserOperation.Register) {
-      let data = res.data as LoginResponse;
+    } else if (op == UserOperation.Register) {
+      let data = wsJsonToRes<LoginResponse>(msg).data;
       this.state.userLoading = false;
       this.state.doneRegisteringUser = true;
       UserService.Instance.login(data);
       this.setState(this.state);
-    } else if (res.op == UserOperation.CreateSite) {
+    } else if (op == UserOperation.CreateSite) {
       window.location.href = '/';
     }
   }
diff --git a/src/shared/components/sidebar.tsx b/src/shared/components/sidebar.tsx
index 82b05af..0934535 100644
--- a/src/shared/components/sidebar.tsx
+++ b/src/shared/components/sidebar.tsx
@@ -1,13 +1,13 @@
 import { Component, linkEvent } from 'inferno';
 import { Link } from 'inferno-router';
 import {
-  Community,
-  CommunityUser,
-  FollowCommunityForm,
-  DeleteCommunityForm,
-  RemoveCommunityForm,
-  UserView,
-  AddModToCommunityForm,
+  CommunityView,
+  CommunityModeratorView,
+  FollowCommunity,
+  DeleteCommunity,
+  RemoveCommunity,
+  UserViewSafe,
+  AddModToCommunity,
   Category,
 } from 'lemmy-js-client';
 import { WebSocketService, UserService } from '../services';
@@ -19,10 +19,10 @@ import { BannerIconHeader } from './banner-icon-header';
 import { i18n } from '../i18next';
 
 interface SidebarProps {
-  community: Community;
+  community_view: CommunityView;
   categories: Category[];
-  moderators: CommunityUser[];
-  admins: UserView[];
+  moderators: CommunityModeratorView[];
+  admins: UserViewSafe[];
   online: number;
   enableNsfw: boolean;
   showIcon?: boolean;
@@ -60,7 +60,7 @@ export class Sidebar extends Component<SidebarProps, SidebarState> {
         ) : (
           <CommunityForm
             categories={this.props.categories}
-            community={this.props.community}
+            community_view={this.props.community_view}
             onEdit={this.handleEditCommunity}
             onCancel={this.handleEditCancel}
             enableNsfw={this.props.enableNsfw}
@@ -93,7 +93,8 @@ export class Sidebar extends Component<SidebarProps, SidebarState> {
   }
 
   communityTitle() {
-    let community = this.props.community;
+    let community = this.props.community_view.community;
+    let subscribed = this.props.community_view.subscribed;
     return (
       <div>
         <h5 className="mb-0">
@@ -101,7 +102,7 @@ export class Sidebar extends Component<SidebarProps, SidebarState> {
             <BannerIconHeader icon={community.icon} banner={community.banner} />
           )}
           <span class="mr-2">{community.title}</span>
-          {community.subscribed && (
+          {subscribed && (
             <a
               class="btn btn-secondary btn-sm mr-2"
               href="#"
@@ -141,7 +142,7 @@ export class Sidebar extends Component<SidebarProps, SidebarState> {
   }
 
   badges() {
-    let community = this.props.community;
+    let community_view = this.props.community_view;
     return (
       <ul class="my-1 list-inline">
         <li className="list-inline-item badge badge-secondary">
@@ -149,28 +150,28 @@ export class Sidebar extends Component<SidebarProps, SidebarState> {
         </li>
         <li className="list-inline-item badge badge-secondary">
           {i18n.t('number_of_subscribers', {
-            count: community.number_of_subscribers,
+            count: community_view.counts.subscribers,
           })}
         </li>
         <li className="list-inline-item badge badge-secondary">
           {i18n.t('number_of_posts', {
-            count: community.number_of_posts,
+            count: community_view.counts.posts,
           })}
         </li>
         <li className="list-inline-item badge badge-secondary">
           {i18n.t('number_of_comments', {
-            count: community.number_of_comments,
+            count: community_view.counts.comments,
           })}
         </li>
         <li className="list-inline-item">
           <Link className="badge badge-secondary" to="/communities">
-            {community.category_name}
+            {community_view.category.name}
           </Link>
         </li>
         <li className="list-inline-item">
           <Link
             className="badge badge-secondary"
-            to={`/modlog/community/${this.props.community.id}`}
+            to={`/modlog/community/${this.props.community_view.community.id}`}
           >
             {i18n.t('modlog')}
           </Link>
@@ -185,16 +186,7 @@ export class Sidebar extends Component<SidebarProps, SidebarState> {
         <li class="list-inline-item">{i18n.t('mods')}: </li>
         {this.props.moderators.map(mod => (
           <li class="list-inline-item">
-            <UserListing
-              user={{
-                name: mod.user_name,
-                preferred_username: mod.user_preferred_username,
-                avatar: mod.avatar,
-                id: mod.user_id,
-                local: mod.user_local,
-                actor_id: mod.user_actor_id,
-              }}
-            />
+            <UserListing user={mod.moderator} />
           </li>
         ))}
       </ul>
@@ -202,14 +194,16 @@ export class Sidebar extends Component<SidebarProps, SidebarState> {
   }
 
   createPost() {
-    let community = this.props.community;
+    let community_view = this.props.community_view;
     return (
-      community.subscribed && (
+      community_view.subscribed && (
         <Link
           className={`btn btn-secondary btn-block mb-2 ${
-            community.deleted || community.removed ? 'no-click' : ''
+            community_view.community.deleted || community_view.community.removed
+              ? 'no-click'
+              : ''
           }`}
-          to={`/create_post?community_id=${community.id}`}
+          to={`/create_post?community_id=${community_view.community.id}`}
         >
           {i18n.t('create_a_post')}
         </Link>
@@ -218,14 +212,17 @@ export class Sidebar extends Component<SidebarProps, SidebarState> {
   }
 
   subscribe() {
-    let community = this.props.community;
+    let community_view = this.props.community_view;
     return (
       <div class="mb-2">
-        {!community.subscribed && (
+        {!community_view.subscribed && (
           <a
             class="btn btn-secondary btn-block"
             href="#"
-            onClick={linkEvent(community.id, this.handleSubscribe)}
+            onClick={linkEvent(
+              community_view.community.id,
+              this.handleSubscribe
+            )}
           >
             {i18n.t('subscribe')}
           </a>
@@ -235,19 +232,19 @@ export class Sidebar extends Component<SidebarProps, SidebarState> {
   }
 
   description() {
-    let community = this.props.community;
+    let description = this.props.community_view.community.description;
     return (
-      community.description && (
+      description && (
         <div
           className="md-div"
-          dangerouslySetInnerHTML={mdToHtml(community.description)}
+          dangerouslySetInnerHTML={mdToHtml(description)}
         />
       )
     );
   }
 
   adminButtons() {
-    let community = this.props.community;
+    let community_view = this.props.community_view;
     return (
       <>
         <ul class="list-inline mb-1 text-muted font-weight-bold">
@@ -309,12 +306,14 @@ export class Sidebar extends Component<SidebarProps, SidebarState> {
                     class="pointer"
                     onClick={linkEvent(this, this.handleDeleteClick)}
                     data-tippy-content={
-                      !community.deleted ? i18n.t('delete') : i18n.t('restore')
+                      !community_view.community.deleted
+                        ? i18n.t('delete')
+                        : i18n.t('restore')
                     }
                   >
                     <svg
                       class={`icon icon-inline ${
-                        community.deleted && 'text-danger'
+                        community_view.community.deleted && 'text-danger'
                       }`}
                     >
                       <use xlinkHref="#icon-trash"></use>
@@ -326,7 +325,7 @@ export class Sidebar extends Component<SidebarProps, SidebarState> {
           )}
           {this.canAdmin && (
             <li className="list-inline-item">
-              {!this.props.community.removed ? (
+              {!this.props.community_view.community.removed ? (
                 <span
                   class="pointer"
                   onClick={linkEvent(this, this.handleModRemoveShow)}
@@ -392,11 +391,12 @@ export class Sidebar extends Component<SidebarProps, SidebarState> {
 
   handleDeleteClick(i: Sidebar, event: any) {
     event.preventDefault();
-    let deleteForm: DeleteCommunityForm = {
-      edit_id: i.props.community.id,
-      deleted: !i.props.community.deleted,
+    let deleteForm: DeleteCommunity = {
+      edit_id: i.props.community_view.community.id,
+      deleted: !i.props.community_view.community.deleted,
+      auth: UserService.Instance.authField(),
     };
-    WebSocketService.Instance.deleteCommunity(deleteForm);
+    WebSocketService.Instance.client.deleteCommunity(deleteForm);
   }
 
   handleShowConfirmLeaveModTeamClick(i: Sidebar) {
@@ -405,12 +405,13 @@ export class Sidebar extends Component<SidebarProps, SidebarState> {
   }
 
   handleLeaveModTeamClick(i: Sidebar) {
-    let form: AddModToCommunityForm = {
+    let form: AddModToCommunity = {
       user_id: UserService.Instance.user.id,
-      community_id: i.props.community.id,
+      community_id: i.props.community_view.community.id,
       added: false,
+      auth: UserService.Instance.authField(),
     };
-    WebSocketService.Instance.addModToCommunity(form);
+    WebSocketService.Instance.client.addModToCommunity(form);
     i.state.showConfirmLeaveModTeam = false;
     i.setState(i.state);
   }
@@ -422,31 +423,33 @@ export class Sidebar extends Component<SidebarProps, SidebarState> {
 
   handleUnsubscribe(communityId: number, event: any) {
     event.preventDefault();
-    let form: FollowCommunityForm = {
+    let form: FollowCommunity = {
       community_id: communityId,
       follow: false,
+      auth: UserService.Instance.authField(),
     };
-    WebSocketService.Instance.followCommunity(form);
+    WebSocketService.Instance.client.followCommunity(form);
   }
 
   handleSubscribe(communityId: number, event: any) {
     event.preventDefault();
-    let form: FollowCommunityForm = {
+    let form: FollowCommunity = {
       community_id: communityId,
       follow: true,
+      auth: UserService.Instance.authField(),
     };
-    WebSocketService.Instance.followCommunity(form);
+    WebSocketService.Instance.client.followCommunity(form);
   }
 
   private get amCreator(): boolean {
-    return this.props.community.creator_id == UserService.Instance.user.id;
+    return this.props.community_view.creator.id == UserService.Instance.user.id;
   }
 
   get canMod(): boolean {
     return (
       UserService.Instance.user &&
       this.props.moderators
-        .map(m => m.user_id)
+        .map(m => m.moderator.id)
         .includes(UserService.Instance.user.id)
     );
   }
@@ -454,7 +457,9 @@ export class Sidebar extends Component<SidebarProps, SidebarState> {
   get canAdmin(): boolean {
     return (
       UserService.Instance.user &&
-      this.props.admins.map(a => a.id).includes(UserService.Instance.user.id)
+      this.props.admins
+        .map(a => a.user.id)
+        .includes(UserService.Instance.user.id)
     );
   }
 
@@ -476,13 +481,14 @@ export class Sidebar extends Component<SidebarProps, SidebarState> {
 
   handleModRemoveSubmit(i: Sidebar, event: any) {
     event.preventDefault();
-    let removeForm: RemoveCommunityForm = {
-      edit_id: i.props.community.id,
-      removed: !i.props.community.removed,
+    let removeForm: RemoveCommunity = {
+      edit_id: i.props.community_view.community.id,
+      removed: !i.props.community_view.community.removed,
       reason: i.state.removeReason,
       expires: getUnixTime(i.state.removeExpires),
+      auth: UserService.Instance.authField(),
     };
-    WebSocketService.Instance.removeCommunity(removeForm);
+    WebSocketService.Instance.client.removeCommunity(removeForm);
 
     i.state.showRemoveDialog = false;
     i.setState(i.state);
diff --git a/src/shared/components/site-form.tsx b/src/shared/components/site-form.tsx
index 9b572f5..6547edd 100644
--- a/src/shared/components/site-form.tsx
+++ b/src/shared/components/site-form.tsx
@@ -2,8 +2,8 @@ import { Component, linkEvent } from 'inferno';
 import { Prompt } from 'inferno-router';
 import { MarkdownTextArea } from './markdown-textarea';
 import { ImageUploadForm } from './image-upload-form';
-import { Site, SiteForm as SiteFormI } from 'lemmy-js-client';
-import { WebSocketService } from '../services';
+import { Site, EditSite } from 'lemmy-js-client';
+import { UserService, WebSocketService } from '../services';
 import { capitalizeFirstLetter, randomStr } from '../utils';
 import { i18n } from '../i18next';
 
@@ -13,7 +13,7 @@ interface SiteFormProps {
 }
 
 interface SiteFormState {
-  siteForm: SiteFormI;
+  siteForm: EditSite;
   loading: boolean;
 }
 
@@ -27,6 +27,7 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
       name: null,
       icon: null,
       banner: null,
+      auth: UserService.Instance.authField(),
     },
     loading: false,
   };
@@ -54,6 +55,7 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
         enable_nsfw: this.props.site.enable_nsfw,
         icon: this.props.site.icon,
         banner: this.props.site.banner,
+        auth: UserService.Instance.authField(),
       };
     }
   }
@@ -242,9 +244,9 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
     event.preventDefault();
     i.state.loading = true;
     if (i.props.site) {
-      WebSocketService.Instance.editSite(i.state.siteForm);
+      WebSocketService.Instance.client.editSite(i.state.siteForm);
     } else {
-      WebSocketService.Instance.createSite(i.state.siteForm);
+      WebSocketService.Instance.client.createSite(i.state.siteForm);
     }
     i.setState(i.state);
   }
diff --git a/src/shared/components/theme.tsx b/src/shared/components/theme.tsx
index 5dfc01c..8da4ee1 100644
--- a/src/shared/components/theme.tsx
+++ b/src/shared/components/theme.tsx
@@ -1,9 +1,9 @@
-import { User } from 'lemmy-js-client';
+import { User_ } from 'lemmy-js-client';
 import { Helmet } from 'inferno-helmet';
 import { Component } from 'inferno';
 
 interface Props {
-  user: User | undefined;
+  user: User_ | undefined;
 }
 
 export class Theme extends Component<Props> {
diff --git a/src/shared/components/user-details.tsx b/src/shared/components/user-details.tsx
index 01c9aed..f128158 100644
--- a/src/shared/components/user-details.tsx
+++ b/src/shared/components/user-details.tsx
@@ -1,13 +1,20 @@
 import { Component, linkEvent } from 'inferno';
 import { i18n } from '../i18next';
-import { Post, Comment, SortType, UserDetailsResponse } from 'lemmy-js-client';
+import {
+  PostView,
+  CommentView,
+  SortType,
+  GetUserDetailsResponse,
+  UserViewSafe,
+} from 'lemmy-js-client';
 import { UserDetailsView } from '../interfaces';
 import { commentsToFlatNodes, setupTippy } from '../utils';
 import { PostListing } from './post-listing';
 import { CommentNodes } from './comment-nodes';
 
 interface UserDetailsProps {
-  userRes: UserDetailsResponse;
+  userRes: GetUserDetailsResponse;
+  admins: UserViewSafe[];
   page: number;
   limit: number;
   sort: SortType;
@@ -19,6 +26,18 @@ interface UserDetailsProps {
 
 interface UserDetailsState {}
 
+enum ItemEnum {
+  Comment,
+  Post,
+}
+type ItemType = {
+  id: number;
+  type_: ItemEnum;
+  view: CommentView | PostView;
+  published: string;
+  score: number;
+};
+
 export class UserDetails extends Component<UserDetailsProps, UserDetailsState> {
   constructor(props: any, context: any) {
     super(props, context);
@@ -60,56 +79,68 @@ export class UserDetails extends Component<UserDetailsProps, UserDetailsState> {
     }
   }
 
+  renderItemType(i: ItemType) {
+    switch (i.type_) {
+      case ItemEnum.Comment:
+        let c = i.view as CommentView;
+        return (
+          <CommentNodes
+            key={i.id}
+            nodes={[{ comment_view: c }]}
+            admins={this.props.admins}
+            noBorder
+            noIndent
+            showCommunity
+            showContext
+            enableDownvotes={this.props.enableDownvotes}
+          />
+        );
+      case ItemEnum.Post:
+        let p = i.view as PostView;
+        return (
+          <PostListing
+            key={i.id}
+            post_view={p}
+            admins={this.props.admins}
+            showCommunity
+            enableDownvotes={this.props.enableDownvotes}
+            enableNsfw={this.props.enableNsfw}
+          />
+        );
+      default:
+        return <div />;
+    }
+  }
+
   overview() {
-    const comments = this.props.userRes.comments.map((c: Comment) => {
-      return { type: 'comments', data: c };
-    });
-    const posts = this.props.userRes.posts.map((p: Post) => {
-      return { type: 'posts', data: p };
-    });
-
-    const combined: { type: string; data: Comment | Post }[] = [
-      ...comments,
-      ...posts,
-    ];
+    let id = 0;
+    let comments: ItemType[] = this.props.userRes.comments.map(r => ({
+      id: id++,
+      type_: ItemEnum.Comment,
+      view: r,
+      published: r.comment.published,
+      score: r.counts.score,
+    }));
+    let posts: ItemType[] = this.props.userRes.posts.map(r => ({
+      id: id++,
+      type_: ItemEnum.Comment,
+      view: r,
+      published: r.post.published,
+      score: r.counts.score,
+    }));
+
+    let combined = [...comments, ...posts];
 
     // Sort it
     if (this.props.sort === SortType.New) {
-      combined.sort((a, b) => b.data.published.localeCompare(a.data.published));
+      combined.sort((a, b) => b.published.localeCompare(a.published));
     } else {
-      combined.sort((a, b) => b.data.score - a.data.score);
+      combined.sort((a, b) => b.score - a.score);
     }
 
     return (
       <div>
-        {combined.map(i => (
-          <>
-            <div>
-              {i.type === 'posts' ? (
-                <PostListing
-                  key={(i.data as Post).id}
-                  post={i.data as Post}
-                  admins={this.props.userRes.admins}
-                  showCommunity
-                  enableDownvotes={this.props.enableDownvotes}
-                  enableNsfw={this.props.enableNsfw}
-                />
-              ) : (
-                <CommentNodes
-                  key={(i.data as Comment).id}
-                  nodes={[{ comment: i.data as Comment }]}
-                  admins={this.props.userRes.admins}
-                  noBorder
-                  noIndent
-                  showCommunity
-                  showContext
-                  enableDownvotes={this.props.enableDownvotes}
-                />
-              )}
-            </div>
-            <hr class="my-3" />
-          </>
-        ))}
+        {combined.map(i => [this.renderItemType(i), <hr class="my-3" />])}
       </div>
     );
   }
@@ -119,7 +150,7 @@ export class UserDetails extends Component<UserDetailsProps, UserDetailsState> {
       <div>
         <CommentNodes
           nodes={commentsToFlatNodes(this.props.userRes.comments)}
-          admins={this.props.userRes.admins}
+          admins={this.props.admins}
           noIndent
           showCommunity
           showContext
@@ -135,8 +166,8 @@ export class UserDetails extends Component<UserDetailsProps, UserDetailsState> {
         {this.props.userRes.posts.map(post => (
           <>
             <PostListing
-              post={post}
-              admins={this.props.userRes.admins}
+              post_view={post}
+              admins={this.props.admins}
               showCommunity
               enableDownvotes={this.props.enableDownvotes}
               enableNsfw={this.props.enableNsfw}
diff --git a/src/shared/components/user-listing.tsx b/src/shared/components/user-listing.tsx
index a8e4025..a20785a 100644
--- a/src/shared/components/user-listing.tsx
+++ b/src/shared/components/user-listing.tsx
@@ -1,22 +1,12 @@
 import { Component } from 'inferno';
 import { Link } from 'inferno-router';
-import { UserView } from 'lemmy-js-client';
+import { UserSafe } from 'lemmy-js-client';
 import { showAvatars, hostname, isCakeDay } from '../utils';
 import { CakeDay } from './cake-day';
 import { PictrsImage } from './pictrs-image';
 
-export interface UserOther {
-  name: string;
-  preferred_username?: string;
-  id?: number; // Necessary if its federated
-  avatar?: string;
-  local?: boolean;
-  actor_id?: string;
-  published?: string;
-}
-
 interface UserListingProps {
-  user: UserView | UserOther;
+  user: UserSafe;
   realLink?: boolean;
   useApubName?: boolean;
   muted?: boolean;
diff --git a/src/shared/components/user.tsx b/src/shared/components/user.tsx
index b03d9dd..814e789 100644
--- a/src/shared/components/user.tsx
+++ b/src/shared/components/user.tsx
@@ -5,14 +5,13 @@ import {
   UserOperation,
   SortType,
   ListingType,
-  UserSettingsForm,
+  SaveUserSettings,
   LoginResponse,
-  DeleteAccountForm,
-  WebSocketJsonResponse,
+  DeleteAccount,
   GetSiteResponse,
-  UserDetailsResponse,
+  GetUserDetailsResponse,
   AddAdminResponse,
-  GetUserDetailsForm,
+  GetUserDetails,
   CommentResponse,
   PostResponse,
   BanUserResponse,
@@ -40,9 +39,9 @@ import {
   editCommentRes,
   saveCommentRes,
   createPostLikeFindRes,
-  setAuth,
   previewLines,
   editPostFindRes,
+  wsUserOp,
 } from '../utils';
 import { UserListing } from './user-listing';
 import { HtmlTags } from './html-tags';
@@ -58,18 +57,18 @@ import { BannerIconHeader } from './banner-icon-header';
 import { CommunityLink } from './community-link';
 
 interface UserState {
-  userRes: UserDetailsResponse;
+  userRes: GetUserDetailsResponse;
   userId: number;
   userName: string;
   view: UserDetailsView;
   sort: SortType;
   page: number;
   loading: boolean;
-  userSettingsForm: UserSettingsForm;
+  userSettingsForm: SaveUserSettings;
   userSettingsLoading: boolean;
   deleteAccountLoading: boolean;
   deleteAccountShowConfirm: boolean;
-  deleteAccountForm: DeleteAccountForm;
+  deleteAccountForm: DeleteAccount;
   siteRes: GetSiteResponse;
 }
 
@@ -106,17 +105,18 @@ export class User extends Component<any, UserState> {
       lang: null,
       show_avatars: null,
       send_notifications_to_email: null,
-      auth: null,
       bio: null,
       preferred_username: null,
+      auth: UserService.Instance.authField(),
     },
     userSettingsLoading: null,
     deleteAccountLoading: null,
     deleteAccountShowConfirm: false,
     deleteAccountForm: {
       password: null,
+      auth: UserService.Instance.authField(),
     },
-    siteRes: this.isoData.site,
+    siteRes: this.isoData.site_res,
   };
 
   constructor(props: any, context: any) {
@@ -157,21 +157,22 @@ export class User extends Component<any, UserState> {
   }
 
   fetchUserData() {
-    let form: GetUserDetailsForm = {
+    let form: GetUserDetails = {
       user_id: this.state.userId,
       username: this.state.userName,
       sort: this.state.sort,
       saved_only: this.state.view === UserDetailsView.Saved,
       page: this.state.page,
       limit: fetchLimit,
+      auth: UserService.Instance.authField(false),
     };
-    WebSocketService.Instance.getUserDetails(form);
+    WebSocketService.Instance.client.getUserDetails(form);
   }
 
   get isCurrentUser() {
     return (
       UserService.Instance.user &&
-      UserService.Instance.user.id == this.state.userRes.user.id
+      UserService.Instance.user.id == this.state.userRes.user_view.user.id
     );
   }
 
@@ -205,14 +206,14 @@ export class User extends Component<any, UserState> {
     let sort = this.getSortTypeFromProps(pathSplit[6]);
     let page = this.getPageFromProps(Number(pathSplit[8]));
 
-    let form: GetUserDetailsForm = {
+    let form: GetUserDetails = {
       sort,
       saved_only: view === UserDetailsView.Saved,
       page,
       limit: fetchLimit,
+      auth: req.auth,
     };
     this.setIdOrName(form, user_id, username);
-    setAuth(form, req.auth);
     promises.push(req.client.getUserDetails(form));
     return promises;
   }
@@ -251,12 +252,12 @@ export class User extends Component<any, UserState> {
   }
 
   get documentTitle(): string {
-    return `@${this.state.userRes.user.name} - ${this.state.siteRes.site.name}`;
+    return `@${this.state.userRes.user_view.user.name} - ${this.state.siteRes.site_view.site.name}`;
   }
 
   get bioTag(): string {
-    return this.state.userRes.user.bio
-      ? previewLines(this.state.userRes.user.bio)
+    return this.state.userRes.user_view.user.bio
+      ? previewLines(this.state.userRes.user_view.user.bio)
       : undefined;
   }
 
@@ -277,7 +278,7 @@ export class User extends Component<any, UserState> {
                   title={this.documentTitle}
                   path={this.context.router.route.match.url}
                   description={this.bioTag}
-                  image={this.state.userRes.user.avatar}
+                  image={this.state.userRes.user_view.user.avatar}
                 />
                 {this.userInfo()}
                 <hr />
@@ -285,11 +286,14 @@ export class User extends Component<any, UserState> {
               {!this.state.loading && this.selects()}
               <UserDetails
                 userRes={this.state.userRes}
+                admins={this.state.siteRes.admins}
                 sort={this.state.sort}
                 page={this.state.page}
                 limit={fetchLimit}
-                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}
                 view={this.state.view}
                 onPageChange={this.handlePageChange}
               />
@@ -391,29 +395,29 @@ export class User extends Component<any, UserState> {
   }
 
   userInfo() {
-    let user = this.state.userRes.user;
+    let uv = this.state.userRes.user_view;
 
     return (
       <div>
-        <BannerIconHeader banner={user.banner} icon={user.avatar} />
+        <BannerIconHeader banner={uv.user.banner} icon={uv.user.avatar} />
         <div class="mb-3">
           <div class="">
             <div class="mb-0 d-flex flex-wrap">
               <div>
-                {user.preferred_username && (
-                  <h5 class="mb-0">{user.preferred_username}</h5>
+                {uv.user.preferred_username && (
+                  <h5 class="mb-0">{uv.user.preferred_username}</h5>
                 )}
                 <ul class="list-inline mb-2">
                   <li className="list-inline-item">
                     <UserListing
-                      user={user}
+                      user={uv.user}
                       realLink
                       useApubName
                       muted
                       hideAvatar
                     />
                   </li>
-                  {user.banned && (
+                  {uv.user.banned && (
                     <li className="list-inline-item badge badge-danger">
                       {i18n.t('banned')}
                     </li>
@@ -432,45 +436,45 @@ export class User extends Component<any, UserState> {
                 <>
                   <a
                     className={`d-flex align-self-start btn btn-secondary mr-2 ${
-                      !user.matrix_user_id && 'invisible'
+                      !uv.user.matrix_user_id && 'invisible'
                     }`}
                     target="_blank"
                     rel="noopener"
-                    href={`https://matrix.to/#/${user.matrix_user_id}`}
+                    href={`https://matrix.to/#/${uv.user.matrix_user_id}`}
                   >
                     {i18n.t('send_secure_message')}
                   </a>
                   <Link
                     className={'d-flex align-self-start btn btn-secondary'}
-                    to={`/create_private_message/recipient/${user.id}`}
+                    to={`/create_private_message/recipient/${uv.user.id}`}
                   >
                     {i18n.t('send_message')}
                   </Link>
                 </>
               )}
             </div>
-            {user.bio && (
+            {uv.user.bio && (
               <div className="d-flex align-items-center mb-2">
                 <div
                   className="md-div"
-                  dangerouslySetInnerHTML={mdToHtml(user.bio)}
+                  dangerouslySetInnerHTML={mdToHtml(uv.user.bio)}
                 />
               </div>
             )}
             <div>
               <ul class="list-inline mb-2">
                 <li className="list-inline-item badge badge-light">
-                  {i18n.t('number_of_posts', { count: user.number_of_posts })}
+                  {i18n.t('number_of_posts', { count: uv.counts.post_count })}
                 </li>
                 <li className="list-inline-item badge badge-light">
                   {i18n.t('number_of_comments', {
-                    count: user.number_of_comments,
+                    count: uv.counts.comment_count,
                   })}
                 </li>
               </ul>
             </div>
             <div class="text-muted">
-              {i18n.t('joined')} <MomentTime data={user} showAgo />
+              {i18n.t('joined')} <MomentTime data={uv.user} showAgo />
             </div>
             <div className="d-flex align-items-center text-muted mb-2">
               <svg class="icon">
@@ -478,7 +482,7 @@ export class User extends Component<any, UserState> {
               </svg>
               <span className="ml-2">
                 {i18n.t('cake_day_title')}{' '}
-                {moment.utc(user.published).local().format('MMM DD, YYYY')}
+                {moment.utc(uv.user.published).local().format('MMM DD, YYYY')}
               </span>
             </div>
           </div>
@@ -704,7 +708,7 @@ export class User extends Component<any, UserState> {
                   />
                 </div>
               </div>
-              {this.state.siteRes.site.enable_nsfw && (
+              {this.state.siteRes.site_view.site.enable_nsfw && (
                 <div class="form-group">
                   <div class="form-check">
                     <input
@@ -840,17 +844,9 @@ export class User extends Component<any, UserState> {
             <div class="card-body">
               <h5>{i18n.t('moderates')}</h5>
               <ul class="list-unstyled mb-0">
-                {this.state.userRes.moderates.map(community => (
+                {this.state.userRes.moderates.map(cmv => (
                   <li>
-                    <CommunityLink
-                      community={{
-                        name: community.community_name,
-                        id: community.community_id,
-                        local: community.community_local,
-                        actor_id: community.community_actor_id,
-                        icon: community.community_icon,
-                      }}
-                    />
+                    <CommunityLink community={cmv.community} />
                   </li>
                 ))}
               </ul>
@@ -869,17 +865,9 @@ export class User extends Component<any, UserState> {
             <div class="card-body">
               <h5>{i18n.t('subscribed')}</h5>
               <ul class="list-unstyled mb-0">
-                {this.state.userRes.follows.map(community => (
+                {this.state.userRes.follows.map(cfv => (
                   <li>
-                    <CommunityLink
-                      community={{
-                        name: community.community_name,
-                        id: community.community_id,
-                        local: community.community_local,
-                        actor_id: community.community_actor_id,
-                        icon: community.community_icon,
-                      }}
-                    />
+                    <CommunityLink community={cfv.community} />
                   </li>
                 ))}
               </ul>
@@ -946,16 +934,12 @@ export class User extends Component<any, UserState> {
   }
 
   handleUserSettingsSortTypeChange(val: SortType) {
-    this.state.userSettingsForm.default_sort_type = Object.keys(
-      SortType
-    ).indexOf(val);
+    this.state.userSettingsForm.default_sort_type = val;
     this.setState(this.state);
   }
 
   handleUserSettingsListingTypeChange(val: ListingType) {
-    this.state.userSettingsForm.default_listing_type = Object.keys(
-      ListingType
-    ).indexOf(val);
+    this.state.userSettingsForm.default_listing_type = val;
     this.setState(this.state);
   }
 
@@ -998,7 +982,7 @@ export class User extends Component<any, UserState> {
     i.state.userSettingsForm.matrix_user_id = event.target.value;
     if (
       i.state.userSettingsForm.matrix_user_id == '' &&
-      !i.state.userRes.user.matrix_user_id
+      !i.state.userRes.user_view.user.matrix_user_id
     ) {
       i.state.userSettingsForm.matrix_user_id = undefined;
     }
@@ -1034,7 +1018,7 @@ export class User extends Component<any, UserState> {
     i.state.userSettingsLoading = true;
     i.setState(i.state);
 
-    WebSocketService.Instance.saveUserSettings(i.state.userSettingsForm);
+    WebSocketService.Instance.client.saveUserSettings(i.state.userSettingsForm);
   }
 
   handleDeleteAccountShowConfirmToggle(i: User, event: any) {
@@ -1058,7 +1042,7 @@ export class User extends Component<any, UserState> {
     i.state.deleteAccountLoading = true;
     i.setState(i.state);
 
-    WebSocketService.Instance.deleteAccount(i.state.deleteAccountForm);
+    WebSocketService.Instance.client.deleteAccount(i.state.deleteAccountForm);
     i.handleLogoutClick(i);
   }
 
@@ -1089,9 +1073,9 @@ export class User extends Component<any, UserState> {
     }
   }
 
-  parseMessage(msg: WebSocketJsonResponse) {
+  parseMessage(msg: any) {
     console.log(msg);
-    const res = wsJsonToRes(msg);
+    let op = wsUserOp(msg);
     if (msg.error) {
       toast(i18n.t(msg.error), 'danger');
       if (msg.error == 'couldnt_find_that_username_or_email') {
@@ -1104,83 +1088,83 @@ export class User extends Component<any, UserState> {
       return;
     } else if (msg.reconnect) {
       this.fetchUserData();
-    } else if (res.op == UserOperation.GetUserDetails) {
+    } else if (op == UserOperation.GetUserDetails) {
       // Since the UserDetails contains posts/comments as well as some general user info we listen here as well
       // and set the parent state if it is not set or differs
       // TODO this might need to get abstracted
-      const data = res.data as UserDetailsResponse;
+      let data = wsJsonToRes<GetUserDetailsResponse>(msg).data;
       this.state.userRes = data;
       this.setUserInfo();
       this.state.loading = false;
       this.setState(this.state);
-    } else if (res.op == UserOperation.SaveUserSettings) {
-      const data = res.data as LoginResponse;
+    } else if (op == UserOperation.SaveUserSettings) {
+      let data = wsJsonToRes<LoginResponse>(msg).data;
       UserService.Instance.login(data);
-      this.state.userRes.user.bio = this.state.userSettingsForm.bio;
-      this.state.userRes.user.preferred_username = this.state.userSettingsForm.preferred_username;
-      this.state.userRes.user.banner = this.state.userSettingsForm.banner;
-      this.state.userRes.user.avatar = this.state.userSettingsForm.avatar;
+      this.state.userRes.user_view.user.bio = this.state.userSettingsForm.bio;
+      this.state.userRes.user_view.user.preferred_username = this.state.userSettingsForm.preferred_username;
+      this.state.userRes.user_view.user.banner = this.state.userSettingsForm.banner;
+      this.state.userRes.user_view.user.avatar = this.state.userSettingsForm.avatar;
       this.state.userSettingsLoading = false;
       this.setState(this.state);
 
       window.scrollTo(0, 0);
-    } else if (res.op == UserOperation.DeleteAccount) {
+    } else if (op == UserOperation.DeleteAccount) {
       this.setState({
         deleteAccountLoading: false,
         deleteAccountShowConfirm: false,
       });
       this.context.router.history.push('/');
-    } else if (res.op == UserOperation.AddAdmin) {
-      const 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.CreateCommentLike) {
-      const data = res.data as CommentResponse;
-      createCommentLikeRes(data, this.state.userRes.comments);
+    } else if (op == UserOperation.CreateCommentLike) {
+      let data = wsJsonToRes<CommentResponse>(msg).data;
+      createCommentLikeRes(data.comment_view, this.state.userRes.comments);
       this.setState(this.state);
     } else if (
-      res.op == UserOperation.EditComment ||
-      res.op == UserOperation.DeleteComment ||
-      res.op == UserOperation.RemoveComment
+      op == UserOperation.EditComment ||
+      op == UserOperation.DeleteComment ||
+      op == UserOperation.RemoveComment
     ) {
-      const data = res.data as CommentResponse;
-      editCommentRes(data, this.state.userRes.comments);
+      let data = wsJsonToRes<CommentResponse>(msg).data;
+      editCommentRes(data.comment_view, this.state.userRes.comments);
       this.setState(this.state);
-    } else if (res.op == UserOperation.CreateComment) {
-      const data = res.data as CommentResponse;
+    } else if (op == UserOperation.CreateComment) {
+      let data = wsJsonToRes<CommentResponse>(msg).data;
       if (
         UserService.Instance.user &&
-        data.comment.creator_id == UserService.Instance.user.id
+        data.comment_view.creator.id == UserService.Instance.user.id
       ) {
         toast(i18n.t('reply_sent'));
       }
-    } else if (res.op == UserOperation.SaveComment) {
-      const data = res.data as CommentResponse;
-      saveCommentRes(data, this.state.userRes.comments);
+    } else if (op == UserOperation.SaveComment) {
+      let data = wsJsonToRes<CommentResponse>(msg).data;
+      saveCommentRes(data.comment_view, this.state.userRes.comments);
       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 ||
-      res.op == UserOperation.SavePost
+      op == UserOperation.EditPost ||
+      op == UserOperation.DeletePost ||
+      op == UserOperation.RemovePost ||
+      op == UserOperation.LockPost ||
+      op == UserOperation.StickyPost ||
+      op == UserOperation.SavePost
     ) {
-      let data = res.data as PostResponse;
-      editPostFindRes(data, this.state.userRes.posts);
+      let data = wsJsonToRes<PostResponse>(msg).data;
+      editPostFindRes(data.post_view, this.state.userRes.posts);
       this.setState(this.state);
-    } else if (res.op == UserOperation.CreatePostLike) {
-      const data = res.data as PostResponse;
-      createPostLikeFindRes(data, this.state.userRes.posts);
+    } else if (op == UserOperation.CreatePostLike) {
+      let data = wsJsonToRes<PostResponse>(msg).data;
+      createPostLikeFindRes(data.post_view, this.state.userRes.posts);
       this.setState(this.state);
-    } else if (res.op == UserOperation.BanUser) {
-      const data = res.data as BanUserResponse;
+    } else if (op == UserOperation.BanUser) {
+      let data = wsJsonToRes<BanUserResponse>(msg).data;
       this.state.userRes.comments
-        .filter(c => c.creator_id == data.user.id)
-        .forEach(c => (c.banned = data.banned));
+        .filter(c => c.creator.id == data.user_view.user.id)
+        .forEach(c => (c.creator.banned = data.banned));
       this.state.userRes.posts
-        .filter(c => c.creator_id == data.user.id)
-        .forEach(c => (c.banned = data.banned));
+        .filter(c => c.creator.id == data.user_view.user.id)
+        .forEach(c => (c.creator.banned = data.banned));
       this.setState(this.state);
     }
   }
diff --git a/src/shared/interfaces.ts b/src/shared/interfaces.ts
index df083b9..116f5d1 100644
--- a/src/shared/interfaces.ts
+++ b/src/shared/interfaces.ts
@@ -1,9 +1,14 @@
-import { GetSiteResponse, LemmyHttp } from 'lemmy-js-client';
+import {
+  CommentView,
+  GetSiteResponse,
+  LemmyHttp,
+  UserMentionView,
+} from 'lemmy-js-client';
 
 export interface IsoData {
   path: string;
   routeData: any[];
-  site: GetSiteResponse;
+  site_res: GetSiteResponse;
   // Lang and theme
   lang: string;
   // communities?: ListCommunitiesResponse;
@@ -21,6 +26,20 @@ export interface InitialFetchRequest {
   client: LemmyHttp;
 }
 
+export interface CommentNode {
+  comment_view: CommentView | UserMentionView;
+  children?: CommentNode[];
+  depth?: number;
+}
+
+export interface PostFormParams {
+  name: string;
+  url?: string;
+  body?: string;
+  community_name?: string;
+  community_id?: number;
+}
+
 export enum CommentSortType {
   Hot,
   Top,
diff --git a/src/shared/services/UserService.ts b/src/shared/services/UserService.ts
index 72066ad..391a5ef 100644
--- a/src/shared/services/UserService.ts
+++ b/src/shared/services/UserService.ts
@@ -1,8 +1,10 @@
 // import Cookies from 'js-cookie';
 import IsomorphicCookie from 'isomorphic-cookie';
-import { User, LoginResponse } from 'lemmy-js-client';
+import { User_, LoginResponse } from 'lemmy-js-client';
 import jwt_decode from 'jwt-decode';
 import { Subject, BehaviorSubject } from 'rxjs';
+import { i18n } from '../i18next';
+import { toast } from '../utils';
 
 interface Claims {
   id: number;
@@ -11,7 +13,7 @@ interface Claims {
 
 export class UserService {
   private static _instance: UserService;
-  public user: User;
+  public user: User_;
   public claims: Claims;
   public jwtSub: Subject<string> = new Subject<string>();
   public unreadCountSub: BehaviorSubject<number> = new BehaviorSubject<number>(
@@ -48,6 +50,15 @@ export class UserService {
     return IsomorphicCookie.load('jwt');
   }
 
+  public authField(throwErr: boolean = true): string {
+    if (this.auth == null && throwErr) {
+      toast(i18n.t('not_logged_in'), 'danger');
+      throw 'Not logged in';
+    } else {
+      return this.auth;
+    }
+  }
+
   private setClaims(jwt: string) {
     this.claims = jwt_decode(jwt);
     this.jwtSub.next(jwt);
diff --git a/src/shared/services/WebSocketService.ts b/src/shared/services/WebSocketService.ts
index 53af6d1..868bd40 100644
--- a/src/shared/services/WebSocketService.ts
+++ b/src/shared/services/WebSocketService.ts
@@ -1,66 +1,10 @@
 import { wsUri } from '../env';
 import {
   LemmyWebsocket,
-  LoginForm,
-  RegisterForm,
-  CommunityForm,
-  DeleteCommunityForm,
-  RemoveCommunityForm,
-  PostForm,
-  DeletePostForm,
-  RemovePostForm,
-  LockPostForm,
-  StickyPostForm,
-  SavePostForm,
-  CommentForm,
-  DeleteCommentForm,
-  RemoveCommentForm,
-  MarkCommentAsReadForm,
-  SaveCommentForm,
-  CommentLikeForm,
-  GetPostForm,
-  GetPostsForm,
-  CreatePostLikeForm,
-  GetCommunityForm,
-  FollowCommunityForm,
-  GetFollowedCommunitiesForm,
-  GetUserDetailsForm,
-  ListCommunitiesForm,
-  GetModlogForm,
-  BanFromCommunityForm,
-  AddModToCommunityForm,
-  TransferCommunityForm,
-  AddAdminForm,
-  TransferSiteForm,
-  BanUserForm,
-  SiteForm,
-  UserView,
-  GetRepliesForm,
-  GetUserMentionsForm,
-  MarkUserMentionAsReadForm,
-  SearchForm,
-  UserSettingsForm,
-  DeleteAccountForm,
-  PasswordResetForm,
-  PasswordChangeForm,
-  PrivateMessageForm,
-  EditPrivateMessageForm,
-  DeletePrivateMessageForm,
-  MarkPrivateMessageAsReadForm,
-  GetPrivateMessagesForm,
-  GetCommentsForm,
-  UserJoinForm,
-  GetSiteConfig,
-  GetSiteForm,
-  SiteConfigForm,
-  MarkAllAsReadForm,
+  UserViewSafe,
   WebSocketJsonResponse,
-  CommunityJoinForm,
-  PostJoinForm,
 } from 'lemmy-js-client';
-import { UserService } from './';
-import { i18n } from '../i18next';
-import { toast, isBrowser } from '../utils';
+import { isBrowser } from '../utils';
 import { Observable } from 'rxjs';
 import { share } from 'rxjs/operators';
 import {
@@ -77,9 +21,9 @@ export class WebSocketService {
   };
   public subject: Observable<any>;
 
-  public admins: UserView[];
-  public banned: UserView[];
-  private client = new LemmyWebsocket();
+  public admins: UserViewSafe[];
+  public banned: UserViewSafe[];
+  public client = new LemmyWebsocket();
 
   private constructor() {
     this.ws = new ReconnectingWebSocket(wsUri, [], this.wsOptions);
@@ -93,7 +37,7 @@ export class WebSocketService {
         console.log(`Connected to ${wsUri}`);
 
         if (!firstConnect) {
-          let res: WebSocketJsonResponse = {
+          let res: WebSocketJsonResponse<any> = {
             reconnect: true,
           };
           obs.next(res);
@@ -107,307 +51,6 @@ export class WebSocketService {
   public static get Instance() {
     return this._instance || (this._instance = new this());
   }
-
-  public userJoin() {
-    let form: UserJoinForm = { auth: UserService.Instance.auth };
-    this.ws.send(this.client.userJoin(form));
-  }
-
-  public postJoin(form: PostJoinForm) {
-    this.ws.send(this.client.postJoin(form));
-  }
-
-  public communityJoin(form: CommunityJoinForm) {
-    this.ws.send(this.client.communityJoin(form));
-  }
-
-  public login(form: LoginForm) {
-    this.ws.send(this.client.login(form));
-  }
-
-  public register(form: RegisterForm) {
-    this.ws.send(this.client.register(form));
-  }
-
-  public getCaptcha() {
-    this.ws.send(this.client.getCaptcha());
-  }
-
-  public createCommunity(form: CommunityForm) {
-    this.setAuth(form); // TODO all these setauths at some point would be good to make required
-    this.ws.send(this.client.createCommunity(form));
-  }
-
-  public editCommunity(form: CommunityForm) {
-    this.setAuth(form);
-    this.ws.send(this.client.editCommunity(form));
-  }
-
-  public deleteCommunity(form: DeleteCommunityForm) {
-    this.setAuth(form);
-    this.ws.send(this.client.deleteCommunity(form));
-  }
-
-  public removeCommunity(form: RemoveCommunityForm) {
-    this.setAuth(form);
-    this.ws.send(this.client.removeCommunity(form));
-  }
-
-  public followCommunity(form: FollowCommunityForm) {
-    this.setAuth(form);
-    this.ws.send(this.client.followCommunity(form));
-  }
-
-  public listCommunities(form: ListCommunitiesForm) {
-    this.setAuth(form, false);
-    this.ws.send(this.client.listCommunities(form));
-  }
-
-  public getFollowedCommunities() {
-    let form: GetFollowedCommunitiesForm = { auth: UserService.Instance.auth };
-    this.ws.send(this.client.getFollowedCommunities(form));
-  }
-
-  public listCategories() {
-    this.ws.send(this.client.listCategories());
-  }
-
-  public createPost(form: PostForm) {
-    this.setAuth(form);
-    this.ws.send(this.client.createPost(form));
-  }
-
-  public getPost(form: GetPostForm) {
-    this.setAuth(form, false);
-    this.ws.send(this.client.getPost(form));
-  }
-
-  public getCommunity(form: GetCommunityForm) {
-    this.setAuth(form, false);
-    this.ws.send(this.client.getCommunity(form));
-  }
-
-  public createComment(form: CommentForm) {
-    this.setAuth(form);
-    this.ws.send(this.client.createComment(form));
-  }
-
-  public editComment(form: CommentForm) {
-    this.setAuth(form);
-    this.ws.send(this.client.editComment(form));
-  }
-
-  public deleteComment(form: DeleteCommentForm) {
-    this.setAuth(form);
-    this.ws.send(this.client.deleteComment(form));
-  }
-
-  public removeComment(form: RemoveCommentForm) {
-    this.setAuth(form);
-    this.ws.send(this.client.removeComment(form));
-  }
-
-  public markCommentAsRead(form: MarkCommentAsReadForm) {
-    this.setAuth(form);
-    this.ws.send(this.client.markCommentAsRead(form));
-  }
-
-  public likeComment(form: CommentLikeForm) {
-    this.setAuth(form);
-    this.ws.send(this.client.likeComment(form));
-  }
-
-  public saveComment(form: SaveCommentForm) {
-    this.setAuth(form);
-    this.ws.send(this.client.saveComment(form));
-  }
-
-  public getPosts(form: GetPostsForm) {
-    this.setAuth(form, false);
-    this.ws.send(this.client.getPosts(form));
-  }
-
-  public getComments(form: GetCommentsForm) {
-    this.setAuth(form, false);
-    this.ws.send(this.client.getComments(form));
-  }
-
-  public likePost(form: CreatePostLikeForm) {
-    this.setAuth(form);
-    this.ws.send(this.client.likePost(form));
-  }
-
-  public editPost(form: PostForm) {
-    this.setAuth(form);
-    this.ws.send(this.client.editPost(form));
-  }
-
-  public deletePost(form: DeletePostForm) {
-    this.setAuth(form);
-    this.ws.send(this.client.deletePost(form));
-  }
-
-  public removePost(form: RemovePostForm) {
-    this.setAuth(form);
-    this.ws.send(this.client.removePost(form));
-  }
-
-  public lockPost(form: LockPostForm) {
-    this.setAuth(form);
-    this.ws.send(this.client.lockPost(form));
-  }
-
-  public stickyPost(form: StickyPostForm) {
-    this.setAuth(form);
-    this.ws.send(this.client.stickyPost(form));
-  }
-
-  public savePost(form: SavePostForm) {
-    this.setAuth(form);
-    this.ws.send(this.client.savePost(form));
-  }
-
-  public banFromCommunity(form: BanFromCommunityForm) {
-    this.setAuth(form);
-    this.ws.send(this.client.banFromCommunity(form));
-  }
-
-  public addModToCommunity(form: AddModToCommunityForm) {
-    this.setAuth(form);
-    this.ws.send(this.client.addModToCommunity(form));
-  }
-
-  public transferCommunity(form: TransferCommunityForm) {
-    this.setAuth(form);
-    this.ws.send(this.client.transferCommunity(form));
-  }
-
-  public transferSite(form: TransferSiteForm) {
-    this.setAuth(form);
-    this.ws.send(this.client.transferSite(form));
-  }
-
-  public banUser(form: BanUserForm) {
-    this.setAuth(form);
-    this.ws.send(this.client.banUser(form));
-  }
-
-  public addAdmin(form: AddAdminForm) {
-    this.setAuth(form);
-    this.ws.send(this.client.addAdmin(form));
-  }
-
-  public getUserDetails(form: GetUserDetailsForm) {
-    this.setAuth(form, false);
-    this.ws.send(this.client.getUserDetails(form));
-  }
-
-  public getReplies(form: GetRepliesForm) {
-    this.setAuth(form);
-    this.ws.send(this.client.getReplies(form));
-  }
-
-  public getUserMentions(form: GetUserMentionsForm) {
-    this.setAuth(form);
-    this.ws.send(this.client.getUserMentions(form));
-  }
-
-  public markUserMentionAsRead(form: MarkUserMentionAsReadForm) {
-    this.setAuth(form);
-    this.ws.send(this.client.markUserMentionAsRead(form));
-  }
-
-  public getModlog(form: GetModlogForm) {
-    this.ws.send(this.client.getModlog(form));
-  }
-
-  public createSite(form: SiteForm) {
-    this.setAuth(form);
-    this.ws.send(this.client.createSite(form));
-  }
-
-  public editSite(form: SiteForm) {
-    this.setAuth(form);
-    this.ws.send(this.client.editSite(form));
-  }
-
-  public getSite(form: GetSiteForm = {}) {
-    this.setAuth(form, false);
-    this.ws.send(this.client.getSite(form));
-  }
-
-  public getSiteConfig() {
-    let form: GetSiteConfig = {};
-    this.setAuth(form);
-    this.ws.send(this.client.getSiteConfig(form));
-  }
-
-  public search(form: SearchForm) {
-    this.setAuth(form, false);
-    this.ws.send(this.client.search(form));
-  }
-
-  public markAllAsRead() {
-    let form: MarkAllAsReadForm = { auth: null };
-    this.setAuth(form);
-    this.ws.send(this.client.markAllAsRead(form));
-  }
-
-  public saveUserSettings(form: UserSettingsForm) {
-    this.setAuth(form);
-    this.ws.send(this.client.saveUserSettings(form));
-  }
-
-  public deleteAccount(form: DeleteAccountForm) {
-    this.setAuth(form);
-    this.ws.send(this.client.deleteAccount(form));
-  }
-
-  public passwordReset(form: PasswordResetForm) {
-    this.ws.send(this.client.passwordReset(form));
-  }
-
-  public passwordChange(form: PasswordChangeForm) {
-    this.ws.send(this.client.passwordChange(form));
-  }
-
-  public createPrivateMessage(form: PrivateMessageForm) {
-    this.setAuth(form);
-    this.ws.send(this.client.createPrivateMessage(form));
-  }
-
-  public editPrivateMessage(form: EditPrivateMessageForm) {
-    this.setAuth(form);
-    this.ws.send(this.client.editPrivateMessage(form));
-  }
-
-  public deletePrivateMessage(form: DeletePrivateMessageForm) {
-    this.setAuth(form);
-    this.ws.send(this.client.deletePrivateMessage(form));
-  }
-
-  public markPrivateMessageAsRead(form: MarkPrivateMessageAsReadForm) {
-    this.setAuth(form);
-    this.ws.send(this.client.markPrivateMessageAsRead(form));
-  }
-
-  public getPrivateMessages(form: GetPrivateMessagesForm) {
-    this.setAuth(form);
-    this.ws.send(this.client.getPrivateMessages(form));
-  }
-
-  public saveSiteConfig(form: SiteConfigForm) {
-    this.setAuth(form);
-    this.ws.send(this.client.saveSiteConfig(form));
-  }
-
-  public setAuth(obj: any, throwErr: boolean = true) {
-    obj.auth = UserService.Instance.auth;
-    if (obj.auth == null && throwErr) {
-      toast(i18n.t('not_logged_in'), 'danger');
-      throw 'Not logged in';
-    }
-  }
 }
 
 if (isBrowser()) {
diff --git a/src/shared/utils.ts b/src/shared/utils.ts
index c823901..603512b 100644
--- a/src/shared/utils.ts
+++ b/src/shared/utils.ts
@@ -30,23 +30,25 @@ import 'moment/locale/da';
 
 import {
   UserOperation,
-  Comment,
-  CommentNode as CommentNodeI,
-  Post,
-  PrivateMessage,
-  User,
+  CommentView,
+  User_,
   SortType,
   ListingType,
   SearchType,
   WebSocketResponse,
   WebSocketJsonResponse,
-  SearchForm,
+  Search,
   SearchResponse,
-  CommentResponse,
-  PostResponse,
+  PostView,
+  PrivateMessageView,
 } from 'lemmy-js-client';
 
-import { CommentSortType, DataType, IsoData } from './interfaces';
+import {
+  CommentSortType,
+  DataType,
+  IsoData,
+  CommentNode as CommentNodeI,
+} from './interfaces';
 import { UserService, WebSocketService } from './services';
 
 var Tribute;
@@ -154,14 +156,20 @@ export function randomStr(
     .join('');
 }
 
-export function wsJsonToRes(msg: WebSocketJsonResponse): WebSocketResponse {
-  let opStr: string = msg.op;
+export function wsJsonToRes<ResponseType>(
+  msg: WebSocketJsonResponse<ResponseType>
+): WebSocketResponse<ResponseType> {
   return {
-    op: UserOperation[opStr],
+    op: wsUserOp(msg),
     data: msg.data,
   };
 }
 
+export function wsUserOp(msg: any): UserOperation {
+  let opStr: string = msg.op;
+  return UserOperation[opStr];
+}
+
 export const md = new markdown_it({
   html: false,
   linkify: true,
@@ -190,12 +198,16 @@ export const md = new markdown_it({
     defs: objectFlip(emojiShortName),
   });
 
-export function hotRankComment(comment: Comment): number {
-  return hotRank(comment.score, comment.published);
+export function hotRankComment(comment_view: CommentView): number {
+  return hotRank(comment_view.counts.score, comment_view.comment.published);
+}
+
+export function hotRankActivePost(post_view: PostView): number {
+  return hotRank(post_view.counts.score, post_view.counts.newest_comment_time);
 }
 
-export function hotRankPost(post: Post): number {
-  return hotRank(post.score, post.newest_activity_time);
+export function hotRankPost(post_view: PostView): number {
+  return hotRank(post_view.counts.score, post_view.post.published);
 }
 
 export function hotRank(score: number, timeStr: string): number {
@@ -221,17 +233,8 @@ export function getUnixTime(text: string): number {
   return text ? new Date(text).getTime() / 1000 : undefined;
 }
 
-export function addTypeInfo<T>(
-  arr: T[],
-  name: string
-): { type_: string; data: T }[] {
-  return arr.map(e => {
-    return { type_: name, data: e };
-  });
-}
-
 export function canMod(
-  user: User,
+  user: User_,
   modIds: number[],
   creator_id: number,
   onSelf: boolean = false
@@ -509,21 +512,6 @@ export function isCakeDay(published: string): boolean {
   );
 }
 
-export function isCommentType(
-  item: Comment | PrivateMessage | Post
-): item is Comment {
-  return (
-    (item as Comment).community_id !== undefined &&
-    (item as Comment).content !== undefined
-  );
-}
-
-export function isPostType(
-  item: Comment | PrivateMessage | Post
-): item is Post {
-  return (item as Post).stickied !== undefined;
-}
-
 export function toast(text: string, background: string = 'success') {
   if (isBrowser()) {
     let backgroundColor = `var(--${background})`;
@@ -592,32 +580,34 @@ export function messageToastify(info: NotifyInfo, router: any) {
   }
 }
 
-export function notifyPost(post: Post, router: any) {
+export function notifyPost(post_view: PostView, router: any) {
   let info: NotifyInfo = {
-    name: post.community_name,
-    icon: post.community_icon ? post.community_icon : defaultFavIcon,
-    link: `/post/${post.id}`,
-    body: post.name,
+    name: post_view.community.name,
+    icon: post_view.community.icon ? post_view.community.icon : defaultFavIcon,
+    link: `/post/${post_view.post.id}`,
+    body: post_view.post.name,
   };
   notify(info, router);
 }
 
-export function notifyComment(comment: Comment, router: any) {
+export function notifyComment(comment_view: CommentView, router: any) {
   let info: NotifyInfo = {
-    name: comment.creator_name,
-    icon: comment.creator_avatar ? comment.creator_avatar : defaultFavIcon,
-    link: `/post/${comment.post_id}/comment/${comment.id}`,
-    body: comment.content,
+    name: comment_view.creator.name,
+    icon: comment_view.creator.avatar
+      ? comment_view.creator.avatar
+      : defaultFavIcon,
+    link: `/post/${comment_view.post.id}/comment/${comment_view.comment.id}`,
+    body: comment_view.comment.content,
   };
   notify(info, router);
 }
 
-export function notifyPrivateMessage(pm: PrivateMessage, router: any) {
+export function notifyPrivateMessage(pmv: PrivateMessageView, router: any) {
   let info: NotifyInfo = {
-    name: pm.creator_name,
-    icon: pm.creator_avatar ? pm.creator_avatar : defaultFavIcon,
+    name: pmv.creator.name,
+    icon: pmv.creator.avatar ? pmv.creator.avatar : defaultFavIcon,
     link: `/inbox`,
-    body: pm.content,
+    body: pmv.private_message.content,
   };
   notify(info, router);
 }
@@ -723,27 +713,28 @@ export function setupTippy() {
 
 function userSearch(text: string, cb: any) {
   if (text) {
-    let form: SearchForm = {
+    let form: Search = {
       q: text,
       type_: SearchType.Users,
       sort: SortType.TopAll,
       page: 1,
       limit: mentionDropdownFetchLimit,
+      auth: UserService.Instance.authField(false),
     };
 
-    WebSocketService.Instance.search(form);
+    WebSocketService.Instance.client.search(form);
 
     let userSub = WebSocketService.Instance.subject.subscribe(
       msg => {
         let res = wsJsonToRes(msg);
         if (res.op == UserOperation.Search) {
           let data = res.data as SearchResponse;
-          let users = data.users.map(u => {
+          let users = data.users.map(uv => {
             return {
-              key: `@${u.name}@${hostname(u.actor_id)}`,
-              name: u.name,
-              local: u.local,
-              id: u.id,
+              key: `@${uv.user.name}@${hostname(uv.user.actor_id)}`,
+              name: uv.user.name,
+              local: uv.user.local,
+              id: uv.user.id,
             };
           });
           cb(users);
@@ -760,27 +751,28 @@ function userSearch(text: string, cb: any) {
 
 function communitySearch(text: string, cb: any) {
   if (text) {
-    let form: SearchForm = {
+    let form: Search = {
       q: text,
       type_: SearchType.Communities,
       sort: SortType.TopAll,
       page: 1,
       limit: mentionDropdownFetchLimit,
+      auth: UserService.Instance.authField(false),
     };
 
-    WebSocketService.Instance.search(form);
+    WebSocketService.Instance.client.search(form);
 
     let communitySub = WebSocketService.Instance.subject.subscribe(
       msg => {
         let res = wsJsonToRes(msg);
         if (res.op == UserOperation.Search) {
           let data = res.data as SearchResponse;
-          let communities = data.communities.map(c => {
+          let communities = data.communities.map(cv => {
             return {
-              key: `!${c.name}@${hostname(c.actor_id)}`,
-              name: c.name,
-              local: c.local,
-              id: c.id,
+              key: `!${cv.community.name}@${hostname(cv.community.actor_id)}`,
+              name: cv.community.name,
+              local: cv.community.local,
+              id: cv.community.id,
             };
           });
           cb(communities);
@@ -840,84 +832,84 @@ export function getUsernameFromProps(props: any): string {
   return props.match.params.username;
 }
 
-export function editCommentRes(data: CommentResponse, comments: Comment[]) {
-  let found = comments.find(c => c.id == data.comment.id);
+export function editCommentRes(data: CommentView, comments: CommentView[]) {
+  let found = comments.find(c => c.comment.id == data.comment.id);
   if (found) {
-    found.content = data.comment.content;
-    found.updated = data.comment.updated;
-    found.removed = data.comment.removed;
-    found.deleted = data.comment.deleted;
-    found.upvotes = data.comment.upvotes;
-    found.downvotes = data.comment.downvotes;
-    found.score = data.comment.score;
+    found.comment.content = data.comment.content;
+    found.comment.updated = data.comment.updated;
+    found.comment.removed = data.comment.removed;
+    found.comment.deleted = data.comment.deleted;
+    found.counts.upvotes = data.counts.upvotes;
+    found.counts.downvotes = data.counts.downvotes;
+    found.counts.score = data.counts.score;
   }
 }
 
-export function saveCommentRes(data: CommentResponse, comments: Comment[]) {
-  let found = comments.find(c => c.id == data.comment.id);
+export function saveCommentRes(data: CommentView, comments: CommentView[]) {
+  let found = comments.find(c => c.comment.id == data.comment.id);
   if (found) {
-    found.saved = data.comment.saved;
+    found.saved = data.saved;
   }
 }
 
 export function createCommentLikeRes(
-  data: CommentResponse,
-  comments: Comment[]
+  data: CommentView,
+  comments: CommentView[]
 ) {
-  let found: Comment = comments.find(c => c.id === data.comment.id);
+  let found = comments.find(c => c.comment.id === data.comment.id);
   if (found) {
-    found.score = data.comment.score;
-    found.upvotes = data.comment.upvotes;
-    found.downvotes = data.comment.downvotes;
-    if (data.comment.my_vote !== null) {
-      found.my_vote = data.comment.my_vote;
+    found.counts.score = data.counts.score;
+    found.counts.upvotes = data.counts.upvotes;
+    found.counts.downvotes = data.counts.downvotes;
+    if (data.my_vote !== null) {
+      found.my_vote = data.my_vote;
     }
   }
 }
 
-export function createPostLikeFindRes(data: PostResponse, posts: Post[]) {
-  let found = posts.find(c => c.id == data.post.id);
+export function createPostLikeFindRes(data: PostView, posts: PostView[]) {
+  let found = posts.find(p => p.post.id == data.post.id);
   if (found) {
     createPostLikeRes(data, found);
   }
 }
 
-export function createPostLikeRes(data: PostResponse, post: Post) {
-  if (post) {
-    post.score = data.post.score;
-    post.upvotes = data.post.upvotes;
-    post.downvotes = data.post.downvotes;
-    if (data.post.my_vote !== null) {
-      post.my_vote = data.post.my_vote;
+export function createPostLikeRes(data: PostView, post_view: PostView) {
+  if (post_view) {
+    post_view.counts.score = data.counts.score;
+    post_view.counts.upvotes = data.counts.upvotes;
+    post_view.counts.downvotes = data.counts.downvotes;
+    if (data.my_vote !== null) {
+      post_view.my_vote = data.my_vote;
     }
   }
 }
 
-export function editPostFindRes(data: PostResponse, posts: Post[]) {
-  let found = posts.find(c => c.id == data.post.id);
+export function editPostFindRes(data: PostView, posts: PostView[]) {
+  let found = posts.find(p => p.post.id == data.post.id);
   if (found) {
     editPostRes(data, found);
   }
 }
 
-export function editPostRes(data: PostResponse, post: Post) {
+export function editPostRes(data: PostView, post: PostView) {
   if (post) {
-    post.url = data.post.url;
-    post.name = data.post.name;
-    post.nsfw = data.post.nsfw;
-    post.deleted = data.post.deleted;
-    post.removed = data.post.removed;
-    post.stickied = data.post.stickied;
-    post.body = data.post.body;
-    post.locked = data.post.locked;
-    post.saved = data.post.saved;
+    post.post.url = data.post.url;
+    post.post.name = data.post.name;
+    post.post.nsfw = data.post.nsfw;
+    post.post.deleted = data.post.deleted;
+    post.post.removed = data.post.removed;
+    post.post.stickied = data.post.stickied;
+    post.post.body = data.post.body;
+    post.post.locked = data.post.locked;
+    post.saved = data.saved;
   }
 }
 
-export function commentsToFlatNodes(comments: Comment[]): CommentNodeI[] {
+export function commentsToFlatNodes(comments: CommentView[]): CommentNodeI[] {
   let nodes: CommentNodeI[] = [];
   for (let comment of comments) {
-    nodes.push({ comment: comment });
+    nodes.push({ comment_view: comment });
   }
   return nodes;
 }
@@ -927,30 +919,34 @@ export function commentSort(tree: CommentNodeI[], sort: CommentSortType) {
   if (sort == CommentSortType.Top) {
     tree.sort(
       (a, b) =>
-        +a.comment.removed - +b.comment.removed ||
-        +a.comment.deleted - +b.comment.deleted ||
-        b.comment.score - a.comment.score
+        +a.comment_view.comment.removed - +b.comment_view.comment.removed ||
+        +a.comment_view.comment.deleted - +b.comment_view.comment.deleted ||
+        b.comment_view.counts.score - a.comment_view.counts.score
     );
   } else if (sort == CommentSortType.New) {
     tree.sort(
       (a, b) =>
-        +a.comment.removed - +b.comment.removed ||
-        +a.comment.deleted - +b.comment.deleted ||
-        b.comment.published.localeCompare(a.comment.published)
+        +a.comment_view.comment.removed - +b.comment_view.comment.removed ||
+        +a.comment_view.comment.deleted - +b.comment_view.comment.deleted ||
+        b.comment_view.comment.published.localeCompare(
+          a.comment_view.comment.published
+        )
     );
   } else if (sort == CommentSortType.Old) {
     tree.sort(
       (a, b) =>
-        +a.comment.removed - +b.comment.removed ||
-        +a.comment.deleted - +b.comment.deleted ||
-        a.comment.published.localeCompare(b.comment.published)
+        +a.comment_view.comment.removed - +b.comment_view.comment.removed ||
+        +a.comment_view.comment.deleted - +b.comment_view.comment.deleted ||
+        a.comment_view.comment.published.localeCompare(
+          b.comment_view.comment.published
+        )
     );
   } else if (sort == CommentSortType.Hot) {
     tree.sort(
       (a, b) =>
-        +a.comment.removed - +b.comment.removed ||
-        +a.comment.deleted - +b.comment.deleted ||
-        hotRankComment(b.comment) - hotRankComment(a.comment)
+        +a.comment_view.comment.removed - +b.comment_view.comment.removed ||
+        +a.comment_view.comment.deleted - +b.comment_view.comment.deleted ||
+        hotRankComment(b.comment_view) - hotRankComment(a.comment_view)
     );
   }
 
@@ -985,7 +981,7 @@ function convertCommentSortType(sort: SortType): CommentSortType {
 }
 
 export function postSort(
-  posts: Post[],
+  posts: PostView[],
   sort: SortType,
   communityType: boolean
 ) {
@@ -999,34 +995,34 @@ export function postSort(
   ) {
     posts.sort(
       (a, b) =>
-        +a.removed - +b.removed ||
-        +a.deleted - +b.deleted ||
-        (communityType && +b.stickied - +a.stickied) ||
-        b.score - a.score
+        +a.post.removed - +b.post.removed ||
+        +a.post.deleted - +b.post.deleted ||
+        (communityType && +b.post.stickied - +a.post.stickied) ||
+        b.counts.score - a.counts.score
     );
   } else if (sort == SortType.New) {
     posts.sort(
       (a, b) =>
-        +a.removed - +b.removed ||
-        +a.deleted - +b.deleted ||
-        (communityType && +b.stickied - +a.stickied) ||
-        b.published.localeCompare(a.published)
+        +a.post.removed - +b.post.removed ||
+        +a.post.deleted - +b.post.deleted ||
+        (communityType && +b.post.stickied - +a.post.stickied) ||
+        b.post.published.localeCompare(a.post.published)
     );
   } else if (sort == SortType.Hot) {
     posts.sort(
       (a, b) =>
-        +a.removed - +b.removed ||
-        +a.deleted - +b.deleted ||
-        (communityType && +b.stickied - +a.stickied) ||
-        b.hot_rank - a.hot_rank
+        +a.post.removed - +b.post.removed ||
+        +a.post.deleted - +b.post.deleted ||
+        (communityType && +b.post.stickied - +a.post.stickied) ||
+        hotRankPost(b) - hotRankPost(a)
     );
   } else if (sort == SortType.Active) {
     posts.sort(
       (a, b) =>
-        +a.removed - +b.removed ||
-        +a.deleted - +b.deleted ||
-        (communityType && +b.stickied - +a.stickied) ||
-        b.hot_rank_active - a.hot_rank_active
+        +a.post.removed - +b.post.removed ||
+        +a.post.deleted - +b.post.deleted ||
+        (communityType && +b.post.stickied - +a.post.stickied) ||
+        hotRankActivePost(b) - hotRankActivePost(a)
     );
   }
 }
@@ -1094,12 +1090,6 @@ export function isBrowser() {
   return typeof window !== 'undefined';
 }
 
-export function setAuth(obj: any, auth: string) {
-  if (auth) {
-    obj.auth = auth;
-  }
-}
-
 export function setIsoData(context: any): IsoData {
   let isoData: IsoData = isBrowser()
     ? window.isoData
diff --git a/yarn.lock b/yarn.lock
index 0e54c52..292a3c5 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -5518,10 +5518,10 @@ lcid@^1.0.0:
   dependencies:
     invert-kv "^1.0.0"
 
-lemmy-js-client@^1.0.16:
-  version "1.0.16"
-  resolved "https://registry.yarnpkg.com/lemmy-js-client/-/lemmy-js-client-1.0.16.tgz#84bf094c246d987f2f192bfac75340430821fee9"
-  integrity sha512-WvEEGrYNA2dzfzlufWB9LhUcll0O06WaUjSfBn5lYY/SFFsvBW5ImD42P/QwvN8Sgj6xVQiboe+Z8T++iAjKVw==
+lemmy-js-client@1.0.17-beta5:
+  version "1.0.17-beta5"
+  resolved "https://registry.yarnpkg.com/lemmy-js-client/-/lemmy-js-client-1.0.17-beta5.tgz#b8d128ef3a6a17bc3ac4eea8e30618b68ea4f2df"
+  integrity sha512-Z/8HV8tG9aB75GjDX1U2b3pFZnysGIymeVO+oepOkYfhHRB8SKmLS9ATuIw9OW1NjJduxbpGGgDH+bkf0Sx7dA==
 
 leven@^3.1.0:
   version "3.1.0"
-- 
2.44.1