]> Untitled Git - lemmy-ui.git/commitdiff
Merge remote-tracking branch 'origin/drone-ci' into drone-ci-dess
authorDessalines <tyhou13@gmx.com>
Mon, 4 Jan 2021 15:36:26 +0000 (10:36 -0500)
committerDessalines <tyhou13@gmx.com>
Mon, 4 Jan 2021 15:36:26 +0000 (10:36 -0500)
43 files changed:
Dockerfile
package.json
src/client/index.tsx
src/server/index.tsx
src/shared/components/admin-settings.tsx
src/shared/components/app.tsx
src/shared/components/comment-form.tsx
src/shared/components/comment-node.tsx
src/shared/components/comment-nodes.tsx
src/shared/components/communities.tsx
src/shared/components/community-form.tsx
src/shared/components/community-link.tsx
src/shared/components/community.tsx
src/shared/components/create-community.tsx
src/shared/components/create-post.tsx
src/shared/components/create-private-message.tsx
src/shared/components/inbox.tsx
src/shared/components/instances.tsx
src/shared/components/login.tsx
src/shared/components/main.tsx
src/shared/components/modlog.tsx
src/shared/components/navbar.tsx
src/shared/components/password_change.tsx
src/shared/components/post-form.tsx
src/shared/components/post-listing.tsx
src/shared/components/post-listings.tsx
src/shared/components/post.tsx
src/shared/components/private-message-form.tsx
src/shared/components/private-message.tsx
src/shared/components/search.tsx
src/shared/components/setup.tsx
src/shared/components/sidebar.tsx
src/shared/components/site-form.tsx
src/shared/components/theme.tsx
src/shared/components/user-details.tsx
src/shared/components/user-listing.tsx
src/shared/components/user.tsx
src/shared/env.ts
src/shared/interfaces.ts
src/shared/services/UserService.ts
src/shared/services/WebSocketService.ts
src/shared/utils.ts
yarn.lock

index 745651b54f7728b4054a320b2105d133ad816e23..278142202aeb2a7e0f0167875d9e3ec02eee0d29 100644 (file)
@@ -1,5 +1,5 @@
 FROM node:14-alpine as builder\r
-RUN apk update && apk add yarn curl bash && rm -rf /var/cache/apk/*\r
+RUN apk add yarn curl bash --no-cache\r
 \r
 RUN curl -sfL https://install.goreleaser.com/github.com/tj/node-prune.sh | bash -s -- -b /usr/local/bin\r
 \r
@@ -24,7 +24,7 @@ RUN yarn build:prod
 \r
 # Pruning\r
 # RUN npm prune --production\r
-RUN /usr/local/bin/node-prune\r
+RUN node-prune\r
 \r
 FROM node:14-alpine as runner\r
 COPY --from=builder /usr/src/app/dist /app/dist\r
index a22e63388fe68b5f9ba69a03eafcc765e2c5e18c..188e9ddcfa486486da7254bc1dba8c9ba2258ede 100644 (file)
@@ -15,7 +15,7 @@
   },
   "repository": "https://github.com/LemmyNet/lemmy-ui",
   "dependencies": {
-    "@typescript-eslint/parser": "^4.9.1",
+    "@typescript-eslint/parser": "^4.11.0",
     "autosize": "^4.0.2",
     "choices.js": "^9.0.1",
     "emoji-short-name": "^1.0.0",
     "inferno-server": "^7.4.6",
     "isomorphic-cookie": "^1.2.4",
     "jwt-decode": "^3.1.2",
-    "markdown-it": "^12.0.3",
+    "markdown-it": "^12.0.4",
     "markdown-it-container": "^3.0.0",
     "markdown-it-emoji": "^2.0.0",
     "markdown-it-sub": "^1.0.0",
     "markdown-it-sup": "^1.0.0",
     "moment": "^2.29.1",
     "reconnecting-websocket": "^4.4.0",
-    "register-service-worker": "^1.7.1",
+    "register-service-worker": "^1.7.2",
     "rxjs": "^6.6.3",
     "serialize-javascript": "^5.0.1",
     "tippy.js": "^6.2.7",
     "ws": "^7.4.1"
   },
   "devDependencies": {
-    "@babel/core": "^7.12.9",
-    "@babel/plugin-transform-runtime": "^7.12.1",
+    "@babel/core": "^7.12.10",
+    "@babel/plugin-transform-runtime": "^7.12.10",
     "@babel/plugin-transform-typescript": "^7.12.1",
-    "@babel/preset-env": "7.12.7",
+    "@babel/preset-env": "7.12.11",
     "@babel/preset-typescript": "^7.12.7",
     "@babel/runtime": "^7.12.5",
     "@types/autosize": "^3.0.6",
     "@types/express": "^4.17.9",
-    "@types/node": "^14.14.11",
+    "@types/node": "^14.14.16",
     "@types/node-fetch": "^2.5.7",
     "@types/serialize-javascript": "^5.0.0",
     "babel-loader": "^8.2.2",
     "bootstrap": "^4.5.3",
     "bootswatch": "^4.5.3",
     "clean-webpack-plugin": "^3.0.0",
-    "copy-webpack-plugin": "^6.4.0",
+    "copy-webpack-plugin": "^7.0.0",
     "css-loader": "^5.0.1",
-    "eslint": "^7.15.0",
-    "eslint-plugin-jane": "^9.0.4",
-    "husky": "^4.3.5",
-    "lemmy-js-client": "^1.0.16",
+    "eslint": "^7.16.0",
+    "eslint-plugin-jane": "^9.0.6",
+    "husky": "^4.3.6",
+    "lemmy-js-client": "1.0.17-beta6",
     "lint-staged": "^10.5.3",
-    "mini-css-extract-plugin": "^1.3.2",
+    "mini-css-extract-plugin": "^1.3.3",
     "node-fetch": "^2.6.1",
     "node-sass": "^5.0.0",
     "prettier": "^2.2.1",
     "rimraf": "^3.0.2",
     "run-node-webpack-plugin": "^1.3.0",
     "sass-loader": "^10.1.0",
-    "sortpack": "^2.1.10",
+    "sortpack": "^2.1.11",
     "style-loader": "^2.0.0",
     "terser": "^5.5.1",
-    "typescript": "^4.1.2",
-    "webpack": "5.10.0",
+    "typescript": "^4.1.3",
+    "webpack": "5.11.0",
     "webpack-cli": "^4.2.0",
     "webpack-dev-server": "3.11.0",
     "webpack-node-externals": "^2.5.2"
index b7f216d9e4bef8f72efc531e3177a7da826b8416..b07d18096654741a5409dc334b60c791fe36bc4f 100644 (file)
@@ -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>
 );
 
index cc8c3b0bc48a6511c1ba85543aa9e3e9ac46a3d6..10e935d902415e6deb66565da34447ad4dfc3ec4 100644 (file)
@@ -8,13 +8,13 @@ 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';
 import { httpUri } from '../shared/env';
 import { IncomingHttpHeaders } from 'http';
+import { setOptionalAuth } from '../shared/utils';
 
 const server = express();
 const port = 1234;
@@ -30,8 +30,8 @@ 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 = {};
+  setOptionalAuth(getSiteForm, auth);
 
   let promises: Promise<any>[] = [];
 
@@ -70,14 +70,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) {
index 6340d4fc54efe418633c5a9499f17d0be0bc15b9..6b985a434a24b3ba9c343e3bacdafa2a194d58de 100644 (file)
@@ -4,9 +4,8 @@ import {
   UserOperation,
   SiteResponse,
   GetSiteResponse,
-  SiteConfigForm,
+  SaveSiteConfig,
   GetSiteConfigResponse,
-  WebSocketJsonResponse,
   GetSiteConfig,
 } from 'lemmy-js-client';
 import { WebSocketService } from '../services';
@@ -18,7 +17,9 @@ import {
   setIsoData,
   wsSubscribe,
   isBrowser,
-  setAuth,
+  wsUserOp,
+  wsClient,
+  authField,
 } from '../utils';
 import autosize from 'autosize';
 import { SiteForm } from './site-form';
@@ -30,7 +31,7 @@ import { InitialFetchRequest } from 'shared/interfaces';
 interface AdminSettingsState {
   siteRes: GetSiteResponse;
   siteConfigRes: GetSiteConfigResponse;
-  siteConfigForm: SiteConfigForm;
+  siteConfigForm: SaveSiteConfig;
   loading: boolean;
   siteConfigLoading: boolean;
 }
@@ -40,10 +41,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: authField(),
     },
     siteConfigRes: {
       config_hjson: null,
@@ -67,13 +68,16 @@ export class AdminSettings extends Component<any, AdminSettingsState> {
       this.state.siteConfigLoading = false;
       this.state.loading = false;
     } else {
-      WebSocketService.Instance.getSiteConfig();
+      WebSocketService.Instance.send(
+        wsClient.getSiteConfig({
+          auth: 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 +95,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 +116,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 +136,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 +151,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 +202,9 @@ 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.send(
+      wsClient.saveSiteConfig(i.state.siteConfigForm)
+    );
     i.setState(i.state);
   }
 
@@ -223,9 +213,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 +222,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;
index 659464512104a4a679469326aa239cf76fa14cba..8c9fa9f96af023f7fdf7eeebd5f37128479de177 100644 (file)
@@ -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 &&
+              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>
       </>
index b032a065c6dc869b312410462b88f2d3c1e266af..3e81d77525a66c023ce1752140cd26638b03e6d5 100644 (file)
@@ -2,13 +2,20 @@ 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 {
+  authField,
+  capitalizeFirstLetter,
+  wsClient,
+  wsJsonToRes,
+  wsSubscribe,
+  wsUserOp,
+} from '../utils';
 import { WebSocketService, UserService } from '../services';
 import { i18n } from '../i18next';
 import { T } from 'inferno-i18next';
@@ -16,38 +23,29 @@ import { MarkdownTextArea } from './markdown-textarea';
 
 interface CommentFormProps {
   postId?: number;
-  node?: CommentNodeI;
-  onReplyCancel?(): any;
+  node?: CommentNodeI; // Can either be the parent, or the editable comment
   edit?: boolean;
   disabled?: boolean;
   focus?: boolean;
+  onReplyCancel?(): any;
 }
 
 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 +56,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 +69,11 @@ export class CommentForm extends Component<CommentFormProps, CommentFormState> {
       <div class="mb-3">
         {UserService.Instance.user ? (
           <MarkdownTextArea
-            initialContent={this.state.commentForm.content}
+            initialContent={
+              this.props.edit
+                ? this.props.node.comment_view.comment.content
+                : null
+            }
             buttonTitle={this.state.buttonTitle}
             finished={this.state.finished}
             replyType={!!this.props.node}
@@ -110,12 +100,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: authField(),
+      };
+      WebSocketService.Instance.send(wsClient.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.id : null,
+        auth: authField(),
+      };
+      WebSocketService.Instance.send(wsClient.createComment(form));
     }
     this.setState(this.state);
   }
@@ -124,22 +130,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 });
         }
       }
index 030515c04100ce47a55431b2882a653e21860d23..c08edc3a73aa50c9df61e03afb9cbf62e65901fd 100644 (file)
@@ -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,
@@ -27,6 +32,8 @@ import {
   isMod,
   setupTippy,
   colorList,
+  wsClient,
+  authField,
 } from '../utils';
 import moment from 'moment';
 import { MomentTime } from './moment-time';
@@ -70,8 +77,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 +105,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 +125,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 +139,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 +180,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 +188,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 +212,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 +244,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 +254,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 +321,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 +338,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 +346,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 +384,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 +402,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 +429,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 +450,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 +494,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 +535,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 +556,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 +600,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 +697,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 +729,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 +754,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 +762,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 +772,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 +803,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 +813,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 +822,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 +847,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: authField(),
     };
-    WebSocketService.Instance.deleteComment(deleteForm);
+    WebSocketService.Instance.send(wsClient.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: authField(),
     };
 
-    WebSocketService.Instance.saveComment(form);
+    WebSocketService.Instance.send(wsClient.saveComment(form));
 
     i.state.saveLoading = true;
     i.setState(this.state);
@@ -908,12 +894,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: authField(),
     };
 
-    WebSocketService.Instance.likeComment(form);
+    WebSocketService.Instance.send(wsClient.likeComment(form));
     this.setState(this.state);
     setupTippy();
   }
@@ -935,12 +922,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: authField(),
     };
 
-    WebSocketService.Instance.likeComment(form);
+    WebSocketService.Instance.send(wsClient.likeComment(form));
     this.setState(this.state);
     setupTippy();
   }
@@ -961,34 +949,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: authField(),
     };
-    WebSocketService.Instance.removeComment(form);
+    WebSocketService.Instance.send(wsClient.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: authField(),
       };
-      WebSocketService.Instance.markUserMentionAsRead(form);
+      WebSocketService.Instance.send(wsClient.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: authField(),
       };
-      WebSocketService.Instance.markCommentAsRead(form);
+      WebSocketService.Instance.send(wsClient.markCommentAsRead(form));
     }
 
     i.state.readLoading = true;
@@ -1030,37 +1024,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: authField(),
       };
-      WebSocketService.Instance.banFromCommunity(form);
+      WebSocketService.Instance.send(wsClient.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: authField(),
       };
-      WebSocketService.Instance.banUser(form);
+      WebSocketService.Instance.send(wsClient.banUser(form));
     }
 
     i.state.showBanDialog = false;
@@ -1078,12 +1074,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: authField(),
     };
-    WebSocketService.Instance.addModToCommunity(form);
+    WebSocketService.Instance.send(wsClient.addModToCommunity(form));
     i.state.showConfirmAppointAsMod = false;
     i.setState(i.state);
   }
@@ -1099,11 +1097,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: authField(),
     };
-    WebSocketService.Instance.addAdmin(form);
+    WebSocketService.Instance.send(wsClient.addAdmin(form));
     i.state.showConfirmAppointAsAdmin = false;
     i.setState(i.state);
   }
@@ -1119,11 +1118,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: authField(),
     };
-    WebSocketService.Instance.transferCommunity(form);
+    WebSocketService.Instance.send(wsClient.transferCommunity(form));
     i.state.showConfirmTransferCommunity = false;
     i.setState(i.state);
   }
@@ -1139,17 +1140,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: authField(),
     };
-    WebSocketService.Instance.transferSite(form);
+    WebSocketService.Instance.send(wsClient.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);
   }
 
index 3f99bf343fc5ccc343632615cae05e90b99a4613..90a7783a15e2541b120d8f3c900e6c6c35bca01c 100644 (file)
@@ -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}
index 523f9a9b6b61ba7976858db0f713e03ef3d774ea..236f777d67ea98dc90e6cfb501722b6454a8f20f 100644 (file)
@@ -3,14 +3,13 @@ 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 {
@@ -18,9 +17,12 @@ import {
   toast,
   getPageFromProps,
   isBrowser,
-  setAuth,
   setIsoData,
   wsSubscribe,
+  wsUserOp,
+  wsClient,
+  authField,
+  setOptionalAuth,
 } from '../utils';
 import { CommunityLink } from './community-link';
 import { i18n } from '../i18next';
@@ -29,10 +31,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 +48,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 +62,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 +90,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 +126,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 +154,7 @@ export class Communities extends Component<any, CommunitiesState> {
                           <span
                             class="pointer btn-link"
                             onClick={linkEvent(
-                              community.id,
+                              cv.community.id,
                               this.handleSubscribe
                             )}
                           >
@@ -212,64 +212,70 @@ export class Communities extends Component<any, CommunitiesState> {
   }
 
   handleUnsubscribe(communityId: number) {
-    let form: FollowCommunityForm = {
+    let form: FollowCommunity = {
       community_id: communityId,
       follow: false,
+      auth: authField(),
     };
-    WebSocketService.Instance.followCommunity(form);
+    WebSocketService.Instance.send(wsClient.followCommunity(form));
   }
 
   handleSubscribe(communityId: number) {
-    let form: FollowCommunityForm = {
+    let form: FollowCommunity = {
       community_id: communityId,
       follow: true,
+      auth: authField(),
     };
-    WebSocketService.Instance.followCommunity(form);
+    WebSocketService.Instance.send(wsClient.followCommunity(form));
   }
 
   refetch() {
-    let listCommunitiesForm: ListCommunitiesForm = {
+    let listCommunitiesForm: ListCommunities = {
       sort: SortType.TopAll,
       limit: communityLimit,
       page: this.state.page,
+      auth: authField(false),
     };
 
-    WebSocketService.Instance.listCommunities(listCommunitiesForm);
+    WebSocketService.Instance.send(
+      wsClient.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,
     };
-    setAuth(listCommunitiesForm, req.auth);
+    setOptionalAuth(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);
     }
   }
index de7b6b2d78257a560aa6b9503bc36a788f74975e..18406fc7dc98b00120fc82cbec4daf55488f1dbd 100644 (file)
@@ -2,12 +2,12 @@ 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 {
@@ -16,6 +16,9 @@ import {
   toast,
   randomStr,
   wsSubscribe,
+  wsUserOp,
+  wsClient,
+  authField,
 } from '../utils';
 import { i18n } from '../i18next';
 
@@ -23,16 +26,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 +54,7 @@ export class CommunityForm extends Component<
       nsfw: false,
       icon: null,
       banner: null,
+      auth: authField(false),
     },
     loading: false,
   };
@@ -70,17 +74,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: authField(),
       };
     }
 
@@ -88,6 +92,7 @@ export class CommunityForm extends Component<
     this.subscription = wsSubscribe(this.parseMessage);
   }
 
+  // TODO this should be checked out
   componentDidUpdate() {
     if (
       !this.state.loading &&
@@ -119,7 +124,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 +255,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 +280,16 @@ 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.send(wsClient.editCommunity(form));
     } else {
-      WebSocketService.Instance.createCommunity(i.state.communityForm);
+      WebSocketService.Instance.send(
+        wsClient.createCommunity(i.state.communityForm)
+      );
     }
     i.setState(i.state);
   }
@@ -332,22 +343,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);
     }
   }
 }
index 8bf73ca38e0d62211a4a3c02a1053b4ac6ab3eab..b8f3bc05ec2b963975bc03373b1448cd69298a73 100644 (file)
@@ -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;
index c7d0a277cc647dd0d80074bfbc430ccc21c21424..6025e911e88339388aff151ba04ea1a8e2bcb952 100644 (file)
@@ -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,11 @@ import {
   setIsoData,
   wsSubscribe,
   isBrowser,
-  setAuth,
   communityRSSUrl,
+  wsUserOp,
+  wsClient,
+  authField,
+  setOptionalAuth,
 } from '../utils';
 import { i18n } from '../i18next';
 
@@ -63,8 +65,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 +100,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 +129,18 @@ export class Community extends Component<any, State> {
     } else {
       this.fetchCommunity();
       this.fetchData();
-      WebSocketService.Instance.listCategories();
+      WebSocketService.Instance.send(wsClient.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: authField(false),
     };
-    WebSocketService.Instance.getCommunity(form);
+    WebSocketService.Instance.send(wsClient.getCommunity(form));
   }
 
   componentWillUnmount() {
@@ -169,8 +172,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_ };
+    setOptionalAuth(communityForm, req.auth);
     promises.push(req.client.getCommunity(communityForm));
 
     let dataType: DataType = pathSplit[4]
@@ -186,24 +189,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,
       };
+      setOptionalAuth(getPostsForm, 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,
       };
+      setOptionalAuth(getCommentsForm, req.auth);
       this.setIdOrName(getCommentsForm, id, name_);
-      setAuth(getCommentsForm, req.auth);
       promises.push(req.client.getComments(getCommentsForm));
     }
 
@@ -232,10 +235,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 +254,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 +264,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 +279,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 +292,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 +308,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 +345,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 +408,145 @@ 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: authField(false),
       };
-      WebSocketService.Instance.getPosts(getPostsForm);
+      WebSocketService.Instance.send(wsClient.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: authField(false),
       };
-      WebSocketService.Instance.getComments(getCommentsForm);
+      WebSocketService.Instance.send(wsClient.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.send(
+        wsClient.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.send(
+        wsClient.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);
     }
index c96a8260c05494c831dfac43753ad8d3c048f773..aab548078220a565285a29b48d5177e115328a74 100644 (file)
@@ -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,15 @@ import {
   wsJsonToRes,
   wsSubscribe,
   isBrowser,
+  wsUserOp,
+  wsClient,
 } 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 +32,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 +54,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.send(wsClient.listCategories());
     }
   }
 
@@ -64,7 +65,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 +88,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 +97,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);
index a0eeb35c0907476b67f226ea9455981d80af9f37..edb2f059c634146a444b005689144ff75f2cd6ad 100644 (file)
@@ -3,30 +3,32 @@ import { Subscription } from 'rxjs';
 import { PostForm } from './post-form';
 import { HtmlTags } from './html-tags';
 import {
+  authField,
   isBrowser,
-  setAuth,
   setIsoData,
+  setOptionalAuth,
   toast,
+  wsClient,
   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 +36,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 +64,14 @@ export class CreatePost extends Component<any, CreatePostState> {
   }
 
   refetch() {
-    let listCommunitiesForm: ListCommunitiesForm = {
+    let listCommunitiesForm: ListCommunities = {
       sort: SortType.TopAll,
       limit: 9999,
+      auth: authField(false),
     };
-    WebSocketService.Instance.listCommunities(listCommunitiesForm);
+    WebSocketService.Instance.send(
+      wsClient.listCommunities(listCommunitiesForm)
+    );
   }
 
   componentWillUnmount() {
@@ -76,7 +81,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 +105,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 +154,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,
     };
-    setAuth(listCommunitiesForm, req.auth);
+    setOptionalAuth(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);
index e08bc56f5d951ed7f1059a7daa6fbfeff1f2901c..506e3eef26d93240969147a66bc5523d79108613 100644 (file)
@@ -4,22 +4,23 @@ 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 {
+  authField,
   getRecipientIdFromProps,
   isBrowser,
-  setAuth,
   setIsoData,
   toast,
+  wsClient,
   wsJsonToRes,
   wsSubscribe,
+  wsUserOp,
 } from '../utils';
 import { i18n } from '../i18next';
 import { InitialFetchRequest } from 'shared/interfaces';
@@ -27,8 +28,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 +41,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 +71,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: authField(false),
     };
-    WebSocketService.Instance.getUserDetails(form);
+    WebSocketService.Instance.send(wsClient.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 +122,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 +138,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);
     }
index 04eb0431ecf1e8d65059175e945650adca614dfe..6c6f5d903d39235f042024f4e467326b22fba45a 100644 (file)
@@ -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,10 @@ import {
   setupTippy,
   setIsoData,
   wsSubscribe,
-  setAuth,
   isBrowser,
+  wsUserOp,
+  wsClient,
+  authField,
 } from '../utils';
 import { CommentNodes } from './comment-nodes';
 import { PrivateMessage } from './private-message';
@@ -52,17 +53,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 +88,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,
   };
 
@@ -97,10 +108,9 @@ export class Inbox extends Component<any, InboxState> {
 
     // Only fetch the data if coming from another route
     if (this.isoData.path == this.context.router.route.match.url) {
-      this.state.replies = this.isoData.routeData[0].replies;
-      this.state.mentions = this.isoData.routeData[1].mentions;
-      this.state.messages = this.isoData.routeData[2].messages;
-      this.sendUnreadCount();
+      this.state.replies = this.isoData.routeData[0].replies || [];
+      this.state.mentions = this.isoData.routeData[1].mentions || [];
+      this.state.messages = this.isoData.routeData[2].messages || [];
       this.state.loading = false;
     } else {
       this.refetch();
@@ -115,7 +125,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 +298,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 +374,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 +383,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 +401,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 +464,63 @@ 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: authField(),
     };
-    WebSocketService.Instance.getReplies(repliesForm);
+    WebSocketService.Instance.send(wsClient.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: authField(),
     };
-    WebSocketService.Instance.getUserMentions(userMentionsForm);
+    WebSocketService.Instance.send(wsClient.getUserMentions(userMentionsForm));
 
-    let privateMessagesForm: GetPrivateMessagesForm = {
+    let privateMessagesForm: GetPrivateMessages = {
       unread_only: this.state.unreadOrAll == UnreadOrAll.Unread,
       page: this.state.page,
       limit: fetchLimit,
+      auth: authField(),
     };
-    WebSocketService.Instance.getPrivateMessages(privateMessagesForm);
+    WebSocketService.Instance.send(
+      wsClient.getPrivateMessages(privateMessagesForm)
+    );
   }
 
   handleSortChange(val: SortType) {
@@ -475,7 +531,11 @@ export class Inbox extends Component<any, InboxState> {
   }
 
   markAllAsRead(i: Inbox) {
-    WebSocketService.Instance.markAllAsRead();
+    WebSocketService.Instance.send(
+      wsClient.markAllAsRead({
+        auth: authField(),
+      })
+    );
     i.state.replies = [];
     i.state.mentions = [];
     i.state.messages = [];
@@ -484,148 +544,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 +730,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
     );
   }
index 7be27c70b7ae4fb5fd6dac4dcf7f3fe48ef883cc..85a3c4597bea160fdcfe4bde4529c776dd224be1 100644 (file)
@@ -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() {
index f3b0536a9edb6f8043b1d2ef2d86320d3bfa0e69..c650c38e15070b9b4be482a34e2b05bcaddb1c14 100644 (file)
@@ -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,21 @@ import {
   wsSubscribe,
   isBrowser,
   setIsoData,
+  wsUserOp,
+  wsClient,
+  authField,
 } 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 +57,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 +69,7 @@ export class Login extends Component<any, State> {
     this.subscription = wsSubscribe(this.parseMessage);
 
     if (isBrowser()) {
-      WebSocketService.Instance.getCaptcha();
+      WebSocketService.Instance.send(wsClient.getCaptcha());
     }
   }
 
@@ -78,7 +80,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 +282,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 +351,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.send(wsClient.login(i.state.loginForm));
   }
 
   handleLoginUsernameChange(i: Login, event: any) {
@@ -366,7 +368,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.send(wsClient.register(i.state.registerForm));
   }
 
   handleRegisterUsernameChange(i: Login, event: any) {
@@ -404,15 +406,15 @@ export class Login extends Component<any, State> {
 
   handleRegenCaptcha(_i: Login, event: any) {
     event.preventDefault();
-    WebSocketService.Instance.getCaptcha();
+    WebSocketService.Instance.send(wsClient.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.send(wsClient.passwordReset(resetForm));
   }
 
   handleCaptchaPlay(i: Login, event: any) {
@@ -432,44 +434,52 @@ 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.send(wsClient.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.send(
+          wsClient.userJoin({
+            auth: 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.send(
+          wsClient.userJoin({
+            auth: 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);
       }
     }
index 0814bdd6a25ce136a1e60ebedcd7da698c5601f0..fbccf51a6922df2d4c154ae814b1ea8dfd7a6ac5 100644 (file)
@@ -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,23 @@ import {
   setIsoData,
   wsSubscribe,
   isBrowser,
-  setAuth,
+  wsUserOp,
+  setOptionalAuth,
+  wsClient,
+  authField,
 } 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 +97,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 +136,11 @@ export class Main extends Component<any, MainState> {
       this.fetchTrendingCommunities();
       this.fetchData();
       if (UserService.Instance.user) {
-        WebSocketService.Instance.getFollowedCommunities();
+        WebSocketService.Instance.send(
+          wsClient.getFollowedCommunities({
+            auth: authField(),
+          })
+        );
       }
     }
 
@@ -142,20 +148,23 @@ export class Main extends Component<any, MainState> {
   }
 
   fetchTrendingCommunities() {
-    let listCommunitiesForm: ListCommunitiesForm = {
+    let listCommunitiesForm: ListCommunities = {
       sort: SortType.Hot,
       limit: 6,
+      auth: authField(false),
     };
-    WebSocketService.Instance.listCommunities(listCommunitiesForm);
+    WebSocketService.Instance.send(
+      wsClient.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.send(wsClient.communityJoin({ community_id: 0 }));
   }
 
   componentWillUnmount() {
@@ -199,26 +208,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_,
       };
-      setAuth(getPostsForm, req.auth);
+      setOptionalAuth(getPostsForm, req.auth);
       promises.push(req.client.getPosts(getPostsForm));
     } else {
-      let getCommentsForm: GetCommentsForm = {
+      let getCommentsForm: GetComments = {
         page,
         limit: fetchLimit,
         sort,
         type_,
       };
-      setAuth(getCommentsForm, req.auth);
+      setOptionalAuth(getCommentsForm, req.auth);
       promises.push(req.client.getComments(getCommentsForm));
     }
 
-    let trendingCommunitiesForm: ListCommunitiesForm = {
+    let trendingCommunitiesForm: ListCommunities = {
       sort: SortType.Hot,
       limit: 6,
     };
@@ -245,7 +254,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 +267,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 +327,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 +349,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 +360,7 @@ export class Main extends Component<any, MainState> {
   }
 
   sidebar() {
+    let site = this.state.siteRes.site_view.site;
     return (
       <div>
         {!this.state.showEditSite ? (
@@ -365,14 +369,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 +392,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 +408,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 +418,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 +426,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 +477,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 +505,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 +522,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 +612,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 +621,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 +663,72 @@ 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: authField(false),
       };
-      WebSocketService.Instance.getPosts(getPostsForm);
+      WebSocketService.Instance.send(wsClient.getPosts(getPostsForm));
     } else {
-      let getCommentsForm: GetCommentsForm = {
+      let getCommentsForm: GetComments = {
         page: this.state.page,
         limit: fetchLimit,
         sort: this.state.sort,
         type_: this.state.listingType,
+        auth: authField(false),
       };
-      WebSocketService.Instance.getComments(getCommentsForm);
+      WebSocketService.Instance.send(wsClient.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.send(
+        wsClient.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 +737,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 +803,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);
     }
   }
index ded9a30a096526f3940541a4d94663f435952963..cfdaef74d7dd61660b22eaec885a0d15ec2b30cd 100644 (file)
@@ -3,51 +3,72 @@ 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,
+  wsClient,
 } 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 +76,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 +106,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 +119,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 +353,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 +435,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.send(wsClient.getModlog(modlogForm));
   }
 
   static fetchInitialData(req: InitialFetchRequest): Promise<any>[] {
@@ -448,7 +448,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 +461,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);
     }
   }
index f5a4c14415b866f64849e51e45e963648df508f6..ea191b8f422693a46b60ce21c7b9eb6d62050b14 100644 (file)
@@ -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,23 @@ import {
   isBrowser,
   wsSubscribe,
   supportLemmyUrl,
+  wsUserOp,
+  wsClient,
+  authField,
 } 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 +58,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 +90,11 @@ export class Navbar extends Component<NavbarProps, NavbarState> {
         // i18n.changeLanguage('de');
       } else {
         this.requestNotificationPermission();
-        WebSocketService.Instance.userJoin();
+        WebSocketService.Instance.send(
+          wsClient.userJoin({
+            auth: authField(),
+          })
+        );
         this.fetchUnreads();
       }
 
@@ -96,7 +102,9 @@ export class Navbar extends Component<NavbarProps, NavbarState> {
         // A login
         if (res !== undefined) {
           this.requestNotificationPermission();
-          WebSocketService.Instance.getSite();
+          WebSocketService.Instance.send(
+            wsClient.getSite({ auth: authField() })
+          );
         } else {
           this.setState({ isLoggedIn: false });
         }
@@ -165,20 +173,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 || UserService.Instance.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 +373,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 +382,71 @@ export class Navbar extends Component<NavbarProps, NavbarState> {
       }
       return;
     } else if (msg.reconnect) {
-      WebSocketService.Instance.userJoin();
+      WebSocketService.Instance.send(
+        wsClient.userJoin({
+          auth: 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;
+      console.log(data.my_user);
       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 +454,37 @@ 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: authField(),
     };
 
-    let userMentionsForm: GetUserMentionsForm = {
+    let userMentionsForm: GetUserMentions = {
       sort: SortType.New,
       unread_only: true,
       page: 1,
       limit: fetchLimit,
+      auth: authField(),
     };
 
-    let privateMessagesForm: GetPrivateMessagesForm = {
+    let privateMessagesForm: GetPrivateMessages = {
       unread_only: true,
       page: 1,
       limit: fetchLimit,
+      auth: authField(),
     };
 
     if (this.currentLocation !== '/inbox') {
-      WebSocketService.Instance.getReplies(repliesForm);
-      WebSocketService.Instance.getUserMentions(userMentionsForm);
-      WebSocketService.Instance.getPrivateMessages(privateMessagesForm);
+      WebSocketService.Instance.send(wsClient.getReplies(repliesForm));
+      WebSocketService.Instance.send(
+        wsClient.getUserMentions(userMentionsForm)
+      );
+      WebSocketService.Instance.send(
+        wsClient.getPrivateMessages(privateMessagesForm)
+      );
     }
   }
 
@@ -471,17 +498,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)
     );
   }
index 172590609c224007355c22b3b47631b0fbdabb9d..6ffa3e0775f50beac3614653a620c6e798b745e3 100644 (file)
@@ -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,8 @@ import {
   setIsoData,
   isBrowser,
   wsSubscribe,
+  wsUserOp,
+  wsClient,
 } from '../utils';
 import { i18n } from '../i18next';
 import { HtmlTags } from './html-tags';
@@ -22,7 +23,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 +37,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 +56,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 +139,20 @@ export class PasswordChange extends Component<any, State> {
     i.state.loading = true;
     i.setState(i.state);
 
-    WebSocketService.Instance.passwordChange(i.state.passwordChangeForm);
+    WebSocketService.Instance.send(
+      wsClient.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);
index 5e683f1e46d4c156330347d81312d79ea3f94e65..3a59f457494e93493b56bbfd1804ad9ed8809dd6 100644 (file)
@@ -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,9 @@ import {
   validTitle,
   wsSubscribe,
   isBrowser,
+  wsUserOp,
+  wsClient,
+  authField,
 } from '../utils';
 
 var Choices;
@@ -46,24 +49,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 +75,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: authField(false),
     },
     loading: false,
     imageLoading: false,
@@ -93,16 +96,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: authField(),
       };
     }
 
@@ -285,7 +287,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 +300,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 +344,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 +374,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.send(wsClient.editPost(form));
     } else {
-      WebSocketService.Instance.createPost(i.state.postForm);
+      WebSocketService.Instance.send(wsClient.createPost(i.state.postForm));
     }
     i.state.loading = true;
     i.setState(i.state);
@@ -396,15 +404,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: authField(false),
       };
 
-      WebSocketService.Instance.search(form);
+      WebSocketService.Instance.send(wsClient.search(form));
 
       // Fetch the page title
       getPageTitle(this.state.postForm.url).then(d => {
@@ -424,17 +433,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: authField(false),
     };
 
     if (this.state.postForm.name !== '') {
-      WebSocketService.Instance.search(form);
+      WebSocketService.Instance.send(wsClient.search(form));
     } else {
       this.state.suggestedPosts = [];
     }
@@ -570,16 +580,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 +606,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;
index b9485b7904dc1afaaf285f03d2ed12606da6edf5..de20c27cff72ac4ffcb5df3b4c3f690898b6c7b3 100644 (file)
@@ -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';
@@ -36,6 +36,8 @@ import {
   setupTippy,
   hostname,
   previewLines,
+  wsClient,
+  authField,
 } from '../utils';
 import { i18n } from '../i18next';
 import { externalHost } from '../env';
@@ -62,11 +64,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 +90,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 +107,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 +128,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 +141,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 +164,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 +192,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 +272,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 +284,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 +314,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 +379,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 +402,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 +479,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 +518,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 +572,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>
@@ -610,21 +601,21 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
   }
 
   duplicatesLine() {
+    let dupes = this.props.duplicates;
     return (
-      this.props.post.duplicates && (
+      dupes &&
+      dupes.length > 0 && (
         <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 => (
+            {dupes.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 +626,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 +637,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 +675,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 +701,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 +722,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 +737,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 +754,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 +772,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 +793,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 +846,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 +861,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 +916,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 +972,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 +982,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 +999,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 +1069,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 +1077,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 +1087,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 +1096,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 +1112,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 +1131,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 +1141,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 +1150,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 +1176,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: authField(),
     };
 
-    WebSocketService.Instance.likePost(form);
+    WebSocketService.Instance.send(wsClient.likePost(form));
     i.setState(i.state);
     setupTippy();
   }
@@ -1205,12 +1208,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: authField(),
     };
 
-    WebSocketService.Instance.likePost(form);
+    WebSocketService.Instance.send(wsClient.likePost(form));
     i.setState(i.state);
     setupTippy();
   }
@@ -1232,33 +1236,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: authField(),
     };
-    WebSocketService.Instance.deletePost(deleteForm);
+    WebSocketService.Instance.send(wsClient.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: authField(),
     };
 
-    WebSocketService.Instance.savePost(form);
+    WebSocketService.Instance.send(wsClient.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 +1286,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: authField(),
     };
-    WebSocketService.Instance.removePost(form);
+    WebSocketService.Instance.send(wsClient.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: authField(),
     };
-    WebSocketService.Instance.lockPost(form);
+    WebSocketService.Instance.send(wsClient.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: authField(),
     };
-    WebSocketService.Instance.stickyPost(form);
+    WebSocketService.Instance.send(wsClient.stickyPost(form));
   }
 
   handleModBanFromCommunityShow(i: PostListing) {
@@ -1349,33 +1355,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: authField(),
       };
-      WebSocketService.Instance.banFromCommunity(form);
+      WebSocketService.Instance.send(wsClient.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: authField(),
       };
-      WebSocketService.Instance.banUser(form);
+      WebSocketService.Instance.send(wsClient.banUser(form));
     }
 
     i.state.showBanDialog = false;
@@ -1383,21 +1391,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: authField(),
     };
-    WebSocketService.Instance.addModToCommunity(form);
+    WebSocketService.Instance.send(wsClient.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: authField(),
     };
-    WebSocketService.Instance.addAdmin(form);
+    WebSocketService.Instance.send(wsClient.addAdmin(form));
     i.setState(i.state);
   }
 
@@ -1412,11 +1422,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: authField(),
     };
-    WebSocketService.Instance.transferCommunity(form);
+    WebSocketService.Instance.send(wsClient.transferCommunity(form));
     i.state.showConfirmTransferCommunity = false;
     i.setState(i.state);
   }
@@ -1432,10 +1443,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: authField(),
     };
-    WebSocketService.Instance.transferSite(form);
+    WebSocketService.Instance.send(wsClient.transferSite(form));
     i.state.showConfirmTransferSite = false;
     i.setState(i.state);
   }
index 47aab75460ec4fe1995d50add17910face13bbe4..a7e069372c37959d6ef579341ee102781693b57e 100644 (file)
@@ -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 {
index 017ff451cdf1ce64ea403e7878a7c8a0fca0cf9f..160dfccd74ba81efb18d0fc13c738f2b3f992e55 100644 (file)
@@ -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,13 @@ import {
   getIdFromProps,
   getCommentIdFromProps,
   wsSubscribe,
-  setAuth,
   isBrowser,
   previewLines,
   isImage,
+  wsUserOp,
+  wsClient,
+  authField,
+  setOptionalAuth,
 } from '../utils';
 import { PostListing } from './post-listing';
 import { Sidebar } from './sidebar';
@@ -64,7 +66,7 @@ interface PostState {
   commentViewType: CommentViewType;
   scrolled?: boolean;
   loading: boolean;
-  crossPosts: PostI[];
+  crossPosts: PostView[];
   siteRes: GetSiteResponse;
   categories: Category[];
 }
@@ -81,7 +83,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 +106,16 @@ export class Post extends Component<any, PostState> {
       }
     } else {
       this.fetchPost();
-      WebSocketService.Instance.listCategories();
+      WebSocketService.Instance.send(wsClient.listCategories());
     }
   }
 
   fetchPost() {
-    let form: GetPostForm = {
+    let form: GetPost = {
       id: this.state.postId,
+      auth: authField(false),
     };
-    WebSocketService.Instance.getPost(form);
+    WebSocketService.Instance.send(wsClient.getPost(form));
   }
 
   static fetchInitialData(req: InitialFetchRequest): Promise<any>[] {
@@ -121,10 +124,10 @@ export class Post extends Component<any, PostState> {
 
     let id = Number(pathSplit[2]);
 
-    let postForm: GetPostForm = {
+    let postForm: GetPost = {
       id,
     };
-    setAuth(postForm, req.auth);
+    setOptionalAuth(postForm, req.auth);
 
     promises.push(req.client.getPost(postForm));
     promises.push(req.client.listCategories());
@@ -138,7 +141,9 @@ export class Post extends Component<any, PostState> {
   }
 
   componentDidMount() {
-    WebSocketService.Instance.postJoin({ post_id: this.state.postId });
+    WebSocketService.Instance.send(
+      wsClient.postJoin({ post_id: this.state.postId })
+    );
     autosize(document.querySelectorAll('textarea'));
   }
 
@@ -172,23 +177,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: authField(),
       };
-      WebSocketService.Instance.markCommentAsRead(form);
+      WebSocketService.Instance.send(wsClient.markCommentAsRead(form));
       UserService.Instance.unreadCountSub.next(
         UserService.Instance.unreadCountSub.value - 1
       );
@@ -196,27 +206,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 +242,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 +353,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 +369,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 +395,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 +420,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 +431,148 @@ 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({
-        id: postId,
-      });
-    } else if (res.op == UserOperation.GetPost) {
-      let data = res.data as GetPostResponse;
+      WebSocketService.Instance.send(wsClient.postJoin({ post_id: postId }));
+      WebSocketService.Instance.send(
+        wsClient.getPost({
+          id: postId,
+          auth: authField(false),
+        })
+      );
+    } else if (op == UserOperation.GetPost) {
+      let data = wsJsonToRes<GetPostResponse>(msg).data;
       this.state.postRes = data;
       this.state.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: authField(false),
         };
-        WebSocketService.Instance.search(form);
+        WebSocketService.Instance.send(wsClient.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);
     }
index 6abc5f337eb923af31b50379be74d8c6ad903897..9c518aa07d588096d99325ca584dc7b416589e34 100644 (file)
@@ -2,13 +2,12 @@ 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 {
@@ -18,6 +17,9 @@ import {
   setupTippy,
   wsSubscribe,
   isBrowser,
+  wsUserOp,
+  wsClient,
+  authField,
 } from '../utils';
 import { UserListing } from './user-listing';
 import { MarkdownTextArea } from './markdown-textarea';
@@ -25,15 +27,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 +50,7 @@ export class PrivateMessageForm extends Component<
     privateMessageForm: {
       content: null,
       recipient_id: this.props.recipient.id,
+      auth: authField(),
     },
     loading: false,
     previewMode: false,
@@ -64,11 +67,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 +107,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,14 +190,15 @@ 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: authField(),
       };
-      WebSocketService.Instance.editPrivateMessage(editForm);
+      WebSocketService.Instance.send(wsClient.editPrivateMessage(form));
     } else {
-      WebSocketService.Instance.createPrivateMessage(
-        i.state.privateMessageForm
+      WebSocketService.Instance.send(
+        wsClient.createPrivateMessage(i.state.privateMessageForm)
       );
     }
     i.state.loading = true;
@@ -232,25 +225,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);
     }
   }
index a0e86a77fa1caaab927f0d930abc7de54412fd48..9e2fd76ccef384146b4d4b6a5f530afbb73c60f4 100644 (file)
@@ -1,12 +1,12 @@
 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';
+import { authField, mdToHtml, toast, wsClient } from '../utils';
 import { MomentTime } from './moment-time';
 import { PrivateMessageForm } from './private-message-form';
 import { UserListing } from './user-listing';
@@ -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: authField(),
     };
-    WebSocketService.Instance.deletePrivateMessage(form);
+    WebSocketService.Instance.send(wsClient.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: authField(),
     };
-    WebSocketService.Instance.markPrivateMessageAsRead(form);
+    WebSocketService.Instance.send(wsClient.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);
index 6796ad3586304577458fc58c0910968b8aac6a3b..9af800e26bc30edeeffe646c209e96415236c303 100644 (file)
@@ -2,17 +2,16 @@ 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';
@@ -27,7 +26,10 @@ import {
   commentsToFlatNodes,
   setIsoData,
   wsSubscribe,
-  setAuth,
+  wsUserOp,
+  wsClient,
+  authField,
+  setOptionalAuth,
 } from '../utils';
 import { PostListing } from './post-listing';
 import { HtmlTags } from './html-tags';
@@ -80,7 +82,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 {
@@ -143,7 +145,7 @@ export class Search extends Component<any, SearchState> {
       page: this.getPageFromProps(pathSplit[9]),
       limit: fetchLimit,
     };
-    setAuth(form, req.auth);
+    setOptionalAuth(form, req.auth);
 
     if (form.q != '') {
       promises.push(req.client.search(form));
@@ -252,19 +254,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 +281,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 +301,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 +310,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 +348,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 +372,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 +459,11 @@ export class Search extends Component<any, SearchState> {
       sort: this.state.sort,
       page: this.state.page,
       limit: fetchLimit,
+      auth: authField(false),
     };
 
     if (this.state.q != '') {
-      WebSocketService.Instance.search(form);
+      WebSocketService.Instance.send(wsClient.search(form));
     }
   }
 
@@ -495,25 +503,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);
     }
   }
index 33fa44323232f9d2919bdae64705457b0eca4a19..bb30bc2c32ac5511eaffd2cda0915697096ee5ac 100644 (file)
@@ -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, wsClient } 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.send(wsClient.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 = '/';
     }
   }
index 82b05af38dc82342f5e4d5ffefa9689d227bae63..d44eb14ab8b81a6524ec6db3dfec6242a8129454 100644 (file)
@@ -1,17 +1,17 @@
 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';
-import { mdToHtml, getUnixTime } from '../utils';
+import { mdToHtml, getUnixTime, wsClient, authField } from '../utils';
 import { CommunityForm } from './community-form';
 import { UserListing } from './user-listing';
 import { CommunityLink } from './community-link';
@@ -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: authField(),
     };
-    WebSocketService.Instance.deleteCommunity(deleteForm);
+    WebSocketService.Instance.send(wsClient.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: authField(),
     };
-    WebSocketService.Instance.addModToCommunity(form);
+    WebSocketService.Instance.send(wsClient.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: authField(),
     };
-    WebSocketService.Instance.followCommunity(form);
+    WebSocketService.Instance.send(wsClient.followCommunity(form));
   }
 
   handleSubscribe(communityId: number, event: any) {
     event.preventDefault();
-    let form: FollowCommunityForm = {
+    let form: FollowCommunity = {
       community_id: communityId,
       follow: true,
+      auth: authField(),
     };
-    WebSocketService.Instance.followCommunity(form);
+    WebSocketService.Instance.send(wsClient.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: authField(),
     };
-    WebSocketService.Instance.removeCommunity(removeForm);
+    WebSocketService.Instance.send(wsClient.removeCommunity(removeForm));
 
     i.state.showRemoveDialog = false;
     i.setState(i.state);
index 9b572f57ecd0ed406dbf4498d3198c58e047e5fa..2446b055df9dc3b02b1672f61c9d8a4aa9dee7d7 100644 (file)
@@ -2,9 +2,14 @@ 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 { Site, EditSite } from 'lemmy-js-client';
 import { WebSocketService } from '../services';
-import { capitalizeFirstLetter, randomStr } from '../utils';
+import {
+  authField,
+  capitalizeFirstLetter,
+  randomStr,
+  wsClient,
+} from '../utils';
 import { i18n } from '../i18next';
 
 interface SiteFormProps {
@@ -13,7 +18,7 @@ interface SiteFormProps {
 }
 
 interface SiteFormState {
-  siteForm: SiteFormI;
+  siteForm: EditSite;
   loading: boolean;
 }
 
@@ -27,6 +32,7 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
       name: null,
       icon: null,
       banner: null,
+      auth: authField(),
     },
     loading: false,
   };
@@ -54,6 +60,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: authField(),
       };
     }
   }
@@ -242,9 +249,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.send(wsClient.editSite(i.state.siteForm));
     } else {
-      WebSocketService.Instance.createSite(i.state.siteForm);
+      WebSocketService.Instance.send(wsClient.createSite(i.state.siteForm));
     }
     i.setState(i.state);
   }
index 5dfc01cebb6b7beefd010d71809bc263002020f7..8da4ee1c27b960975886908106e33b38fcc64cb4 100644 (file)
@@ -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> {
index 01c9aed1c72eab95a585cb3709ae672e64649b78..59e844daccddecfa684958d1a1963bc087ae1f62 100644 (file)
@@ -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.Post,
+      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}
index a8e4025f1e21502774390fe46ef221ba3768b171..a20785a77568411a6977d3fa78a803e4b25a5f1e 100644 (file)
@@ -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;
index b03d9ddd92213f3d40782017ea4d6285745ea083..f541c2f75d770cf1d37ddc1b71f483ece0efe924 100644 (file)
@@ -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,12 @@ import {
   editCommentRes,
   saveCommentRes,
   createPostLikeFindRes,
-  setAuth,
   previewLines,
   editPostFindRes,
+  wsUserOp,
+  wsClient,
+  authField,
+  setOptionalAuth,
 } from '../utils';
 import { UserListing } from './user-listing';
 import { HtmlTags } from './html-tags';
@@ -58,18 +60,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 +108,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: authField(false),
     },
     userSettingsLoading: null,
     deleteAccountLoading: null,
     deleteAccountShowConfirm: false,
     deleteAccountForm: {
       password: null,
+      auth: authField(false),
     },
-    siteRes: this.isoData.site,
+    siteRes: this.isoData.site_res,
   };
 
   constructor(props: any, context: any) {
@@ -147,6 +150,8 @@ export class User extends Component<any, UserState> {
     // Only fetch the data if coming from another route
     if (this.isoData.path == this.context.router.route.match.url) {
       this.state.userRes = this.isoData.routeData[0];
+      this.state.userRes.user_view =
+        this.state.userRes.user_view || this.state.userRes.user_view_dangerous;
       this.setUserInfo();
       this.state.loading = false;
     } else {
@@ -157,21 +162,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: authField(false),
     };
-    WebSocketService.Instance.getUserDetails(form);
+    WebSocketService.Instance.send(wsClient.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 +211,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,
     };
+    setOptionalAuth(form, req.auth);
     this.setIdOrName(form, user_id, username);
-    setAuth(form, req.auth);
     promises.push(req.client.getUserDetails(form));
     return promises;
   }
@@ -251,12 +257,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 +283,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 +291,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 +400,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 +441,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 +487,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 +713,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 +849,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 +870,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>
@@ -998,7 +991,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 +1027,9 @@ export class User extends Component<any, UserState> {
     i.state.userSettingsLoading = true;
     i.setState(i.state);
 
-    WebSocketService.Instance.saveUserSettings(i.state.userSettingsForm);
+    WebSocketService.Instance.send(
+      wsClient.saveUserSettings(i.state.userSettingsForm)
+    );
   }
 
   handleDeleteAccountShowConfirmToggle(i: User, event: any) {
@@ -1058,7 +1053,9 @@ export class User extends Component<any, UserState> {
     i.state.deleteAccountLoading = true;
     i.setState(i.state);
 
-    WebSocketService.Instance.deleteAccount(i.state.deleteAccountForm);
+    WebSocketService.Instance.send(
+      wsClient.deleteAccount(i.state.deleteAccountForm)
+    );
     i.handleLogoutClick(i);
   }
 
@@ -1089,9 +1086,8 @@ export class User extends Component<any, UserState> {
     }
   }
 
-  parseMessage(msg: WebSocketJsonResponse) {
-    console.log(msg);
-    const res = wsJsonToRes(msg);
+  parseMessage(msg: any) {
+    let op = wsUserOp(msg);
     if (msg.error) {
       toast(i18n.t(msg.error), 'danger');
       if (msg.error == 'couldnt_find_that_username_or_email') {
@@ -1104,83 +1100,85 @@ 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.state.userRes.user_view =
+        this.state.userRes.user_view || this.state.userRes.user_view_dangerous;
       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);
     }
   }
index 59566a1b3f1b13b3ed55f60469ac8da60093873e..02d66e88447a0ea2ccf63c3f7f80626137bffe68 100644 (file)
@@ -25,8 +25,8 @@ const secure = isBrowser()
 const host = isBrowser() ? externalHost : internalHost;
 
 const httpBase = `http://${host}`; // Don't use secure here
-export const wsUri = `ws${secure}://${host}/api/v1/ws`;
-export const httpUri = `${httpBase}/api/v1`;
+export const wsUri = `ws${secure}://${host}/api/v2/ws`;
+export const httpUri = `${httpBase}/api/v2`;
 export const pictrsUri = `http${secure}://${host}/pictrs/image`;
 
 console.log(`httpbase: ${httpBase}`);
index df083b9bcfad9267399989645c190e5c59b6210a..116f5d150323f89ee5f61f0b48507568157fd085 100644 (file)
@@ -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,
index 72066ad4778686943d4d0fb61092ff779bc686a9..cdcd63f3b5890094628e0a6c9132404c873406a6 100644 (file)
@@ -1,6 +1,6 @@
 // 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';
 
@@ -11,7 +11,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>(
index 53af6d1916b0bfdd4a98a4a58cc00375c5cce653..3aab0e29f16218dd35083107f8a55161d647f0cf 100644 (file)
@@ -1,66 +1,6 @@
 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,
-  WebSocketJsonResponse,
-  CommunityJoinForm,
-  PostJoinForm,
-} from 'lemmy-js-client';
-import { UserService } from './';
-import { i18n } from '../i18next';
-import { toast, isBrowser } from '../utils';
+import { UserViewSafe, WebSocketJsonResponse } from 'lemmy-js-client';
+import { isBrowser } from '../utils';
 import { Observable } from 'rxjs';
 import { share } from 'rxjs/operators';
 import {
@@ -70,16 +10,15 @@ import {
 
 export class WebSocketService {
   private static _instance: WebSocketService;
-  public ws: ReconnectingWebSocket;
+  private ws: ReconnectingWebSocket;
   public wsOptions: WSOptions = {
     connectionTimeout: 5000,
     maxRetries: 10,
   };
   public subject: Observable<any>;
 
-  public admins: UserView[];
-  public banned: UserView[];
-  private client = new LemmyWebsocket();
+  public admins: UserViewSafe[];
+  public banned: UserViewSafe[];
 
   private constructor() {
     this.ws = new ReconnectingWebSocket(wsUri, [], this.wsOptions);
@@ -93,7 +32,7 @@ export class WebSocketService {
         console.log(`Connected to ${wsUri}`);
 
         if (!firstConnect) {
-          let res: WebSocketJsonResponse = {
+          let res: WebSocketJsonResponse<any> = {
             reconnect: true,
           };
           obs.next(res);
@@ -102,316 +41,19 @@ export class WebSocketService {
         firstConnect = false;
       };
     }).pipe(share());
-  }
-
-  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));
+    if (isBrowser()) {
+      window.onbeforeunload = () => {
+        this.ws.close();
+      };
+    }
   }
 
-  public saveSiteConfig(form: SiteConfigForm) {
-    this.setAuth(form);
-    this.ws.send(this.client.saveSiteConfig(form));
+  public send(data: string) {
+    this.ws.send(data);
   }
 
-  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';
-    }
+  public static get Instance() {
+    return this._instance || (this._instance = new this());
   }
 }
-
-if (isBrowser()) {
-  window.onbeforeunload = () => {
-    WebSocketService.Instance.ws.close();
-  };
-}
index c823901dbc23d0cbe0d50d4608c59bf3b46e8659..433b674baeefaa13cc45b9ae4a106513edfabb1f 100644 (file)
@@ -30,23 +30,26 @@ 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,
+  LemmyWebsocket,
 } 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;
@@ -64,6 +67,9 @@ import tippy from 'tippy.js';
 import moment from 'moment';
 import { Subscription } from 'rxjs';
 import { retryWhen, delay, take } from 'rxjs/operators';
+import { i18n } from './i18next';
+
+export const wsClient = new LemmyWebsocket();
 
 export const favIconUrl = '/static/assets/favicon.svg';
 export const favIconPngUrl = '/static/assets/apple-touch-icon.png';
@@ -154,14 +160,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 +202,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 +237,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
@@ -290,6 +297,14 @@ export function routeSortTypeToEnum(sort: string): SortType {
   return SortType[sort];
 }
 
+export function listingTypeFromNum(type_: number): ListingType {
+  return Object.values(ListingType)[type_];
+}
+
+export function sortTypeFromNum(type_: number): SortType {
+  return Object.values(SortType)[type_];
+}
+
 export function routeListingTypeToEnum(type: string): ListingType {
   return ListingType[type];
 }
@@ -509,21 +524,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 +592,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 +725,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: authField(false),
     };
 
-    WebSocketService.Instance.search(form);
+    WebSocketService.Instance.send(wsClient.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 +763,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: authField(false),
     };
 
-    WebSocketService.Instance.search(form);
+    WebSocketService.Instance.send(wsClient.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 +844,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 +931,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 +993,7 @@ function convertCommentSortType(sort: SortType): CommentSortType {
 }
 
 export function postSort(
-  posts: Post[],
+  posts: PostView[],
   sort: SortType,
   communityType: boolean
 ) {
@@ -999,34 +1007,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 +1102,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
@@ -1121,6 +1123,24 @@ export function wsSubscribe(parseMessage: any): Subscription {
   }
 }
 
+export function setOptionalAuth(obj: any, auth = UserService.Instance.auth) {
+  if (auth) {
+    obj.auth = auth;
+  }
+}
+
+export function authField(
+  throwErr: boolean = true,
+  auth = UserService.Instance.auth
+): string {
+  if (auth == null && throwErr) {
+    toast(i18n.t('not_logged_in'), 'danger');
+    throw 'Not logged in';
+  } else {
+    return auth;
+  }
+}
+
 moment.updateLocale('en', {
   relativeTime: {
     future: 'in %s',
index 0e54c52636199f8b8cbb1117243f071b1bbc2c8e..50206a37334fc6f428ad8fb380e0f689ab2ae1e5 100644 (file)
--- a/yarn.lock
+++ b/yarn.lock
@@ -9,30 +9,36 @@
   dependencies:
     "@babel/highlight" "^7.10.4"
 
+"@babel/code-frame@^7.12.11":
+  version "7.12.11"
+  resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.12.11.tgz#f4ad435aa263db935b8f10f2c552d23fb716a63f"
+  integrity sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw==
+  dependencies:
+    "@babel/highlight" "^7.10.4"
+
 "@babel/compat-data@^7.12.5", "@babel/compat-data@^7.12.7":
   version "7.12.7"
   resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.12.7.tgz#9329b4782a7d6bbd7eef57e11addf91ee3ef1e41"
   integrity sha512-YaxPMGs/XIWtYqrdEOZOCPsVWfEoriXopnsz3/i7apYPXQ3698UFhS6dVT1KN5qOsWmVgw/FOrmQgpRaZayGsw==
 
-"@babel/core@^7.12.9":
-  version "7.12.9"
-  resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.12.9.tgz#fd450c4ec10cdbb980e2928b7aa7a28484593fc8"
-  integrity sha512-gTXYh3M5wb7FRXQy+FErKFAv90BnlOuNn1QkCK2lREoPAjrQCO49+HVSrFoe5uakFAF5eenS75KbO2vQiLrTMQ==
+"@babel/core@^7.12.10":
+  version "7.12.10"
+  resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.12.10.tgz#b79a2e1b9f70ed3d84bbfb6d8c4ef825f606bccd"
+  integrity sha512-eTAlQKq65zHfkHZV0sIVODCPGVgoo1HdBlbSLi9CqOzuZanMv2ihzY+4paiKr1mH+XmYESMAmJ/dpZ68eN6d8w==
   dependencies:
     "@babel/code-frame" "^7.10.4"
-    "@babel/generator" "^7.12.5"
+    "@babel/generator" "^7.12.10"
     "@babel/helper-module-transforms" "^7.12.1"
     "@babel/helpers" "^7.12.5"
-    "@babel/parser" "^7.12.7"
+    "@babel/parser" "^7.12.10"
     "@babel/template" "^7.12.7"
-    "@babel/traverse" "^7.12.9"
-    "@babel/types" "^7.12.7"
+    "@babel/traverse" "^7.12.10"
+    "@babel/types" "^7.12.10"
     convert-source-map "^1.7.0"
     debug "^4.1.0"
     gensync "^1.0.0-beta.1"
     json5 "^2.1.2"
     lodash "^4.17.19"
-    resolve "^1.3.2"
     semver "^5.4.1"
     source-map "^0.5.0"
 
     jsesc "^2.5.1"
     source-map "^0.5.0"
 
+"@babel/generator@^7.12.10", "@babel/generator@^7.12.11":
+  version "7.12.11"
+  resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.12.11.tgz#98a7df7b8c358c9a37ab07a24056853016aba3af"
+  integrity sha512-Ggg6WPOJtSi8yYQvLVjG8F/TlpWDlKx0OpS4Kt+xMQPs5OaGYWy+v1A+1TvxI6sAMGZpKWWoAQ1DaeQbImlItA==
+  dependencies:
+    "@babel/types" "^7.12.11"
+    jsesc "^2.5.1"
+    source-map "^0.5.0"
+
 "@babel/generator@^7.12.5":
   version "7.12.5"
   resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.12.5.tgz#a2c50de5c8b6d708ab95be5e6053936c1884a4de"
     "@babel/template" "^7.10.4"
     "@babel/types" "^7.10.4"
 
+"@babel/helper-function-name@^7.12.11":
+  version "7.12.11"
+  resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.12.11.tgz#1fd7738aee5dcf53c3ecff24f1da9c511ec47b42"
+  integrity sha512-AtQKjtYNolKNi6nNNVLQ27CP6D9oFR6bq/HPYSizlzbp7uC1M59XJe8L+0uXjbIaZaUJF99ruHqVGiKXU/7ybA==
+  dependencies:
+    "@babel/helper-get-function-arity" "^7.12.10"
+    "@babel/template" "^7.12.7"
+    "@babel/types" "^7.12.11"
+
 "@babel/helper-get-function-arity@^7.10.4":
   version "7.10.4"
   resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.10.4.tgz#98c1cbea0e2332f33f9a4661b8ce1505b2c19ba2"
   dependencies:
     "@babel/types" "^7.10.4"
 
+"@babel/helper-get-function-arity@^7.12.10":
+  version "7.12.10"
+  resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.12.10.tgz#b158817a3165b5faa2047825dfa61970ddcc16cf"
+  integrity sha512-mm0n5BPjR06wh9mPQaDdXWDoll/j5UpCAPl1x8fS71GHm7HA6Ua2V4ylG1Ju8lvcTOietbPNNPaSilKj+pj+Ag==
+  dependencies:
+    "@babel/types" "^7.12.10"
+
 "@babel/helper-hoist-variables@^7.10.4":
   version "7.10.4"
   resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.10.4.tgz#d49b001d1d5a68ca5e6604dda01a6297f7c9381e"
   dependencies:
     "@babel/types" "^7.11.0"
 
+"@babel/helper-split-export-declaration@^7.12.11":
+  version "7.12.11"
+  resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.12.11.tgz#1b4cc424458643c47d37022223da33d76ea4603a"
+  integrity sha512-LsIVN8j48gHgwzfocYUSkO/hjYAOJqlpJEc7tGXcIm4cubjVUf8LGW6eWRyxEu7gA25q02p0rQUWoCI33HNS5g==
+  dependencies:
+    "@babel/types" "^7.12.11"
+
 "@babel/helper-validator-identifier@^7.10.4":
   version "7.10.4"
   resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz#a78c7a7251e01f616512d31b10adcf52ada5e0d2"
   integrity sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw==
 
+"@babel/helper-validator-identifier@^7.12.11":
+  version "7.12.11"
+  resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.12.11.tgz#c9a1f021917dcb5ccf0d4e453e399022981fc9ed"
+  integrity sha512-np/lG3uARFybkoHokJUmf1QfEvRVCPbmQeUQpKow5cQ3xWrV9i3rUHodKDJPQfTVX61qKi+UdYk8kik84n7XOw==
+
 "@babel/helper-validator-option@^7.12.1":
   version "7.12.1"
   resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.12.1.tgz#175567380c3e77d60ff98a54bb015fe78f2178d9"
   integrity sha512-YpJabsXlJVWP0USHjnC/AQDTLlZERbON577YUVO/wLpqyj6HAtVYnWaQaN0iUN+1/tWn3c+uKKXjRut5115Y2A==
 
+"@babel/helper-validator-option@^7.12.11":
+  version "7.12.11"
+  resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.12.11.tgz#d66cb8b7a3e7fe4c6962b32020a131ecf0847f4f"
+  integrity sha512-TBFCyj939mFSdeX7U7DDj32WtzYY7fDcalgq8v3fBZMNOJQNn7nOYzMaUCiPxPYfCup69mtIpqlKgMZLvQ8Xhw==
+
 "@babel/helper-wrap-function@^7.10.4":
   version "7.10.4"
   resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.10.4.tgz#8a6f701eab0ff39f765b5a1cfef409990e624b87"
   resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.12.3.tgz#a305415ebe7a6c7023b40b5122a0662d928334cd"
   integrity sha512-kFsOS0IbsuhO5ojF8Hc8z/8vEIOkylVBrjiZUbLTE3XFe0Qi+uu6HjzQixkFaqr0ZPAMZcBVxEwmsnsLPZ2Xsw==
 
+"@babel/parser@^7.12.10", "@babel/parser@^7.12.11":
+  version "7.12.11"
+  resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.12.11.tgz#9ce3595bcd74bc5c466905e86c535b8b25011e79"
+  integrity sha512-N3UxG+uuF4CMYoNj8AhnbAcJF0PiuJ9KHuy1lQmkYsxTer/MAH9UBNHsBoAX/4s6NvlDD047No8mYVGGzLL4hg==
+
 "@babel/parser@^7.12.7":
   version "7.12.7"
   resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.12.7.tgz#fee7b39fe809d0e73e5b25eecaf5780ef3d73056"
   dependencies:
     "@babel/helper-plugin-utils" "^7.10.4"
 
-"@babel/plugin-transform-block-scoping@^7.12.1":
-  version "7.12.1"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.12.1.tgz#f0ee727874b42a208a48a586b84c3d222c2bbef1"
-  integrity sha512-zJyAC9sZdE60r1nVQHblcfCj29Dh2Y0DOvlMkcqSo0ckqjiCwNiUezUKw+RjOCwGfpLRwnAeQ2XlLpsnGkvv9w==
+"@babel/plugin-transform-block-scoping@^7.12.11":
+  version "7.12.12"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.12.12.tgz#d93a567a152c22aea3b1929bb118d1d0a175cdca"
+  integrity sha512-VOEPQ/ExOVqbukuP7BYJtI5ZxxsmegTwzZ04j1aF0dkSypGo9XpDHuOrABsJu+ie+penpSJheDJ11x1BEZNiyQ==
   dependencies:
     "@babel/helper-plugin-utils" "^7.10.4"
 
   dependencies:
     "@babel/helper-plugin-utils" "^7.10.4"
 
-"@babel/plugin-transform-runtime@^7.12.1":
-  version "7.12.1"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.12.1.tgz#04b792057eb460389ff6a4198e377614ea1e7ba5"
-  integrity sha512-Ac/H6G9FEIkS2tXsZjL4RAdS3L3WHxci0usAnz7laPWUmFiGtj7tIASChqKZMHTSQTQY6xDbOq+V1/vIq3QrWg==
+"@babel/plugin-transform-runtime@^7.12.10":
+  version "7.12.10"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.12.10.tgz#af0fded4e846c4b37078e8e5d06deac6cd848562"
+  integrity sha512-xOrUfzPxw7+WDm9igMgQCbO3cJKymX7dFdsgRr1eu9n3KjjyU4pptIXbXPseQDquw+W+RuJEJMHKHNsPNNm3CA==
   dependencies:
-    "@babel/helper-module-imports" "^7.12.1"
+    "@babel/helper-module-imports" "^7.12.5"
     "@babel/helper-plugin-utils" "^7.10.4"
-    resolve "^1.8.1"
     semver "^5.5.1"
 
 "@babel/plugin-transform-shorthand-properties@^7.12.1":
   dependencies:
     "@babel/helper-plugin-utils" "^7.10.4"
 
-"@babel/plugin-transform-typeof-symbol@^7.12.1":
-  version "7.12.1"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.12.1.tgz#9ca6be343d42512fbc2e68236a82ae64bc7af78a"
-  integrity sha512-EPGgpGy+O5Kg5pJFNDKuxt9RdmTgj5sgrus2XVeMp/ZIbOESadgILUbm50SNpghOh3/6yrbsH+NB5+WJTmsA7Q==
+"@babel/plugin-transform-typeof-symbol@^7.12.10":
+  version "7.12.10"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.12.10.tgz#de01c4c8f96580bd00f183072b0d0ecdcf0dec4b"
+  integrity sha512-JQ6H8Rnsogh//ijxspCjc21YPd3VLVoYtAwv3zQmqAt8YGYUtdo5usNhdl4b9/Vir2kPFZl6n1h0PfUz4hJhaA==
   dependencies:
     "@babel/helper-plugin-utils" "^7.10.4"
 
     "@babel/helper-create-regexp-features-plugin" "^7.12.1"
     "@babel/helper-plugin-utils" "^7.10.4"
 
-"@babel/preset-env@7.12.7":
-  version "7.12.7"
-  resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.12.7.tgz#54ea21dbe92caf6f10cb1a0a576adc4ebf094b55"
-  integrity sha512-OnNdfAr1FUQg7ksb7bmbKoby4qFOHw6DKWWUNB9KqnnCldxhxJlP+21dpyaWFmf2h0rTbOkXJtAGevY3XW1eew==
+"@babel/preset-env@7.12.11":
+  version "7.12.11"
+  resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.12.11.tgz#55d5f7981487365c93dbbc84507b1c7215e857f9"
+  integrity sha512-j8Tb+KKIXKYlDBQyIOy4BLxzv1NUOwlHfZ74rvW+Z0Gp4/cI2IMDPBWAgWceGcE7aep9oL/0K9mlzlMGxA8yNw==
   dependencies:
     "@babel/compat-data" "^7.12.7"
     "@babel/helper-compilation-targets" "^7.12.5"
     "@babel/helper-module-imports" "^7.12.5"
     "@babel/helper-plugin-utils" "^7.10.4"
-    "@babel/helper-validator-option" "^7.12.1"
+    "@babel/helper-validator-option" "^7.12.11"
     "@babel/plugin-proposal-async-generator-functions" "^7.12.1"
     "@babel/plugin-proposal-class-properties" "^7.12.1"
     "@babel/plugin-proposal-dynamic-import" "^7.12.1"
     "@babel/plugin-transform-arrow-functions" "^7.12.1"
     "@babel/plugin-transform-async-to-generator" "^7.12.1"
     "@babel/plugin-transform-block-scoped-functions" "^7.12.1"
-    "@babel/plugin-transform-block-scoping" "^7.12.1"
+    "@babel/plugin-transform-block-scoping" "^7.12.11"
     "@babel/plugin-transform-classes" "^7.12.1"
     "@babel/plugin-transform-computed-properties" "^7.12.1"
     "@babel/plugin-transform-destructuring" "^7.12.1"
     "@babel/plugin-transform-spread" "^7.12.1"
     "@babel/plugin-transform-sticky-regex" "^7.12.7"
     "@babel/plugin-transform-template-literals" "^7.12.1"
-    "@babel/plugin-transform-typeof-symbol" "^7.12.1"
+    "@babel/plugin-transform-typeof-symbol" "^7.12.10"
     "@babel/plugin-transform-unicode-escapes" "^7.12.1"
     "@babel/plugin-transform-unicode-regex" "^7.12.1"
     "@babel/preset-modules" "^0.1.3"
-    "@babel/types" "^7.12.7"
-    core-js-compat "^3.7.0"
+    "@babel/types" "^7.12.11"
+    core-js-compat "^3.8.0"
     semver "^5.5.0"
 
 "@babel/preset-modules@^0.1.3":
   dependencies:
     regenerator-runtime "^0.13.4"
 
+"@babel/runtime@^7.11.2", "@babel/runtime@^7.12.5":
+  version "7.12.5"
+  resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.12.5.tgz#410e7e487441e1b360c29be715d870d9b985882e"
+  integrity sha512-plcc+hbExy3McchJCEQG3knOsuh3HH+Prx1P6cLIkET/0dLuQDEnrT+s27Axgc9bqfsmNUNHfscgMUdBpC9xfg==
+  dependencies:
+    regenerator-runtime "^0.13.4"
+
 "@babel/runtime@^7.12.0":
   version "7.12.1"
   resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.12.1.tgz#b4116a6b6711d010b2dad3b7b6e43bf1b9954740"
   dependencies:
     regenerator-runtime "^0.13.4"
 
-"@babel/runtime@^7.12.5":
-  version "7.12.5"
-  resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.12.5.tgz#410e7e487441e1b360c29be715d870d9b985882e"
-  integrity sha512-plcc+hbExy3McchJCEQG3knOsuh3HH+Prx1P6cLIkET/0dLuQDEnrT+s27Axgc9bqfsmNUNHfscgMUdBpC9xfg==
-  dependencies:
-    regenerator-runtime "^0.13.4"
-
 "@babel/template@^7.10.4":
   version "7.10.4"
   resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.10.4.tgz#3251996c4200ebc71d1a8fc405fba940f36ba278"
     globals "^11.1.0"
     lodash "^4.17.19"
 
-"@babel/traverse@^7.12.5", "@babel/traverse@^7.12.9":
+"@babel/traverse@^7.12.10":
+  version "7.12.12"
+  resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.12.12.tgz#d0cd87892704edd8da002d674bc811ce64743376"
+  integrity sha512-s88i0X0lPy45RrLM8b9mz8RPH5FqO9G9p7ti59cToE44xFm1Q+Pjh5Gq4SXBbtb88X7Uy7pexeqRIQDDMNkL0w==
+  dependencies:
+    "@babel/code-frame" "^7.12.11"
+    "@babel/generator" "^7.12.11"
+    "@babel/helper-function-name" "^7.12.11"
+    "@babel/helper-split-export-declaration" "^7.12.11"
+    "@babel/parser" "^7.12.11"
+    "@babel/types" "^7.12.12"
+    debug "^4.1.0"
+    globals "^11.1.0"
+    lodash "^4.17.19"
+
+"@babel/traverse@^7.12.5":
   version "7.12.9"
   resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.12.9.tgz#fad26c972eabbc11350e0b695978de6cc8e8596f"
   integrity sha512-iX9ajqnLdoU1s1nHt36JDI9KG4k+vmI8WgjK5d+aDTwQbL2fUnzedNedssA645Ede3PM2ma1n8Q4h2ohwXgMXw==
     lodash "^4.17.19"
     to-fast-properties "^2.0.0"
 
+"@babel/types@^7.12.10", "@babel/types@^7.12.11", "@babel/types@^7.12.12":
+  version "7.12.12"
+  resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.12.12.tgz#4608a6ec313abbd87afa55004d373ad04a96c299"
+  integrity sha512-lnIX7piTxOH22xE7fDXDbSHg9MM1/6ORnafpJmov5rs0kX5g4BZxeXNJLXsMRiO0U5Rb8/FvMS6xlTnTHvxonQ==
+  dependencies:
+    "@babel/helper-validator-identifier" "^7.12.11"
+    lodash "^4.17.19"
+    to-fast-properties "^2.0.0"
+
 "@babel/types@^7.12.5", "@babel/types@^7.12.7":
   version "7.12.7"
   resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.12.7.tgz#6039ff1e242640a29452c9ae572162ec9a8f5d13"
     "@nodelib/fs.scandir" "2.1.3"
     fastq "^1.6.0"
 
-"@npmcli/move-file@^1.0.1":
-  version "1.0.1"
-  resolved "https://registry.yarnpkg.com/@npmcli/move-file/-/move-file-1.0.1.tgz#de103070dac0f48ce49cf6693c23af59c0f70464"
-  integrity sha512-Uv6h1sT+0DrblvIrolFtbvM1FgWm+/sy4B3pvLp67Zys+thcukzS5ekn7HsZFGpWP4Q3fYJCljbWQE/XivMRLw==
-  dependencies:
-    mkdirp "^1.0.4"
-
 "@popperjs/core@^2.4.4":
   version "2.4.4"
   resolved "https://registry.yarnpkg.com/@popperjs/core/-/core-2.4.4.tgz#11d5db19bd178936ec89cd84519c4de439574398"
   resolved "https://registry.yarnpkg.com/@types/node/-/node-14.6.0.tgz#7d4411bf5157339337d7cff864d9ff45f177b499"
   integrity sha512-mikldZQitV94akrc4sCcSjtJfsTKt4p+e/s0AGscVA6XArQ9kFclP+ZiYUMnq987rc6QlYxXv/EivqlfSLxpKA==
 
-"@types/node@^14.14.11":
-  version "14.14.11"
-  resolved "https://registry.yarnpkg.com/@types/node/-/node-14.14.11.tgz#fc25a4248a5e8d0837019b1d170146d07334abe0"
-  integrity sha512-BJ97wAUuU3NUiUCp44xzUFquQEvnk1wu7q4CMEUYKJWjdkr0YWYDsm4RFtAvxYsNjLsKcrFt6RvK8r+mnzMbEQ==
+"@types/node@^14.14.16":
+  version "14.14.16"
+  resolved "https://registry.yarnpkg.com/@types/node/-/node-14.14.16.tgz#3cc351f8d48101deadfed4c9e4f116048d437b4b"
+  integrity sha512-naXYePhweTi+BMv11TgioE2/FXU4fSl29HAH1ffxVciNsH3rYXjNP2yM8wqmSm7jS20gM8TIklKiTen+1iVncw==
 
 "@types/normalize-package-data@^2.4.0":
   version "2.4.0"
     "@types/webpack-sources" "*"
     source-map "^0.6.0"
 
-"@typescript-eslint/eslint-plugin@4.5.0":
-  version "4.5.0"
-  resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.5.0.tgz#4ff9c1d8535ae832e239f0ef6d7210592d9b0b07"
-  integrity sha512-mjb/gwNcmDKNt+6mb7Aj/TjKzIJjOPcoCJpjBQC9ZnTRnBt1p4q5dJSSmIqAtsZ/Pff5N+hJlbiPc5bl6QN4OQ==
+"@typescript-eslint/eslint-plugin@4.11.0":
+  version "4.11.0"
+  resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.11.0.tgz#bc6c1e4175c0cf42083da4314f7931ad12f731cc"
+  integrity sha512-x4arJMXBxyD6aBXLm3W7mSDZRiABzy+2PCLJbL7OPqlp53VXhaA1HKK7R2rTee5OlRhnUgnp8lZyVIqjnyPT6g==
   dependencies:
-    "@typescript-eslint/experimental-utils" "4.5.0"
-    "@typescript-eslint/scope-manager" "4.5.0"
+    "@typescript-eslint/experimental-utils" "4.11.0"
+    "@typescript-eslint/scope-manager" "4.11.0"
     debug "^4.1.1"
     functional-red-black-tree "^1.0.1"
     regexpp "^3.0.0"
     semver "^7.3.2"
     tsutils "^3.17.1"
 
-"@typescript-eslint/experimental-utils@4.5.0":
-  version "4.5.0"
-  resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-4.5.0.tgz#547fe1158609143ce60645383aa1d6f83ada28df"
-  integrity sha512-bW9IpSAKYvkqDGRZzayBXIgPsj2xmmVHLJ+flGSoN0fF98pGoKFhbunIol0VF2Crka7z984EEhFi623Rl7e6gg==
+"@typescript-eslint/experimental-utils@4.11.0":
+  version "4.11.0"
+  resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-4.11.0.tgz#d1a47cc6cfe1c080ce4ead79267574b9881a1565"
+  integrity sha512-1VC6mSbYwl1FguKt8OgPs8xxaJgtqFpjY/UzUYDBKq4pfQ5lBvN2WVeqYkzf7evW42axUHYl2jm9tNyFsb8oLg==
   dependencies:
     "@types/json-schema" "^7.0.3"
-    "@typescript-eslint/scope-manager" "4.5.0"
-    "@typescript-eslint/types" "4.5.0"
-    "@typescript-eslint/typescript-estree" "4.5.0"
+    "@typescript-eslint/scope-manager" "4.11.0"
+    "@typescript-eslint/types" "4.11.0"
+    "@typescript-eslint/typescript-estree" "4.11.0"
     eslint-scope "^5.0.0"
     eslint-utils "^2.0.0"
 
     eslint-scope "^5.0.0"
     eslint-utils "^2.0.0"
 
-"@typescript-eslint/parser@4.5.0":
-  version "4.5.0"
-  resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-4.5.0.tgz#b2d659f25eec0041c7bc5660b91db1eefe8d7122"
-  integrity sha512-xb+gmyhQcnDWe+5+xxaQk5iCw6KqXd8VQxGiTeELTMoYeRjpocZYYRP1gFVM2C8Yl0SpUvLa1lhprwqZ00w3Iw==
-  dependencies:
-    "@typescript-eslint/scope-manager" "4.5.0"
-    "@typescript-eslint/types" "4.5.0"
-    "@typescript-eslint/typescript-estree" "4.5.0"
-    debug "^4.1.1"
-
-"@typescript-eslint/parser@^4.9.1":
-  version "4.9.1"
-  resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-4.9.1.tgz#2d74c4db5dd5117379a9659081a4d1ec02629055"
-  integrity sha512-Gv2VpqiomvQ2v4UL+dXlQcZ8zCX4eTkoIW+1aGVWT6yTO+6jbxsw7yQl2z2pPl/4B9qa5JXeIbhJpONKjXIy3g==
+"@typescript-eslint/parser@4.11.0", "@typescript-eslint/parser@^4.11.0":
+  version "4.11.0"
+  resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-4.11.0.tgz#1dd3d7e42708c10ce9f3aa64c63c0ab99868b4e2"
+  integrity sha512-NBTtKCC7ZtuxEV5CrHUO4Pg2s784pvavc3cnz6V+oJvVbK4tH9135f/RBP6eUA2KHiFKAollSrgSctQGmHbqJQ==
   dependencies:
-    "@typescript-eslint/scope-manager" "4.9.1"
-    "@typescript-eslint/types" "4.9.1"
-    "@typescript-eslint/typescript-estree" "4.9.1"
+    "@typescript-eslint/scope-manager" "4.11.0"
+    "@typescript-eslint/types" "4.11.0"
+    "@typescript-eslint/typescript-estree" "4.11.0"
     debug "^4.1.1"
 
 "@typescript-eslint/scope-manager@4.1.0":
     "@typescript-eslint/types" "4.1.0"
     "@typescript-eslint/visitor-keys" "4.1.0"
 
-"@typescript-eslint/scope-manager@4.5.0":
-  version "4.5.0"
-  resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-4.5.0.tgz#8dfd53c3256d4357e7d66c2fc8956835f4d239be"
-  integrity sha512-C0cEO0cTMPJ/w4RA/KVe4LFFkkSh9VHoFzKmyaaDWAnPYIEzVCtJ+Un8GZoJhcvq+mPFXEsXa01lcZDHDG6Www==
+"@typescript-eslint/scope-manager@4.11.0":
+  version "4.11.0"
+  resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-4.11.0.tgz#2d906537db8a3a946721699e4fc0833810490254"
+  integrity sha512-6VSTm/4vC2dHM3ySDW9Kl48en+yLNfVV6LECU8jodBHQOhO8adAVizaZ1fV0QGZnLQjQ/y0aBj5/KXPp2hBTjA==
   dependencies:
-    "@typescript-eslint/types" "4.5.0"
-    "@typescript-eslint/visitor-keys" "4.5.0"
-
-"@typescript-eslint/scope-manager@4.9.1":
-  version "4.9.1"
-  resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-4.9.1.tgz#cc2fde310b3f3deafe8436a924e784eaab265103"
-  integrity sha512-sa4L9yUfD/1sg9Kl8OxPxvpUcqxKXRjBeZxBuZSSV1v13hjfEJkn84n0An2hN8oLQ1PmEl2uA6FkI07idXeFgQ==
-  dependencies:
-    "@typescript-eslint/types" "4.9.1"
-    "@typescript-eslint/visitor-keys" "4.9.1"
+    "@typescript-eslint/types" "4.11.0"
+    "@typescript-eslint/visitor-keys" "4.11.0"
 
 "@typescript-eslint/types@4.1.0":
   version "4.1.0"
   resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-4.1.0.tgz#edbd3fec346f34e13ce7aa176b03b497a32c496a"
   integrity sha512-rkBqWsO7m01XckP9R2YHVN8mySOKKY2cophGM8K5uDK89ArCgahItQYdbg/3n8xMxzu2elss+an1TphlUpDuJw==
 
-"@typescript-eslint/types@4.5.0":
-  version "4.5.0"
-  resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-4.5.0.tgz#98256e07bad1c8d15d0c9627ebec82fd971bb3c3"
-  integrity sha512-n2uQoXnyWNk0Les9MtF0gCK3JiWd987JQi97dMSxBOzVoLZXCNtxFckVqt1h8xuI1ix01t+iMY4h4rFMj/303g==
-
-"@typescript-eslint/types@4.9.1":
-  version "4.9.1"
-  resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-4.9.1.tgz#a1a7dd80e4e5ac2c593bc458d75dd1edaf77faa2"
-  integrity sha512-fjkT+tXR13ks6Le7JiEdagnwEFc49IkOyys7ueWQ4O8k4quKPwPJudrwlVOJCUQhXo45PrfIvIarcrEjFTNwUA==
+"@typescript-eslint/types@4.11.0":
+  version "4.11.0"
+  resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-4.11.0.tgz#86cf95e7eac4ccfd183f9fcf1480cece7caf4ca4"
+  integrity sha512-XXOdt/NPX++txOQHM1kUMgJUS43KSlXGdR/aDyEwuAEETwuPt02Nc7v+s57PzuSqMbNLclblQdv3YcWOdXhQ7g==
 
 "@typescript-eslint/typescript-estree@4.1.0":
   version "4.1.0"
     semver "^7.3.2"
     tsutils "^3.17.1"
 
-"@typescript-eslint/typescript-estree@4.5.0":
-  version "4.5.0"
-  resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-4.5.0.tgz#d50cf91ae3a89878401111031eb6fb6d03554f64"
-  integrity sha512-gN1mffq3zwRAjlYWzb5DanarOPdajQwx5MEWkWCk0XvqC8JpafDTeioDoow2L4CA/RkYZu7xEsGZRhqrTsAG8w==
-  dependencies:
-    "@typescript-eslint/types" "4.5.0"
-    "@typescript-eslint/visitor-keys" "4.5.0"
-    debug "^4.1.1"
-    globby "^11.0.1"
-    is-glob "^4.0.1"
-    lodash "^4.17.15"
-    semver "^7.3.2"
-    tsutils "^3.17.1"
-
-"@typescript-eslint/typescript-estree@4.9.1":
-  version "4.9.1"
-  resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-4.9.1.tgz#6e5b86ff5a5f66809e1f347469fadeec69ac50bf"
-  integrity sha512-bzP8vqwX6Vgmvs81bPtCkLtM/Skh36NE6unu6tsDeU/ZFoYthlTXbBmpIrvosgiDKlWTfb2ZpPELHH89aQjeQw==
+"@typescript-eslint/typescript-estree@4.11.0":
+  version "4.11.0"
+  resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-4.11.0.tgz#1144d145841e5987d61c4c845442a24b24165a4b"
+  integrity sha512-eA6sT5dE5RHAFhtcC+b5WDlUIGwnO9b0yrfGa1mIOIAjqwSQCpXbLiFmKTdRbQN/xH2EZkGqqLDrKUuYOZ0+Hg==
   dependencies:
-    "@typescript-eslint/types" "4.9.1"
-    "@typescript-eslint/visitor-keys" "4.9.1"
+    "@typescript-eslint/types" "4.11.0"
+    "@typescript-eslint/visitor-keys" "4.11.0"
     debug "^4.1.1"
     globby "^11.0.1"
     is-glob "^4.0.1"
     "@typescript-eslint/types" "4.1.0"
     eslint-visitor-keys "^2.0.0"
 
-"@typescript-eslint/visitor-keys@4.5.0":
-  version "4.5.0"
-  resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-4.5.0.tgz#b59f26213ac597efe87f6b13cf2aabee70542af0"
-  integrity sha512-UHq4FSa55NDZqscRU//O5ROFhHa9Hqn9KWTEvJGTArtTQp5GKv9Zqf6d/Q3YXXcFv4woyBml7fJQlQ+OuqRcHA==
+"@typescript-eslint/visitor-keys@4.11.0":
+  version "4.11.0"
+  resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-4.11.0.tgz#906669a50f06aa744378bb84c7d5c4fdbc5b7d51"
+  integrity sha512-tRYKyY0i7cMk6v4UIOCjl1LhuepC/pc6adQqJk4Is3YcC6k46HvsV9Wl7vQoLbm9qADgeujiT7KdLrylvFIQ+A==
   dependencies:
-    "@typescript-eslint/types" "4.5.0"
+    "@typescript-eslint/types" "4.11.0"
     eslint-visitor-keys "^2.0.0"
 
-"@typescript-eslint/visitor-keys@4.9.1":
-  version "4.9.1"
-  resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-4.9.1.tgz#d76374a58c4ead9e92b454d186fea63487b25ae1"
-  integrity sha512-9gspzc6UqLQHd7lXQS7oWs+hrYggspv/rk6zzEMhCbYwPE/sF7oxo7GAjkS35Tdlt7wguIG+ViWCPtVZHz/ybQ==
+"@webassemblyjs/ast@1.9.1":
+  version "1.9.1"
+  resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.9.1.tgz#76c6937716d68bf1484c15139f5ed30b9abc8bb4"
+  integrity sha512-uMu1nCWn2Wxyy126LlGqRVlhdTOsO/bsBRI4dNq3+6SiSuRKRQX6ejjKgh82LoGAPSq72lDUiQ4FWVaf0PecYw==
   dependencies:
-    "@typescript-eslint/types" "4.9.1"
-    eslint-visitor-keys "^2.0.0"
+    "@webassemblyjs/helper-module-context" "1.9.1"
+    "@webassemblyjs/helper-wasm-bytecode" "1.9.1"
+    "@webassemblyjs/wast-parser" "1.9.1"
+
+"@webassemblyjs/floating-point-hex-parser@1.9.1":
+  version "1.9.1"
+  resolved "https://registry.yarnpkg.com/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.9.1.tgz#9eb0ff90a1cdeef51f36ba533ed9f06b5cdadd09"
+  integrity sha512-5VEKu024RySmLKTTBl9q1eO/2K5jk9ZS+2HXDBLA9s9p5IjkaXxWiDb/+b7wSQp6FRdLaH1IVGIfOex58Na2pg==
+
+"@webassemblyjs/helper-api-error@1.9.1":
+  version "1.9.1"
+  resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-api-error/-/helper-api-error-1.9.1.tgz#ad89015c4246cd7f5ed0556700237f8b9c2c752f"
+  integrity sha512-y1lGmfm38djrScwpeL37rRR9f1D6sM8RhMpvM7CYLzOlHVboouZokXK/G88BpzW0NQBSvCCOnW5BFhten4FPfA==
 
-"@webassemblyjs/ast@1.9.0":
-  version "1.9.0"
-  resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.9.0.tgz#bd850604b4042459a5a41cd7d338cbed695ed964"
-  integrity sha512-C6wW5L+b7ogSDVqymbkkvuW9kruN//YisMED04xzeBBqjHa2FYnmvOlS6Xj68xWQRgWvI9cIglsjFowH/RJyEA==
-  dependencies:
-    "@webassemblyjs/helper-module-context" "1.9.0"
-    "@webassemblyjs/helper-wasm-bytecode" "1.9.0"
-    "@webassemblyjs/wast-parser" "1.9.0"
-
-"@webassemblyjs/floating-point-hex-parser@1.9.0":
-  version "1.9.0"
-  resolved "https://registry.yarnpkg.com/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.9.0.tgz#3c3d3b271bddfc84deb00f71344438311d52ffb4"
-  integrity sha512-TG5qcFsS8QB4g4MhrxK5TqfdNe7Ey/7YL/xN+36rRjl/BlGE/NcBvJcqsRgCP6Z92mRE+7N50pRIi8SmKUbcQA==
-
-"@webassemblyjs/helper-api-error@1.9.0":
-  version "1.9.0"
-  resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-api-error/-/helper-api-error-1.9.0.tgz#203f676e333b96c9da2eeab3ccef33c45928b6a2"
-  integrity sha512-NcMLjoFMXpsASZFxJ5h2HZRcEhDkvnNFOAKneP5RbKRzaWJN36NC4jqQHKwStIhGXu5mUWlUUk7ygdtrO8lbmw==
-
-"@webassemblyjs/helper-buffer@1.9.0":
-  version "1.9.0"
-  resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-buffer/-/helper-buffer-1.9.0.tgz#a1442d269c5feb23fcbc9ef759dac3547f29de00"
-  integrity sha512-qZol43oqhq6yBPx7YM3m9Bv7WMV9Eevj6kMi6InKOuZxhw+q9hOkvq5e/PpKSiLfyetpaBnogSbNCfBwyB00CA==
-
-"@webassemblyjs/helper-code-frame@1.9.0":
-  version "1.9.0"
-  resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-code-frame/-/helper-code-frame-1.9.0.tgz#647f8892cd2043a82ac0c8c5e75c36f1d9159f27"
-  integrity sha512-ERCYdJBkD9Vu4vtjUYe8LZruWuNIToYq/ME22igL+2vj2dQ2OOujIZr3MEFvfEaqKoVqpsFKAGsRdBSBjrIvZA==
-  dependencies:
-    "@webassemblyjs/wast-printer" "1.9.0"
-
-"@webassemblyjs/helper-fsm@1.9.0":
-  version "1.9.0"
-  resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-fsm/-/helper-fsm-1.9.0.tgz#c05256b71244214671f4b08ec108ad63b70eddb8"
-  integrity sha512-OPRowhGbshCb5PxJ8LocpdX9Kl0uB4XsAjl6jH/dWKlk/mzsANvhwbiULsaiqT5GZGT9qinTICdj6PLuM5gslw==
-
-"@webassemblyjs/helper-module-context@1.9.0":
-  version "1.9.0"
-  resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-module-context/-/helper-module-context-1.9.0.tgz#25d8884b76839871a08a6c6f806c3979ef712f07"
-  integrity sha512-MJCW8iGC08tMk2enck1aPW+BE5Cw8/7ph/VGZxwyvGbJwjktKkDK7vy7gAmMDx88D7mhDTCNKAW5tED+gZ0W8g==
-  dependencies:
-    "@webassemblyjs/ast" "1.9.0"
-
-"@webassemblyjs/helper-wasm-bytecode@1.9.0":
-  version "1.9.0"
-  resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.9.0.tgz#4fed8beac9b8c14f8c58b70d124d549dd1fe5790"
-  integrity sha512-R7FStIzyNcd7xKxCZH5lE0Bqy+hGTwS3LJjuv1ZVxd9O7eHCedSdrId/hMOd20I+v8wDXEn+bjfKDLzTepoaUw==
-
-"@webassemblyjs/helper-wasm-section@1.9.0":
-  version "1.9.0"
-  resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.9.0.tgz#5a4138d5a6292ba18b04c5ae49717e4167965346"
-  integrity sha512-XnMB8l3ek4tvrKUUku+IVaXNHz2YsJyOOmz+MMkZvh8h1uSJpSen6vYnw3IoQ7WwEuAhL8Efjms1ZWjqh2agvw==
-  dependencies:
-    "@webassemblyjs/ast" "1.9.0"
-    "@webassemblyjs/helper-buffer" "1.9.0"
-    "@webassemblyjs/helper-wasm-bytecode" "1.9.0"
-    "@webassemblyjs/wasm-gen" "1.9.0"
-
-"@webassemblyjs/ieee754@1.9.0":
-  version "1.9.0"
-  resolved "https://registry.yarnpkg.com/@webassemblyjs/ieee754/-/ieee754-1.9.0.tgz#15c7a0fbaae83fb26143bbacf6d6df1702ad39e4"
-  integrity sha512-dcX8JuYU/gvymzIHc9DgxTzUUTLexWwt8uCTWP3otys596io0L5aW02Gb1RjYpx2+0Jus1h4ZFqjla7umFniTg==
+"@webassemblyjs/helper-buffer@1.9.1":
+  version "1.9.1"
+  resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-buffer/-/helper-buffer-1.9.1.tgz#186e67ac25f9546ea7939759413987f157524133"
+  integrity sha512-uS6VSgieHbk/m4GSkMU5cqe/5TekdCzQso4revCIEQ3vpGZgqSSExi4jWpTWwDpAHOIAb1Jfrs0gUB9AA4n71w==
+
+"@webassemblyjs/helper-code-frame@1.9.1":
+  version "1.9.1"
+  resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-code-frame/-/helper-code-frame-1.9.1.tgz#aab177b7cc87a318a8f8664ad68e2c3828ebc42b"
+  integrity sha512-ZQ2ZT6Evk4DPIfD+92AraGYaFIqGm4U20e7FpXwl7WUo2Pn1mZ1v8VGH8i+Y++IQpxPbQo/UyG0Khs7eInskzA==
+  dependencies:
+    "@webassemblyjs/wast-printer" "1.9.1"
+
+"@webassemblyjs/helper-fsm@1.9.1":
+  version "1.9.1"
+  resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-fsm/-/helper-fsm-1.9.1.tgz#527e91628e84d13d3573884b3dc4c53a81dcb911"
+  integrity sha512-J32HGpveEqqcKFS0YbgicB0zAlpfIxJa5MjxDxhu3i5ltPcVfY5EPvKQ1suRguFPehxiUs+/hfkwPEXom/l0lw==
+
+"@webassemblyjs/helper-module-context@1.9.1":
+  version "1.9.1"
+  resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-module-context/-/helper-module-context-1.9.1.tgz#778670b3d471f7cf093d1e7c0dde431b54310e16"
+  integrity sha512-IEH2cMmEQKt7fqelLWB5e/cMdZXf2rST1JIrzWmf4XBt3QTxGdnnLvV4DYoN8pJjOx0VYXsWg+yF16MmJtolZg==
+  dependencies:
+    "@webassemblyjs/ast" "1.9.1"
+
+"@webassemblyjs/helper-wasm-bytecode@1.9.1":
+  version "1.9.1"
+  resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.9.1.tgz#563f59bcf409ccf469edde168b9426961ffbf6df"
+  integrity sha512-i2rGTBqFUcSXxyjt2K4vm/3kkHwyzG6o427iCjcIKjOqpWH8SEem+xe82jUk1iydJO250/CvE5o7hzNAMZf0dQ==
+
+"@webassemblyjs/helper-wasm-section@1.9.1":
+  version "1.9.1"
+  resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.9.1.tgz#f7988f94c12b01b99a16120cb01dc099b00e4798"
+  integrity sha512-FetqzjtXZr2d57IECK+aId3D0IcGweeM0CbAnJHkYJkcRTHP+YcMb7Wmc0j21h5UWBpwYGb9dSkK/93SRCTrGg==
+  dependencies:
+    "@webassemblyjs/ast" "1.9.1"
+    "@webassemblyjs/helper-buffer" "1.9.1"
+    "@webassemblyjs/helper-wasm-bytecode" "1.9.1"
+    "@webassemblyjs/wasm-gen" "1.9.1"
+
+"@webassemblyjs/ieee754@1.9.1":
+  version "1.9.1"
+  resolved "https://registry.yarnpkg.com/@webassemblyjs/ieee754/-/ieee754-1.9.1.tgz#3b715871ca7d75784717cf9ceca9d7b81374b8af"
+  integrity sha512-EvTG9M78zP1MmkBpUjGQHZc26DzPGZSLIPxYHCjQsBMo60Qy2W34qf8z0exRDtxBbRIoiKa5dFyWer/7r1aaSQ==
   dependencies:
     "@xtuc/ieee754" "^1.2.0"
 
-"@webassemblyjs/leb128@1.9.0":
-  version "1.9.0"
-  resolved "https://registry.yarnpkg.com/@webassemblyjs/leb128/-/leb128-1.9.0.tgz#f19ca0b76a6dc55623a09cffa769e838fa1e1c95"
-  integrity sha512-ENVzM5VwV1ojs9jam6vPys97B/S65YQtv/aanqnU7D8aSoHFX8GyhGg0CMfyKNIHBuAVjy3tlzd5QMMINa7wpw==
+"@webassemblyjs/leb128@1.9.1":
+  version "1.9.1"
+  resolved "https://registry.yarnpkg.com/@webassemblyjs/leb128/-/leb128-1.9.1.tgz#b2ecaa39f9e8277cc9c707c1ca8b2aa7b27d0b72"
+  integrity sha512-Oc04ub0vFfLnF+2/+ki3AE+anmW4sv9uNBqb+79fgTaPv6xJsOT0dhphNfL3FrME84CbX/D1T9XT8tjFo0IIiw==
   dependencies:
     "@xtuc/long" "4.2.2"
 
-"@webassemblyjs/utf8@1.9.0":
-  version "1.9.0"
-  resolved "https://registry.yarnpkg.com/@webassemblyjs/utf8/-/utf8-1.9.0.tgz#04d33b636f78e6a6813227e82402f7637b6229ab"
-  integrity sha512-GZbQlWtopBTP0u7cHrEx+73yZKrQoBMpwkGEIqlacljhXCkVM1kMQge/Mf+csMJAjEdSwhOyLAS0AoR3AG5P8w==
-
-"@webassemblyjs/wasm-edit@1.9.0":
-  version "1.9.0"
-  resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-edit/-/wasm-edit-1.9.0.tgz#3fe6d79d3f0f922183aa86002c42dd256cfee9cf"
-  integrity sha512-FgHzBm80uwz5M8WKnMTn6j/sVbqilPdQXTWraSjBwFXSYGirpkSWE2R9Qvz9tNiTKQvoKILpCuTjBKzOIm0nxw==
-  dependencies:
-    "@webassemblyjs/ast" "1.9.0"
-    "@webassemblyjs/helper-buffer" "1.9.0"
-    "@webassemblyjs/helper-wasm-bytecode" "1.9.0"
-    "@webassemblyjs/helper-wasm-section" "1.9.0"
-    "@webassemblyjs/wasm-gen" "1.9.0"
-    "@webassemblyjs/wasm-opt" "1.9.0"
-    "@webassemblyjs/wasm-parser" "1.9.0"
-    "@webassemblyjs/wast-printer" "1.9.0"
-
-"@webassemblyjs/wasm-gen@1.9.0":
-  version "1.9.0"
-  resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-gen/-/wasm-gen-1.9.0.tgz#50bc70ec68ded8e2763b01a1418bf43491a7a49c"
-  integrity sha512-cPE3o44YzOOHvlsb4+E9qSqjc9Qf9Na1OO/BHFy4OI91XDE14MjFN4lTMezzaIWdPqHnsTodGGNP+iRSYfGkjA==
-  dependencies:
-    "@webassemblyjs/ast" "1.9.0"
-    "@webassemblyjs/helper-wasm-bytecode" "1.9.0"
-    "@webassemblyjs/ieee754" "1.9.0"
-    "@webassemblyjs/leb128" "1.9.0"
-    "@webassemblyjs/utf8" "1.9.0"
-
-"@webassemblyjs/wasm-opt@1.9.0":
-  version "1.9.0"
-  resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-opt/-/wasm-opt-1.9.0.tgz#2211181e5b31326443cc8112eb9f0b9028721a61"
-  integrity sha512-Qkjgm6Anhm+OMbIL0iokO7meajkzQD71ioelnfPEj6r4eOFuqm4YC3VBPqXjFyyNwowzbMD+hizmprP/Fwkl2A==
-  dependencies:
-    "@webassemblyjs/ast" "1.9.0"
-    "@webassemblyjs/helper-buffer" "1.9.0"
-    "@webassemblyjs/wasm-gen" "1.9.0"
-    "@webassemblyjs/wasm-parser" "1.9.0"
-
-"@webassemblyjs/wasm-parser@1.9.0":
-  version "1.9.0"
-  resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-parser/-/wasm-parser-1.9.0.tgz#9d48e44826df4a6598294aa6c87469d642fff65e"
-  integrity sha512-9+wkMowR2AmdSWQzsPEjFU7njh8HTO5MqO8vjwEHuM+AMHioNqSBONRdr0NQQ3dVQrzp0s8lTcYqzUdb7YgELA==
-  dependencies:
-    "@webassemblyjs/ast" "1.9.0"
-    "@webassemblyjs/helper-api-error" "1.9.0"
-    "@webassemblyjs/helper-wasm-bytecode" "1.9.0"
-    "@webassemblyjs/ieee754" "1.9.0"
-    "@webassemblyjs/leb128" "1.9.0"
-    "@webassemblyjs/utf8" "1.9.0"
-
-"@webassemblyjs/wast-parser@1.9.0":
-  version "1.9.0"
-  resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-parser/-/wast-parser-1.9.0.tgz#3031115d79ac5bd261556cecc3fa90a3ef451914"
-  integrity sha512-qsqSAP3QQ3LyZjNC/0jBJ/ToSxfYJ8kYyuiGvtn/8MK89VrNEfwj7BPQzJVHi0jGTRK2dGdJ5PRqhtjzoww+bw==
-  dependencies:
-    "@webassemblyjs/ast" "1.9.0"
-    "@webassemblyjs/floating-point-hex-parser" "1.9.0"
-    "@webassemblyjs/helper-api-error" "1.9.0"
-    "@webassemblyjs/helper-code-frame" "1.9.0"
-    "@webassemblyjs/helper-fsm" "1.9.0"
+"@webassemblyjs/utf8@1.9.1":
+  version "1.9.1"
+  resolved "https://registry.yarnpkg.com/@webassemblyjs/utf8/-/utf8-1.9.1.tgz#d02d9daab85cda3211e43caf31dca74c260a73b0"
+  integrity sha512-llkYtppagjCodFjo0alWOUhAkfOiQPQDIc5oA6C9sFAXz7vC9QhZf/f8ijQIX+A9ToM3c9Pq85X0EX7nx9gVhg==
+
+"@webassemblyjs/wasm-edit@1.9.1":
+  version "1.9.1"
+  resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-edit/-/wasm-edit-1.9.1.tgz#e27a6bdbf78e5c72fa812a2fc3cbaad7c3e37578"
+  integrity sha512-S2IaD6+x9B2Xi8BCT0eGsrXXd8UxAh2LVJpg1ZMtHXnrDcsTtIX2bDjHi40Hio6Lc62dWHmKdvksI+MClCYbbw==
+  dependencies:
+    "@webassemblyjs/ast" "1.9.1"
+    "@webassemblyjs/helper-buffer" "1.9.1"
+    "@webassemblyjs/helper-wasm-bytecode" "1.9.1"
+    "@webassemblyjs/helper-wasm-section" "1.9.1"
+    "@webassemblyjs/wasm-gen" "1.9.1"
+    "@webassemblyjs/wasm-opt" "1.9.1"
+    "@webassemblyjs/wasm-parser" "1.9.1"
+    "@webassemblyjs/wast-printer" "1.9.1"
+
+"@webassemblyjs/wasm-gen@1.9.1":
+  version "1.9.1"
+  resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-gen/-/wasm-gen-1.9.1.tgz#56a0787d1fa7994fdc7bea59004e5bec7189c5fc"
+  integrity sha512-bqWI0S4lBQsEN5FTZ35vYzfKUJvtjNnBobB1agCALH30xNk1LToZ7Z8eiaR/Z5iVECTlBndoRQV3F6mbEqE/fg==
+  dependencies:
+    "@webassemblyjs/ast" "1.9.1"
+    "@webassemblyjs/helper-wasm-bytecode" "1.9.1"
+    "@webassemblyjs/ieee754" "1.9.1"
+    "@webassemblyjs/leb128" "1.9.1"
+    "@webassemblyjs/utf8" "1.9.1"
+
+"@webassemblyjs/wasm-opt@1.9.1":
+  version "1.9.1"
+  resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-opt/-/wasm-opt-1.9.1.tgz#fbdf8943a825e6dcc4cd69c3e092289fa4aec96c"
+  integrity sha512-gSf7I7YWVXZ5c6XqTEqkZjVs8K1kc1k57vsB6KBQscSagDNbAdxt6MwuJoMjsE1yWY1tsuL+pga268A6u+Fdkg==
+  dependencies:
+    "@webassemblyjs/ast" "1.9.1"
+    "@webassemblyjs/helper-buffer" "1.9.1"
+    "@webassemblyjs/wasm-gen" "1.9.1"
+    "@webassemblyjs/wasm-parser" "1.9.1"
+
+"@webassemblyjs/wasm-parser@1.9.1":
+  version "1.9.1"
+  resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-parser/-/wasm-parser-1.9.1.tgz#5e8352a246d3f605312c8e414f7990de55aaedfa"
+  integrity sha512-ImM4N2T1MEIond0MyE3rXvStVxEmivQrDKf/ggfh5pP6EHu3lL/YTAoSrR7shrbKNPpeKpGesW1LIK/L4kqduw==
+  dependencies:
+    "@webassemblyjs/ast" "1.9.1"
+    "@webassemblyjs/helper-api-error" "1.9.1"
+    "@webassemblyjs/helper-wasm-bytecode" "1.9.1"
+    "@webassemblyjs/ieee754" "1.9.1"
+    "@webassemblyjs/leb128" "1.9.1"
+    "@webassemblyjs/utf8" "1.9.1"
+
+"@webassemblyjs/wast-parser@1.9.1":
+  version "1.9.1"
+  resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-parser/-/wast-parser-1.9.1.tgz#e25ef13585c060073c1db0d6bd94340fdeee7596"
+  integrity sha512-2xVxejXSvj3ls/o2TR/zI6p28qsGupjHhnHL6URULQRcXmryn3w7G83jQMcT7PHqUfyle65fZtWLukfdLdE7qw==
+  dependencies:
+    "@webassemblyjs/ast" "1.9.1"
+    "@webassemblyjs/floating-point-hex-parser" "1.9.1"
+    "@webassemblyjs/helper-api-error" "1.9.1"
+    "@webassemblyjs/helper-code-frame" "1.9.1"
+    "@webassemblyjs/helper-fsm" "1.9.1"
     "@xtuc/long" "4.2.2"
 
-"@webassemblyjs/wast-printer@1.9.0":
-  version "1.9.0"
-  resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-printer/-/wast-printer-1.9.0.tgz#4935d54c85fef637b00ce9f52377451d00d47899"
-  integrity sha512-2J0nE95rHXHyQ24cWjMKJ1tqB/ds8z/cyeOZxJhcb+rW+SQASVjuznUSmdz5GpVJTzU8JkhYut0D3siFDD6wsA==
+"@webassemblyjs/wast-printer@1.9.1":
+  version "1.9.1"
+  resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-printer/-/wast-printer-1.9.1.tgz#b9f38e93652037d4f3f9c91584635af4191ed7c1"
+  integrity sha512-tDV8V15wm7mmbAH6XvQRU1X+oPGmeOzYsd6h7hlRLz6QpV4Ec/KKxM8OpLtFmQPLCreGxTp+HuxtH4pRIZyL9w==
   dependencies:
-    "@webassemblyjs/ast" "1.9.0"
-    "@webassemblyjs/wast-parser" "1.9.0"
+    "@webassemblyjs/ast" "1.9.1"
+    "@webassemblyjs/wast-parser" "1.9.1"
     "@xtuc/long" "4.2.2"
 
 "@webpack-cli/info@^1.1.0":
@@ -1686,7 +1710,7 @@ ajv-keywords@^3.1.0, ajv-keywords@^3.5.2:
   resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.5.2.tgz#31f29da5ab6e00d1c2d329acf7b5929614d5014d"
   integrity sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==
 
-ajv@^6.1.0, ajv@^6.10.0, ajv@^6.10.2, ajv@^6.12.3, ajv@^6.12.4:
+ajv@^6.1.0, ajv@^6.10.0, ajv@^6.12.3, ajv@^6.12.4:
   version "6.12.4"
   resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.4.tgz#0614facc4522127fa713445c6bfd3ebd376e2234"
   integrity sha512-eienB2c9qVQs2KWexhkrdMLVDoIQCz5KSeLxwg9Lzk4DOfBtIK9PQwwufcsn1jjGuf9WZmqPMbGxOzfcuphJCQ==
@@ -1885,6 +1909,17 @@ array-includes@^3.1.1:
     es-abstract "^1.17.0"
     is-string "^1.0.5"
 
+array-includes@^3.1.2:
+  version "3.1.2"
+  resolved "https://registry.yarnpkg.com/array-includes/-/array-includes-3.1.2.tgz#a8db03e0b88c8c6aeddc49cb132f9bcab4ebf9c8"
+  integrity sha512-w2GspexNQpx+PutG3QpT437/BenZBj0M/MZGn5mzv/MofYqo0xmRHzn4lFsoDlWJ+THYsGJmFlW68WlDFx7VRw==
+  dependencies:
+    call-bind "^1.0.0"
+    define-properties "^1.1.3"
+    es-abstract "^1.18.0-next.1"
+    get-intrinsic "^1.0.1"
+    is-string "^1.0.5"
+
 array-union@^1.0.1:
   version "1.0.2"
   resolved "https://registry.yarnpkg.com/array-union/-/array-union-1.0.2.tgz#9a34410e4f4e3da23dea375be5be70f24778ec39"
@@ -1951,11 +1986,6 @@ ast-types-flow@^0.0.7:
   resolved "https://registry.yarnpkg.com/ast-types-flow/-/ast-types-flow-0.0.7.tgz#f70b735c6bca1a5c9c22d982c3e39e7feba3bdad"
   integrity sha1-9wtzXGvKGlycItmCw+Oef+ujva0=
 
-astral-regex@^1.0.0:
-  version "1.0.0"
-  resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-1.0.0.tgz#6c8c3fb827dd43ee3918f27b82782ab7658a6fd9"
-  integrity sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==
-
 astral-regex@^2.0.0:
   version "2.0.0"
   resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-2.0.0.tgz#483143c567aeed4785759c0865786dc77d7d2e31"
@@ -2008,12 +2038,12 @@ aws4@^1.8.0:
   resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.10.1.tgz#e1e82e4f3e999e2cfd61b161280d16a111f86428"
   integrity sha512-zg7Hz2k5lI8kb7U32998pRRFin7zJlkfezGJjUc2heaD4Pw2wObakCDVzkKztTm/Ln7eiVvYsjqak0Ed4LkMDA==
 
-axe-core@^3.5.4:
-  version "3.5.5"
-  resolved "https://registry.yarnpkg.com/axe-core/-/axe-core-3.5.5.tgz#84315073b53fa3c0c51676c588d59da09a192227"
-  integrity sha512-5P0QZ6J5xGikH780pghEdbEKijCTrruK9KxtPZCFWUpef0f6GipO+xEZ5GKCb020mmqgbiNO6TcA55CriL784Q==
+axe-core@^4.0.2:
+  version "4.1.1"
+  resolved "https://registry.yarnpkg.com/axe-core/-/axe-core-4.1.1.tgz#70a7855888e287f7add66002211a423937063eaf"
+  integrity sha512-5Kgy8Cz6LPC9DJcNb3yjAXTu3XihQgEdnIg50c//zOC/MyLP0Clg+Y8Sh9ZjjnvBrDZU4DgXS9C3T9r4/scGZQ==
 
-axobject-query@^2.1.2:
+axobject-query@^2.2.0:
   version "2.2.0"
   resolved "https://registry.yarnpkg.com/axobject-query/-/axobject-query-2.2.0.tgz#943d47e10c0b704aa42275e20edf3722648989be"
   integrity sha512-Td525n+iPOOyUQIeBfcASuG6uJsDOITl7Mds5gFyerkWiX7qhUTdYUBlSgNMyVqtSJqwpt1kXGLdUt6SykLMRA==
@@ -2223,16 +2253,16 @@ browserslist@^4.14.5:
     escalade "^3.1.0"
     node-releases "^1.1.61"
 
-browserslist@^4.14.7:
-  version "4.14.7"
-  resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.14.7.tgz#c071c1b3622c1c2e790799a37bb09473a4351cb6"
-  integrity sha512-BSVRLCeG3Xt/j/1cCGj1019Wbty0H+Yvu2AOuZSuoaUWn3RatbL33Cxk+Q4jRMRAbOm0p7SLravLjpnT6s0vzQ==
+browserslist@^4.15.0:
+  version "4.16.0"
+  resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.16.0.tgz#410277627500be3cb28a1bfe037586fbedf9488b"
+  integrity sha512-/j6k8R0p3nxOC6kx5JGAxsnhc9ixaWJfYc+TNTzxg6+ARaESAvQGV7h0uNOB4t+pLQJZWzcrMxXOxjgsCj3dqQ==
   dependencies:
-    caniuse-lite "^1.0.30001157"
+    caniuse-lite "^1.0.30001165"
     colorette "^1.2.1"
-    electron-to-chromium "^1.3.591"
+    electron-to-chromium "^1.3.621"
     escalade "^3.1.1"
-    node-releases "^1.1.66"
+    node-releases "^1.1.67"
 
 buffer-from@^1.0.0:
   version "1.1.1"
@@ -2313,29 +2343,6 @@ cacache@^11.0.2, cacache@^11.3.3:
     unique-filename "^1.1.1"
     y18n "^4.0.0"
 
-cacache@^15.0.5:
-  version "15.0.5"
-  resolved "https://registry.yarnpkg.com/cacache/-/cacache-15.0.5.tgz#69162833da29170d6732334643c60e005f5f17d0"
-  integrity sha512-lloiL22n7sOjEEXdL8NAjTgv9a1u43xICE9/203qonkZUCj5X1UEWIdf2/Y0d6QcCtMzbKQyhrcDbdvlZTs/+A==
-  dependencies:
-    "@npmcli/move-file" "^1.0.1"
-    chownr "^2.0.0"
-    fs-minipass "^2.0.0"
-    glob "^7.1.4"
-    infer-owner "^1.0.4"
-    lru-cache "^6.0.0"
-    minipass "^3.1.1"
-    minipass-collect "^1.0.2"
-    minipass-flush "^1.0.5"
-    minipass-pipeline "^1.2.2"
-    mkdirp "^1.0.3"
-    p-map "^4.0.0"
-    promise-inflight "^1.0.1"
-    rimraf "^3.0.2"
-    ssri "^8.0.0"
-    tar "^6.0.2"
-    unique-filename "^1.1.1"
-
 cache-base@^1.0.1:
   version "1.0.1"
   resolved "https://registry.yarnpkg.com/cache-base/-/cache-base-1.0.1.tgz#0a7f46416831c8b662ee36fe4e7c59d76f666ab2"
@@ -2351,6 +2358,14 @@ cache-base@^1.0.1:
     union-value "^1.0.0"
     unset-value "^1.0.0"
 
+call-bind@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.0.tgz#24127054bb3f9bdcb4b1fb82418186072f77b8ce"
+  integrity sha512-AEXsYIyyDY3MCzbwdhzG3Jx1R0J2wetQyUynn6dYHAO+bg8l1k7jwZtRv4ryryFs7EP+NDlikJlVe59jr0cM2w==
+  dependencies:
+    function-bind "^1.1.1"
+    get-intrinsic "^1.0.0"
+
 call-limit@~1.1.0:
   version "1.1.1"
   resolved "https://registry.yarnpkg.com/call-limit/-/call-limit-1.1.1.tgz#ef15f2670db3f1992557e2d965abc459e6e358d4"
@@ -2394,10 +2409,10 @@ caniuse-lite@^1.0.30001135:
   resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001151.tgz#1ddfde5e6fff02aad7940b4edb7d3ac76b0cb00b"
   integrity sha512-Zh3sHqskX6mHNrqUerh+fkf0N72cMxrmflzje/JyVImfpknscMnkeJrlFGJcqTmaa0iszdYptGpWMJCRQDkBVw==
 
-caniuse-lite@^1.0.30001157:
-  version "1.0.30001164"
-  resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001164.tgz#5bbfd64ca605d43132f13cc7fdabb17c3036bfdc"
-  integrity sha512-G+A/tkf4bu0dSp9+duNiXc7bGds35DioCyC6vgK2m/rjA4Krpy5WeZgZyfH2f0wj2kI6yAWWucyap6oOwmY1mg==
+caniuse-lite@^1.0.30001165:
+  version "1.0.30001170"
+  resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001170.tgz#0088bfecc6a14694969e391cc29d7eb6362ca6a7"
+  integrity sha512-Dd4d/+0tsK0UNLrZs3CvNukqalnVTRrxb5mcQm8rHL49t7V5ZaTygwXkrq+FB+dVDf++4ri8eJnFEJAB8332PA==
 
 capture-stack-trace@^1.0.0:
   version "1.0.1"
@@ -2837,14 +2852,12 @@ copy-descriptor@^0.1.0:
   resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d"
   integrity sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=
 
-copy-webpack-plugin@^6.4.0:
-  version "6.4.0"
-  resolved "https://registry.yarnpkg.com/copy-webpack-plugin/-/copy-webpack-plugin-6.4.0.tgz#7fd397af78e0d310dbf6575d1a0f2fe10efd4d59"
-  integrity sha512-p4eIA0ZWk4UI+xewyxOBTDCSDfjK6nCkr3zhDenoi7SFd+NgDNH/D14IZeFaCEFcK/psNDcAUMOB+sAxZ3SsAA==
+copy-webpack-plugin@^7.0.0:
+  version "7.0.0"
+  resolved "https://registry.yarnpkg.com/copy-webpack-plugin/-/copy-webpack-plugin-7.0.0.tgz#3506f867ca6e861ee2769d4deaf8fa0d2563ada9"
+  integrity sha512-SLjQNa5iE3BoCP76ESU9qYo9ZkEWtXoZxDurHoqPchAFRblJ9g96xTeC560UXBMre1Nx6ixIIUfiY3VcjpJw3g==
   dependencies:
-    cacache "^15.0.5"
     fast-glob "^3.2.4"
-    find-cache-dir "^3.3.1"
     glob-parent "^5.1.1"
     globby "^11.0.1"
     loader-utils "^2.0.0"
@@ -2852,14 +2865,13 @@ copy-webpack-plugin@^6.4.0:
     p-limit "^3.0.2"
     schema-utils "^3.0.0"
     serialize-javascript "^5.0.1"
-    webpack-sources "^1.4.3"
 
-core-js-compat@^3.7.0:
-  version "3.8.0"
-  resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.8.0.tgz#3248c6826f4006793bd637db608bca6e4cd688b1"
-  integrity sha512-o9QKelQSxQMYWHXc/Gc4L8bx/4F7TTraE5rhuN8I7mKBt5dBIUpXpIR3omv70ebr8ST5R3PqbDQr+ZI3+Tt1FQ==
+core-js-compat@^3.8.0:
+  version "3.8.1"
+  resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.8.1.tgz#8d1ddd341d660ba6194cbe0ce60f4c794c87a36e"
+  integrity sha512-a16TLmy9NVD1rkjUGbwuyWkiDoN0FDpAwrfLONvHFQx0D9k7J9y0srwMT8QP/Z6HE3MIFaVynEeYwZwPX1o5RQ==
   dependencies:
-    browserslist "^4.14.7"
+    browserslist "^4.15.0"
     semver "7.0.0"
 
 core-js-pure@^3.0.0:
@@ -3252,10 +3264,10 @@ electron-to-chromium@^1.3.571:
   resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.583.tgz#47a9fde74740b1205dba96db2e433132964ba3ee"
   integrity sha512-L9BwLwJohjZW9mQESI79HRzhicPk1DFgM+8hOCfGgGCFEcA3Otpv7QK6SGtYoZvfQfE3wKLh0Hd5ptqUFv3gvQ==
 
-electron-to-chromium@^1.3.591:
-  version "1.3.612"
-  resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.612.tgz#4a49864b9de694403a69d5a9f439cbceca543e48"
-  integrity sha512-CdrdX1B6mQqxfw+51MPWB5qA6TKWjza9f5voBtUlRfEZEwZiFaxJLrhFI8zHE9SBAuGt4h84rQU6Ho9Bauo1LA==
+electron-to-chromium@^1.3.621:
+  version "1.3.633"
+  resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.633.tgz#16dd5aec9de03894e8d14a1db4cda8a369b9b7fe"
+  integrity sha512-bsVCsONiVX1abkWdH7KtpuDAhsQ3N3bjPYhROSAXE78roJKet0Y5wznA14JE9pzbwSZmSMAW6KiKYf1RvbTJkA==
 
 emoji-regex@^7.0.1:
   version "7.0.3"
@@ -3385,6 +3397,24 @@ es-abstract@^1.18.0-next.0:
     string.prototype.trimend "^1.0.1"
     string.prototype.trimstart "^1.0.1"
 
+es-abstract@^1.18.0-next.1:
+  version "1.18.0-next.1"
+  resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.18.0-next.1.tgz#6e3a0a4bda717e5023ab3b8e90bec36108d22c68"
+  integrity sha512-I4UGspA0wpZXWENrdA0uHbnhte683t3qT/1VFH9aX2dA5PPSf6QW5HHXf5HImaqPmjXaVeVk4RGWnaylmV7uAA==
+  dependencies:
+    es-to-primitive "^1.2.1"
+    function-bind "^1.1.1"
+    has "^1.0.3"
+    has-symbols "^1.0.1"
+    is-callable "^1.2.2"
+    is-negative-zero "^2.0.0"
+    is-regex "^1.1.1"
+    object-inspect "^1.8.0"
+    object-keys "^1.1.1"
+    object.assign "^4.1.1"
+    string.prototype.trimend "^1.0.1"
+    string.prototype.trimstart "^1.0.1"
+
 es-to-primitive@^1.2.1:
   version "1.2.1"
   resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.1.tgz#e55cd4c9cdc188bcefb03b366c736323fc5c898a"
@@ -3429,12 +3459,10 @@ eslint-ast-utils@^1.1.0:
     lodash.get "^4.4.2"
     lodash.zip "^4.2.0"
 
-eslint-config-prettier@6.13.0:
-  version "6.13.0"
-  resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-6.13.0.tgz#207d88796b5624e5bb815bbbdfc5891ceb9ebffa"
-  integrity sha512-LcT0i0LSmnzqK2t764pyIt7kKH2AuuqKRTtJTdddWxOiUja9HdG5GXBVF2gmCTvVYWVsTu8J2MhJLVGRh+pj8w==
-  dependencies:
-    get-stdin "^6.0.0"
+eslint-config-prettier@7.1.0:
+  version "7.1.0"
+  resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-7.1.0.tgz#5402eb559aa94b894effd6bddfa0b1ca051c858f"
+  integrity sha512-9sm5/PxaFG7qNJvJzTROMM1Bk1ozXVTKI0buKOyb0Bsr1hrwi0H/TzxF/COtf1uxikIK8SwhX7K6zg78jAzbeA==
 
 eslint-import-resolver-node@^0.3.4:
   version "0.3.4"
@@ -3486,48 +3514,48 @@ eslint-plugin-import@2.22.1:
     resolve "^1.17.0"
     tsconfig-paths "^3.9.0"
 
-eslint-plugin-jane@^9.0.4:
-  version "9.0.4"
-  resolved "https://registry.yarnpkg.com/eslint-plugin-jane/-/eslint-plugin-jane-9.0.4.tgz#57ef9c1c1b86213b782ade988374b27533cc18c8"
-  integrity sha512-29ceI3RjUfjRnObnw4xr9tiMHeWhVHxqwyh6lFi8FkUxkId6f6aklmQGm+ZUap9V545Dg5WDKzr89BPzBl165w==
+eslint-plugin-jane@^9.0.6:
+  version "9.0.6"
+  resolved "https://registry.yarnpkg.com/eslint-plugin-jane/-/eslint-plugin-jane-9.0.6.tgz#eeef17db748ef4cdacf66a54d08e5b1e99b7372b"
+  integrity sha512-TREFB1cewQqx4Bv6I1hAZ3oYmWPFAwUdEV8Kh2bvuGpA1V2hGWEJi6XqLUUkuo+hyVRYcceQp4MGYdgj0PtbLg==
   dependencies:
-    "@typescript-eslint/eslint-plugin" "4.5.0"
-    "@typescript-eslint/parser" "4.5.0"
+    "@typescript-eslint/eslint-plugin" "4.11.0"
+    "@typescript-eslint/parser" "4.11.0"
     babel-eslint "10.1.0"
-    eslint-config-prettier "6.13.0"
+    eslint-config-prettier "7.1.0"
     eslint-plugin-babel "5.3.1"
     eslint-plugin-import "2.22.1"
-    eslint-plugin-jest "24.1.0"
-    eslint-plugin-jsx-a11y "6.3.1"
+    eslint-plugin-jest "24.1.3"
+    eslint-plugin-jsx-a11y "6.4.1"
     eslint-plugin-node "11.1.0"
-    eslint-plugin-prettier "3.1.4"
+    eslint-plugin-prettier "3.3.0"
     eslint-plugin-promise "4.2.1"
     eslint-plugin-react "7.21.5"
     eslint-plugin-react-hooks "4.2.0"
-    eslint-plugin-unicorn "21.0.0"
+    eslint-plugin-unicorn "24.0.0"
 
-eslint-plugin-jest@24.1.0:
-  version "24.1.0"
-  resolved "https://registry.yarnpkg.com/eslint-plugin-jest/-/eslint-plugin-jest-24.1.0.tgz#6708037d7602e5288ce877fd0103f329dc978361"
-  integrity sha512-827YJ+E8B9PvXu/0eiVSNFfxxndbKv+qE/3GSMhdorCaeaOehtqHGX2YDW9B85TEOre9n/zscledkFW/KbnyGg==
+eslint-plugin-jest@24.1.3:
+  version "24.1.3"
+  resolved "https://registry.yarnpkg.com/eslint-plugin-jest/-/eslint-plugin-jest-24.1.3.tgz#fa3db864f06c5623ff43485ca6c0e8fc5fe8ba0c"
+  integrity sha512-dNGGjzuEzCE3d5EPZQ/QGtmlMotqnYWD/QpCZ1UuZlrMAdhG5rldh0N0haCvhGnUkSeuORS5VNROwF9Hrgn3Lg==
   dependencies:
     "@typescript-eslint/experimental-utils" "^4.0.1"
 
-eslint-plugin-jsx-a11y@6.3.1:
-  version "6.3.1"
-  resolved "https://registry.yarnpkg.com/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.3.1.tgz#99ef7e97f567cc6a5b8dd5ab95a94a67058a2660"
-  integrity sha512-i1S+P+c3HOlBJzMFORRbC58tHa65Kbo8b52/TwCwSKLohwvpfT5rm2GjGWzOHTEuq4xxf2aRlHHTtmExDQOP+g==
+eslint-plugin-jsx-a11y@6.4.1:
+  version "6.4.1"
+  resolved "https://registry.yarnpkg.com/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.4.1.tgz#a2d84caa49756942f42f1ffab9002436391718fd"
+  integrity sha512-0rGPJBbwHoGNPU73/QCLP/vveMlM1b1Z9PponxO87jfr6tuH5ligXbDT6nHSSzBC8ovX2Z+BQu7Bk5D/Xgq9zg==
   dependencies:
-    "@babel/runtime" "^7.10.2"
+    "@babel/runtime" "^7.11.2"
     aria-query "^4.2.2"
     array-includes "^3.1.1"
     ast-types-flow "^0.0.7"
-    axe-core "^3.5.4"
-    axobject-query "^2.1.2"
+    axe-core "^4.0.2"
+    axobject-query "^2.2.0"
     damerau-levenshtein "^1.0.6"
     emoji-regex "^9.0.0"
     has "^1.0.3"
-    jsx-ast-utils "^2.4.1"
+    jsx-ast-utils "^3.1.0"
     language-tags "^1.0.5"
 
 eslint-plugin-node@11.1.0:
@@ -3542,10 +3570,10 @@ eslint-plugin-node@11.1.0:
     resolve "^1.10.1"
     semver "^6.1.0"
 
-eslint-plugin-prettier@3.1.4:
-  version "3.1.4"
-  resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-3.1.4.tgz#168ab43154e2ea57db992a2cd097c828171f75c2"
-  integrity sha512-jZDa8z76klRqo+TdGDTFJSavwbnWK2ZpqGKNZ+VvweMW516pDUMmQ2koXvxEE4JhzNvTv+radye/bWGBmA6jmg==
+eslint-plugin-prettier@3.3.0:
+  version "3.3.0"
+  resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-3.3.0.tgz#61e295349a65688ffac0b7808ef0a8244bdd8d40"
+  integrity sha512-tMTwO8iUWlSRZIwS9k7/E4vrTsfvsrcM5p1eftyuqWH25nKsz/o6/54I7jwQ/3zobISyC7wMy9ZsFwgTxOcOpQ==
   dependencies:
     prettier-linter-helpers "^1.0.0"
 
@@ -3576,24 +3604,24 @@ eslint-plugin-react@7.21.5:
     resolve "^1.18.1"
     string.prototype.matchall "^4.0.2"
 
-eslint-plugin-unicorn@21.0.0:
-  version "21.0.0"
-  resolved "https://registry.yarnpkg.com/eslint-plugin-unicorn/-/eslint-plugin-unicorn-21.0.0.tgz#7e3a8b0f725f003619e1f40d769939ecd8d708d0"
-  integrity sha512-S8v7+v4gZTQPj4pKKvexhgSUaLQSyItvxW2SVZDaX9Iu5IjlAmF2eni+L6w8a2aqshxgU8Lle4FIAVDtuejSKQ==
+eslint-plugin-unicorn@24.0.0:
+  version "24.0.0"
+  resolved "https://registry.yarnpkg.com/eslint-plugin-unicorn/-/eslint-plugin-unicorn-24.0.0.tgz#08017ccd7ac704777d459cff6e13a35f54cbcab1"
+  integrity sha512-NfLjIZas/ZUwc3S+pUtbTRqgCkODxPEkJBJ5ZR8wIu90BmX4jmXp10hoOZMScR2CR1NYTtrx0OX4BQvBnbzZzA==
   dependencies:
     ci-info "^2.0.0"
     clean-regexp "^1.0.0"
     eslint-ast-utils "^1.1.0"
-    eslint-template-visitor "^2.0.0"
+    eslint-template-visitor "^2.2.1"
     eslint-utils "^2.1.0"
     import-modules "^2.0.0"
-    lodash "^4.17.15"
+    lodash "^4.17.20"
     pluralize "^8.0.0"
     read-pkg-up "^7.0.1"
     regexp-tree "^0.1.21"
     reserved-words "^0.1.2"
     safe-regex "^2.1.1"
-    semver "^7.3.2"
+    semver "^7.3.4"
 
 eslint-rule-composer@^0.3.0:
   version "0.3.0"
@@ -3616,13 +3644,13 @@ eslint-scope@^5.1.1:
     esrecurse "^4.3.0"
     estraverse "^4.1.1"
 
-eslint-template-visitor@^2.0.0:
-  version "2.2.1"
-  resolved "https://registry.yarnpkg.com/eslint-template-visitor/-/eslint-template-visitor-2.2.1.tgz#2dccb1ab28fa7429e56ba6dd0144def2d89bc2d6"
-  integrity sha512-q3SxoBXz0XjPGkUpwGVAwIwIPIxzCAJX1uwfVc8tW3v7u/zS7WXNH3I2Mu2MDz2NgSITAyKLRaQFPHu/iyKxDQ==
+eslint-template-visitor@^2.2.1:
+  version "2.2.2"
+  resolved "https://registry.yarnpkg.com/eslint-template-visitor/-/eslint-template-visitor-2.2.2.tgz#46cd2b06eca5c1d97369aadd96e131df88fdd59c"
+  integrity sha512-SkcLjzKw3JjKTWHacRDeLBa2gxb600zbCKTkXj/V97QnZ9yxkknoPL8vc8PFueqbFXP7mYNTQzjCjcMpTRdRaA==
   dependencies:
     babel-eslint "^10.1.0"
-    eslint-visitor-keys "^1.3.0"
+    eslint-visitor-keys "^2.0.0"
     esquery "^1.3.1"
     multimap "^1.1.0"
 
@@ -3643,10 +3671,10 @@ eslint-visitor-keys@^2.0.0:
   resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-2.0.0.tgz#21fdc8fbcd9c795cc0321f0563702095751511a8"
   integrity sha512-QudtT6av5WXels9WjIM7qz1XD1cWGvX4gGXvp/zBn9nXG02D0utdU3Em2m/QjTnrsk6bBjmCygl3rmj118msQQ==
 
-eslint@^7.15.0:
-  version "7.15.0"
-  resolved "https://registry.yarnpkg.com/eslint/-/eslint-7.15.0.tgz#eb155fb8ed0865fcf5d903f76be2e5b6cd7e0bc7"
-  integrity sha512-Vr64xFDT8w30wFll643e7cGrIkPEU50yIiI36OdSIDoSGguIeaLzBo0vpGvzo9RECUqq7htURfwEtKqwytkqzA==
+eslint@^7.16.0:
+  version "7.16.0"
+  resolved "https://registry.yarnpkg.com/eslint/-/eslint-7.16.0.tgz#a761605bf9a7b32d24bb7cde59aeb0fd76f06092"
+  integrity sha512-iVWPS785RuDA4dWuhhgXTNrGxHHK3a8HLSMBgbbU59ruJDubUraXN8N5rn7kb8tG6sjg74eE0RA3YWT51eusEw==
   dependencies:
     "@babel/code-frame" "^7.0.0"
     "@eslint/eslintrc" "^0.2.2"
@@ -3682,7 +3710,7 @@ eslint@^7.15.0:
     semver "^7.2.1"
     strip-ansi "^6.0.0"
     strip-json-comments "^3.1.0"
-    table "^5.2.3"
+    table "^6.0.4"
     text-table "^0.2.0"
     v8-compile-cache "^2.0.3"
 
@@ -4288,6 +4316,15 @@ get-caller-file@^2.0.1:
   resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e"
   integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==
 
+get-intrinsic@^1.0.0, get-intrinsic@^1.0.1:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.0.2.tgz#6820da226e50b24894e08859469dc68361545d49"
+  integrity sha512-aeX0vrFm21ILl3+JpFFRNe9aUvp6VFZb2/CTbgLb8j75kOhvoNYjt9d8KA/tJG4gSo8nzEDedRl0h7vDmBYRVg==
+  dependencies:
+    function-bind "^1.1.1"
+    has "^1.0.3"
+    has-symbols "^1.0.1"
+
 get-own-enumerable-property-symbols@^3.0.0:
   version "3.0.2"
   resolved "https://registry.yarnpkg.com/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.2.tgz#b5fde77f22cbe35f390b4e089922c50bce6ef664"
@@ -4298,11 +4335,6 @@ get-stdin@^4.0.1:
   resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-4.0.1.tgz#b968c6b0a04384324902e8bf1a5df32579a450fe"
   integrity sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4=
 
-get-stdin@^6.0.0:
-  version "6.0.0"
-  resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-6.0.0.tgz#9e09bf712b360ab9225e812048f71fde9c89657b"
-  integrity sha512-jp4tHawyV7+fkkSKyvjuLZswblUtz+SQKzSWnBbii16BuZksJlU1wuBYXY75r+duh/llF1ur6oNwi+2ZzjKZ7g==
-
 get-stream@^3.0.0:
   version "3.0.0"
   resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-3.0.0.tgz#8e943d1358dc37555054ecbe2edb05aa174ede14"
@@ -4674,10 +4706,10 @@ humanize-ms@^1.2.1:
   dependencies:
     ms "^2.0.0"
 
-husky@^4.3.5:
-  version "4.3.5"
-  resolved "https://registry.yarnpkg.com/husky/-/husky-4.3.5.tgz#ab8d2a0eb6b62fef2853ee3d442c927d89290902"
-  integrity sha512-E5S/1HMoDDaqsH8kDF5zeKEQbYqe3wL9zJDyqyYqc8I4vHBtAoxkDBGXox0lZ9RI+k5GyB728vZdmnM4bYap+g==
+husky@^4.3.6:
+  version "4.3.6"
+  resolved "https://registry.yarnpkg.com/husky/-/husky-4.3.6.tgz#ebd9dd8b9324aa851f1587318db4cccb7665a13c"
+  integrity sha512-o6UjVI8xtlWRL5395iWq9LKDyp/9TE7XMOTvIpEVzW638UcGxTmV5cfel6fsk/jbZSTlvfGVJf2svFtybcIZag==
   dependencies:
     chalk "^4.0.0"
     ci-info "^2.0.0"
@@ -5043,6 +5075,11 @@ is-callable@^1.1.4, is-callable@^1.2.0:
   resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.0.tgz#83336560b54a38e35e3a2df7afd0454d691468bb"
   integrity sha512-pyVD9AaGLxtg6srb2Ng6ynWJqkHU9bEM087AKck0w8QwDarTfNcpIYoU8x8Hv2Icm8u6kFJM18Dag8lyqGkviw==
 
+is-callable@^1.2.2:
+  version "1.2.2"
+  resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.2.tgz#c7c6715cd22d4ddb48d3e19970223aceabb080d9"
+  integrity sha512-dnMqspv5nU3LoewK2N/y7KLtxtakvTuaCsU9FU50/QDmdbHNy/4/JuRtMHqRU22o3q+W89YQndQEeCVwK+3qrA==
+
 is-ci@^1.0.10:
   version "1.2.1"
   resolved "https://registry.yarnpkg.com/is-ci/-/is-ci-1.2.1.tgz#e3779c8ee17fccf428488f6e281187f2e632841c"
@@ -5432,14 +5469,6 @@ jsprim@^1.2.2:
     json-schema "0.2.3"
     verror "1.10.0"
 
-jsx-ast-utils@^2.4.1:
-  version "2.4.1"
-  resolved "https://registry.yarnpkg.com/jsx-ast-utils/-/jsx-ast-utils-2.4.1.tgz#1114a4c1209481db06c690c2b4f488cc665f657e"
-  integrity sha512-z1xSldJ6imESSzOjd3NNkieVJKRlKYSOtMG8SFyCj2FIrvSaSuli/WjpBkEzCBoR9bYYYFgqJw61Xhu7Lcgk+w==
-  dependencies:
-    array-includes "^3.1.1"
-    object.assign "^4.1.0"
-
 "jsx-ast-utils@^2.4.1 || ^3.0.0":
   version "3.1.0"
   resolved "https://registry.yarnpkg.com/jsx-ast-utils/-/jsx-ast-utils-3.1.0.tgz#642f1d7b88aa6d7eb9d8f2210e166478444fa891"
@@ -5448,6 +5477,14 @@ jsx-ast-utils@^2.4.1:
     array-includes "^3.1.1"
     object.assign "^4.1.1"
 
+jsx-ast-utils@^3.1.0:
+  version "3.2.0"
+  resolved "https://registry.yarnpkg.com/jsx-ast-utils/-/jsx-ast-utils-3.2.0.tgz#41108d2cec408c3453c1bbe8a4aae9e1e2bd8f82"
+  integrity sha512-EIsmt3O3ljsU6sot/J4E1zDRxfBNrhjyf/OKjlydwgEimQuznlM4Wv7U+ueONJMyEn1WRE0K8dhi3dVAXYT24Q==
+  dependencies:
+    array-includes "^3.1.2"
+    object.assign "^4.1.2"
+
 jwt-decode@^3.1.2:
   version "3.1.2"
   resolved "https://registry.yarnpkg.com/jwt-decode/-/jwt-decode-3.1.2.tgz#3fb319f3675a2df0c2895c8f5e9fa4b67b04ed59"
@@ -5518,10 +5555,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-beta6:
+  version "1.0.17-beta6"
+  resolved "https://registry.yarnpkg.com/lemmy-js-client/-/lemmy-js-client-1.0.17-beta6.tgz#afe1e1da13172a161c4d976b1ee58fe81eb22829"
+  integrity sha512-+oX7J7wht8nH4a5NQngK1GNner3TDv6ZOhQQVI5KcK7vynVVIcgveC5KBJArHBAl5acXpLs3Khmx0ZEb+sErJA==
 
 leven@^3.1.0:
   version "3.1.0"
@@ -5772,7 +5809,7 @@ lodash@^3.10.1:
   resolved "https://registry.yarnpkg.com/lodash/-/lodash-3.10.1.tgz#5bf45e8e49ba4189e17d482789dfd15bd140b7b6"
   integrity sha1-W/Rejkm6QYnhfUgnid/RW9FAt7Y=
 
-lodash@^4.0.0, lodash@^4.17.11, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@~4.17.10:
+lodash@^4.0.0, lodash@^4.17.11, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.20, lodash@~4.17.10:
   version "4.17.20"
   resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.20.tgz#b44a9b6297bcb698f1c51a3545a2b3b368d59c52"
   integrity sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==
@@ -5943,10 +5980,10 @@ markdown-it-sup@^1.0.0:
   resolved "https://registry.yarnpkg.com/markdown-it-sup/-/markdown-it-sup-1.0.0.tgz#cb9c9ff91a5255ac08f3fd3d63286e15df0a1fc3"
   integrity sha1-y5yf+RpSVawI8/09YyhuFd8KH8M=
 
-markdown-it@^12.0.3:
-  version "12.0.3"
-  resolved "https://registry.yarnpkg.com/markdown-it/-/markdown-it-12.0.3.tgz#8d1e47daf1d716d63610495eb93f6665573e4abe"
-  integrity sha512-M57RsMv+QQmJHz1yCu0gTJRMx/LlxRPtrrw+2kb/CpDVK/graCmWO0qfNnz/SE1FCNdyq3pkMMZ+itTnyT/YGA==
+markdown-it@^12.0.4:
+  version "12.0.4"
+  resolved "https://registry.yarnpkg.com/markdown-it/-/markdown-it-12.0.4.tgz#eec8247d296327eac3ba9746bdeec9cfcc751e33"
+  integrity sha512-34RwOXZT8kyuOJy25oJNJoulO8L0bTHYWXcdZBYZqFnjIy3NgjeoM3FmPXIOFQ26/lSHYMr8oc62B6adxXcb3Q==
   dependencies:
     argparse "^2.0.1"
     entities "~2.1.0"
@@ -6079,10 +6116,10 @@ mimic-fn@^2.1.0:
   resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b"
   integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==
 
-mini-css-extract-plugin@^1.3.2:
-  version "1.3.2"
-  resolved "https://registry.yarnpkg.com/mini-css-extract-plugin/-/mini-css-extract-plugin-1.3.2.tgz#e2c9c0ef3f7c1a516916a3ab7b073a761e5b1d26"
-  integrity sha512-ofYJgCZNm1TToSv02pGANe1lfb31g7ULwNV5Nt31d2dAnVLxFHoguDUAj6U0BLEO7Nrztq4mdtL1yFDaeW7J+A==
+mini-css-extract-plugin@^1.3.3:
+  version "1.3.3"
+  resolved "https://registry.yarnpkg.com/mini-css-extract-plugin/-/mini-css-extract-plugin-1.3.3.tgz#7802e62b34199aa7d1a62e654395859a836486a0"
+  integrity sha512-7lvliDSMiuZc81kI+5/qxvn47SCM7BehXex3f2c6l/pR3Goj58IQxZh9nuPQ3AkGQgoETyXuIqLDaO5Oa0TyBw==
   dependencies:
     loader-utils "^2.0.0"
     schema-utils "^3.0.0"
@@ -6105,27 +6142,6 @@ minimist@^1.1.3, minimist@^1.2.0, minimist@^1.2.5:
   resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602"
   integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==
 
-minipass-collect@^1.0.2:
-  version "1.0.2"
-  resolved "https://registry.yarnpkg.com/minipass-collect/-/minipass-collect-1.0.2.tgz#22b813bf745dc6edba2576b940022ad6edc8c617"
-  integrity sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA==
-  dependencies:
-    minipass "^3.0.0"
-
-minipass-flush@^1.0.5:
-  version "1.0.5"
-  resolved "https://registry.yarnpkg.com/minipass-flush/-/minipass-flush-1.0.5.tgz#82e7135d7e89a50ffe64610a787953c4c4cbb373"
-  integrity sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==
-  dependencies:
-    minipass "^3.0.0"
-
-minipass-pipeline@^1.2.2:
-  version "1.2.4"
-  resolved "https://registry.yarnpkg.com/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz#68472f79711c084657c067c5c6ad93cddea8214c"
-  integrity sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==
-  dependencies:
-    minipass "^3.0.0"
-
 minipass@^2.3.3, minipass@^2.6.0, minipass@^2.8.6, minipass@^2.9.0:
   version "2.9.0"
   resolved "https://registry.yarnpkg.com/minipass/-/minipass-2.9.0.tgz#e713762e7d3e32fed803115cf93e04bca9fcc9a6"
@@ -6134,7 +6150,7 @@ minipass@^2.3.3, minipass@^2.6.0, minipass@^2.8.6, minipass@^2.9.0:
     safe-buffer "^5.1.2"
     yallist "^3.0.0"
 
-minipass@^3.0.0, minipass@^3.1.1:
+minipass@^3.0.0:
   version "3.1.3"
   resolved "https://registry.yarnpkg.com/minipass/-/minipass-3.1.3.tgz#7d42ff1f39635482e15f9cdb53184deebd5815fd"
   integrity sha512-Mgd2GdMVzY+x3IJ+oHnVM+KG3lA5c8tnabyJKmHSaG2kAGpudxuOf8ToDkhumF7UzME7DecbQE9uOZhNm7PuJg==
@@ -6219,7 +6235,7 @@ mixin-deep@^1.2.0:
   dependencies:
     minimist "^1.2.5"
 
-mkdirp@^1.0.3, mkdirp@^1.0.4:
+mkdirp@^1.0.3:
   version "1.0.4"
   resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e"
   integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==
@@ -6401,7 +6417,7 @@ node-releases@^1.1.61:
   resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.64.tgz#71b4ae988e9b1dd7c1ffce58dd9e561752dfebc5"
   integrity sha512-Iec8O9166/x2HRMJyLLLWkd0sFFLrFNy+Xf+JQfSQsdBJzPcHpNl3JQ9gD4j+aJxmCa25jNsIbM4bmACtSbkSg==
 
-node-releases@^1.1.66:
+node-releases@^1.1.67:
   version "1.1.67"
   resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.67.tgz#28ebfcccd0baa6aad8e8d4d8fe4cbc49ae239c12"
   integrity sha512-V5QF9noGFl3EymEwUYzO+3NTDpGfQB4ve6Qfnzf3UNydMhjQRVPR1DZTuvWiLzaFJYw2fmDwAfnRNEVb64hSIg==
@@ -6819,6 +6835,16 @@ object.assign@^4.1.1:
     has-symbols "^1.0.1"
     object-keys "^1.1.1"
 
+object.assign@^4.1.2:
+  version "4.1.2"
+  resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.2.tgz#0ed54a342eceb37b38ff76eb831a0e788cb63940"
+  integrity sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==
+  dependencies:
+    call-bind "^1.0.0"
+    define-properties "^1.1.3"
+    has-symbols "^1.0.1"
+    object-keys "^1.1.1"
+
 object.entries@^1.1.2:
   version "1.1.2"
   resolved "https://registry.yarnpkg.com/object.entries/-/object.entries-1.1.2.tgz#bc73f00acb6b6bb16c203434b10f9a7e797d3add"
@@ -7867,10 +7893,10 @@ regexpu-core@^4.7.1:
     unicode-match-property-ecmascript "^1.0.4"
     unicode-match-property-value-ecmascript "^1.2.0"
 
-register-service-worker@^1.7.1:
-  version "1.7.1"
-  resolved "https://registry.yarnpkg.com/register-service-worker/-/register-service-worker-1.7.1.tgz#6308347ac6c0af0f6c0b22ea5d59d25e836bc932"
-  integrity sha512-IdTfUZ4u8iJL8o1w8es8l6UMGPmkwHolUdT+UmM1UypC80IB4KbpuIlvwWVj8UDS7eJwkEYRcKRgfRX+oTmJsw==
+register-service-worker@^1.7.2:
+  version "1.7.2"
+  resolved "https://registry.yarnpkg.com/register-service-worker/-/register-service-worker-1.7.2.tgz#6516983e1ef790a98c4225af1216bc80941a4bd2"
+  integrity sha512-CiD3ZSanZqcMPRhtfct5K9f7i3OLCcBBWsJjLh1gW9RO/nS94sVzY59iS+fgYBOBqaBpf4EzfqUF3j9IG+xo8A==
 
 registry-auth-token@^3.0.1:
   version "3.4.0"
@@ -8011,7 +8037,7 @@ resolve-url@^0.2.1:
   resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a"
   integrity sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=
 
-resolve@^1.10.0, resolve@^1.10.1, resolve@^1.12.0, resolve@^1.13.1, resolve@^1.17.0, resolve@^1.3.2, resolve@^1.8.1:
+resolve@^1.10.0, resolve@^1.10.1, resolve@^1.12.0, resolve@^1.13.1, resolve@^1.17.0:
   version "1.17.0"
   resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.17.0.tgz#b25941b54968231cc2d1bb76a79cb7f2c0bf8444"
   integrity sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==
@@ -8233,6 +8259,13 @@ semver@^7.2.1, semver@^7.3.2:
   resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.2.tgz#604962b052b81ed0786aae84389ffba70ffd3938"
   integrity sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ==
 
+semver@^7.3.4:
+  version "7.3.4"
+  resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.4.tgz#27aaa7d2e4ca76452f98d3add093a72c943edc97"
+  integrity sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw==
+  dependencies:
+    lru-cache "^6.0.0"
+
 semver@~5.3.0:
   version "5.3.0"
   resolved "https://registry.yarnpkg.com/semver/-/semver-5.3.0.tgz#9b2ce5d3de02d17c6012ad326aa6b4d0cf54f94f"
@@ -8367,15 +8400,6 @@ slash@^3.0.0:
   resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634"
   integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==
 
-slice-ansi@^2.1.0:
-  version "2.1.0"
-  resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-2.1.0.tgz#cacd7693461a637a5788d92a7dd4fba068e81636"
-  integrity sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ==
-  dependencies:
-    ansi-styles "^3.2.0"
-    astral-regex "^1.0.0"
-    is-fullwidth-code-point "^2.0.0"
-
 slice-ansi@^3.0.0:
   version "3.0.0"
   resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-3.0.0.tgz#31ddc10930a1b7e0b67b08c96c2f49b77a789787"
@@ -8505,10 +8529,10 @@ sorted-union-stream@~2.1.3:
     from2 "^1.3.0"
     stream-iterate "^1.1.0"
 
-sortpack@^2.1.10:
-  version "2.1.10"
-  resolved "https://registry.yarnpkg.com/sortpack/-/sortpack-2.1.10.tgz#eb23257ff2db47954b0a7c3e4c84f71f1e32fac1"
-  integrity sha512-gJcTUZoIZoFDxyVhy+aJeUoknzs27K+EAuO+gTkWqxLZuiQYlgL1yu7u0B9cqMqavZcFMXcpmPBgovsDJXe3Fw==
+sortpack@^2.1.11:
+  version "2.1.11"
+  resolved "https://registry.yarnpkg.com/sortpack/-/sortpack-2.1.11.tgz#afef2312f411f646707150b6659b4fb3499a7422"
+  integrity sha512-gm8tL0UDg4lO5KM6m/lWxoXYSNEJT4jEXuvxaqsc6kScOqJECU7tcmxWhgUY1tiQFhOO6jY4RaYUn2HPmZVSYA==
 
 source-list-map@^2.0.0, source-list-map@^2.0.1:
   version "2.0.1"
@@ -8656,13 +8680,6 @@ ssri@^6.0.0, ssri@^6.0.1:
   dependencies:
     figgy-pudding "^3.5.1"
 
-ssri@^8.0.0:
-  version "8.0.0"
-  resolved "https://registry.yarnpkg.com/ssri/-/ssri-8.0.0.tgz#79ca74e21f8ceaeddfcb4b90143c458b8d988808"
-  integrity sha512-aq/pz989nxVYwn16Tsbj1TqFpD5LLrQxHf5zaHuieFV+R0Bbr4y8qUsOA45hXT/N4/9UNXTarBjnjVmjSOVaAA==
-  dependencies:
-    minipass "^3.1.1"
-
 static-extend@^0.1.1:
   version "0.1.2"
   resolved "https://registry.yarnpkg.com/static-extend/-/static-extend-0.1.2.tgz#60809c39cbff55337226fd5e0b520f341f1fb5c6"
@@ -8921,15 +8938,15 @@ table-layout@^1.0.0:
     typical "^5.2.0"
     wordwrapjs "^4.0.0"
 
-table@^5.2.3:
-  version "5.4.6"
-  resolved "https://registry.yarnpkg.com/table/-/table-5.4.6.tgz#1292d19500ce3f86053b05f0e8e7e4a3bb21079e"
-  integrity sha512-wmEc8m4fjnob4gt5riFRtTu/6+4rSe12TpAELNSqHMfF3IqnA+CH37USM6/YR3qRZv7e56kAEAtd6nKZaxe0Ug==
+table@^6.0.4:
+  version "6.0.4"
+  resolved "https://registry.yarnpkg.com/table/-/table-6.0.4.tgz#c523dd182177e926c723eb20e1b341238188aa0d"
+  integrity sha512-sBT4xRLdALd+NFBvwOz8bw4b15htyythha+q+DVZqy2RS08PPC8O2sZFgJYEY7bJvbCFKccs+WIZ/cd+xxTWCw==
   dependencies:
-    ajv "^6.10.2"
-    lodash "^4.17.14"
-    slice-ansi "^2.1.0"
-    string-width "^3.0.0"
+    ajv "^6.12.4"
+    lodash "^4.17.20"
+    slice-ansi "^4.0.0"
+    string-width "^4.2.0"
 
 tapable@^2.0.0:
   version "2.0.0"
@@ -9203,10 +9220,10 @@ typedarray@^0.0.6:
   resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777"
   integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=
 
-typescript@^4.1.2:
-  version "4.1.2"
-  resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.1.2.tgz#6369ef22516fe5e10304aae5a5c4862db55380e9"
-  integrity sha512-thGloWsGH3SOxv1SoY7QojKi0tc+8FnOmiarEGMbd/lar7QOEd3hvlx3Fp5y6FlDUGl9L+pd4n2e+oToGMmhRQ==
+typescript@^4.1.3:
+  version "4.1.3"
+  resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.1.3.tgz#519d582bd94cba0cf8934c7d8e8467e473f53bb7"
+  integrity sha512-B3ZIOf1IKeH2ixgHhj6la6xdwR9QrLC5d1VKeCSY4tvkqhF2eqd9O7txNlS0PO3GrBAFIdr3L1ndNwteUbZLYg==
 
 typical@^5.0.0, typical@^5.2.0:
   version "5.2.0"
@@ -9553,7 +9570,7 @@ webpack-node-externals@^2.5.2:
   resolved "https://registry.yarnpkg.com/webpack-node-externals/-/webpack-node-externals-2.5.2.tgz#178e017a24fec6015bc9e672c77958a6afac861d"
   integrity sha512-aHdl/y2N7PW2Sx7K+r3AxpJO+aDMcYzMQd60Qxefq3+EwhewSbTBqNumOsCE1JsCUNoyfGj5465N0sSf6hc/5w==
 
-webpack-sources@^1.1.0, webpack-sources@^1.4.3:
+webpack-sources@^1.1.0:
   version "1.4.3"
   resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-1.4.3.tgz#eedd8ec0b928fbf1cbfe994e22d2d890f330a933"
   integrity sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ==
@@ -9569,17 +9586,17 @@ webpack-sources@^2.1.1:
     source-list-map "^2.0.1"
     source-map "^0.6.1"
 
-webpack@5.10.0:
-  version "5.10.0"
-  resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.10.0.tgz#6f77c31522a2c525152d9c344f9765d168b3df08"
-  integrity sha512-P0bHAXmIz0zsNcHNLqFmLY1ZtrT+jtBr7FqpuDtA2o7GiHC+zBsfhgK7SmJ1HG7BAEb3G9JoMdSVi7mEDvG3Zg==
+webpack@5.11.0:
+  version "5.11.0"
+  resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.11.0.tgz#1647abc060441d86d01d8835b8f0fc1dae2fe76f"
+  integrity sha512-ubWv7iP54RqAC/VjixgpnLLogCFbAfSOREcSWnnOlZEU8GICC5eKmJSu6YEnph2N2amKqY9rvxSwgyHxVqpaRw==
   dependencies:
     "@types/eslint-scope" "^3.7.0"
     "@types/estree" "^0.0.45"
-    "@webassemblyjs/ast" "1.9.0"
-    "@webassemblyjs/helper-module-context" "1.9.0"
-    "@webassemblyjs/wasm-edit" "1.9.0"
-    "@webassemblyjs/wasm-parser" "1.9.0"
+    "@webassemblyjs/ast" "1.9.1"
+    "@webassemblyjs/helper-module-context" "1.9.1"
+    "@webassemblyjs/wasm-edit" "1.9.1"
+    "@webassemblyjs/wasm-parser" "1.9.1"
     acorn "^8.0.4"
     browserslist "^4.14.5"
     chrome-trace-event "^1.0.2"