]> Untitled Git - lemmy-ui.git/commitdiff
Removing monads. Fixes #884 (#886)
authorDessalines <dessalines@users.noreply.github.com>
Wed, 4 Jan 2023 16:56:24 +0000 (11:56 -0500)
committerGitHub <noreply@github.com>
Wed, 4 Jan 2023 16:56:24 +0000 (11:56 -0500)
* Removing monads. Fixes #884

* Fixing post fetching.

* Dont show not_logged_in error for navbar.

* Adding the lemmy-js-client RC.

* Fixing registration application mode

66 files changed:
lemmy-translations
package.json
src/client/index.tsx
src/server/index.tsx
src/shared/components/app/app.tsx
src/shared/components/app/footer.tsx
src/shared/components/app/navbar.tsx
src/shared/components/app/theme.tsx
src/shared/components/comment/comment-form.tsx
src/shared/components/comment/comment-node.tsx
src/shared/components/comment/comment-nodes.tsx
src/shared/components/comment/comment-report.tsx
src/shared/components/common/banner-icon-header.tsx
src/shared/components/common/comment-sort-select.tsx
src/shared/components/common/data-type-select.tsx
src/shared/components/common/html-tags.tsx
src/shared/components/common/image-upload-form.tsx
src/shared/components/common/language-select.tsx
src/shared/components/common/listing-type-select.tsx
src/shared/components/common/markdown-textarea.tsx
src/shared/components/common/moment-time.tsx
src/shared/components/common/registration-application.tsx
src/shared/components/common/sort-select.tsx
src/shared/components/community/communities.tsx
src/shared/components/community/community-form.tsx
src/shared/components/community/community-link.tsx
src/shared/components/community/community.tsx
src/shared/components/community/create-community.tsx
src/shared/components/community/sidebar.tsx
src/shared/components/home/admin-settings.tsx
src/shared/components/home/home.tsx
src/shared/components/home/instances.tsx
src/shared/components/home/legal.tsx
src/shared/components/home/login.tsx
src/shared/components/home/setup.tsx
src/shared/components/home/signup.tsx
src/shared/components/home/site-form.tsx
src/shared/components/home/site-sidebar.tsx
src/shared/components/modlog.tsx
src/shared/components/person/inbox.tsx
src/shared/components/person/password-change.tsx
src/shared/components/person/person-details.tsx
src/shared/components/person/person-listing.tsx
src/shared/components/person/profile.tsx
src/shared/components/person/registration-applications.tsx
src/shared/components/person/reports.tsx
src/shared/components/person/settings.tsx
src/shared/components/person/verify-email.tsx
src/shared/components/post/create-post.tsx
src/shared/components/post/metadata-card.tsx
src/shared/components/post/post-form.tsx
src/shared/components/post/post-listing.tsx
src/shared/components/post/post-listings.tsx
src/shared/components/post/post-report.tsx
src/shared/components/post/post.tsx
src/shared/components/private_message/create-private-message.tsx
src/shared/components/private_message/private-message-form.tsx
src/shared/components/private_message/private-message-report.tsx
src/shared/components/private_message/private-message.tsx
src/shared/components/search.tsx
src/shared/interfaces.ts
src/shared/routes.ts
src/shared/services/UserService.ts
src/shared/utils.ts
tsconfig.json
yarn.lock

index c97005696d132acf2cad3506df4f3c2256142349..975c922271f27920aef51a2c3ae9f24714c44004 160000 (submodule)
@@ -1 +1 @@
-Subproject commit c97005696d132acf2cad3506df4f3c2256142349
+Subproject commit 975c922271f27920aef51a2c3ae9f24714c44004
index 54732becf0ed5263a330a6664ea6fdd6129efbe4..ff20ab60cd58a957820ba8e0142ee976e9ff4eba 100644 (file)
     "@babel/preset-env": "7.19.3",
     "@babel/preset-typescript": "^7.18.6",
     "@babel/runtime": "^7.18.9",
-    "@sniptt/monads": "^0.5.10",
     "autosize": "^5.0.1",
     "babel-loader": "^8.2.5",
     "babel-plugin-inferno": "^6.5.0",
     "check-password-strength": "^2.0.7",
     "choices.js": "^10.1.0",
-    "class-transformer": "^0.5.1",
     "classnames": "^2.3.1",
     "clean-webpack-plugin": "^4.0.0",
     "copy-webpack-plugin": "^11.0.0",
@@ -47,7 +45,7 @@
     "inferno-server": "^8.0.5",
     "isomorphic-cookie": "^1.2.4",
     "jwt-decode": "^3.1.2",
-    "lemmy-js-client": "0.17.0-rc.57",
+    "lemmy-js-client": "0.17.0-rc.61",
     "markdown-it": "^13.0.1",
     "markdown-it-container": "^3.0.0",
     "markdown-it-footnote": "^3.0.3",
@@ -57,7 +55,6 @@
     "mini-css-extract-plugin": "^2.6.1",
     "moment": "^2.29.4",
     "node-fetch": "^2.6.1",
-    "reflect-metadata": "^0.1.13",
     "register-service-worker": "^1.7.2",
     "run-node-webpack-plugin": "^1.3.0",
     "rxjs": "^7.5.6",
index 3838dca74eb21d56934f1584a3cb3a59e9e9164d..a96e6bdec85b72bbb066efc3e325f1dc10ddfa3e 100644 (file)
@@ -1,10 +1,9 @@
 import { hydrate } from "inferno-hydrate";
 import { BrowserRouter } from "inferno-router";
-import { GetSiteResponse } from "lemmy-js-client";
 import { App } from "../shared/components/app/app";
-import { convertWindowJson, initializeSite } from "../shared/utils";
+import { initializeSite } from "../shared/utils";
 
-const site = convertWindowJson(GetSiteResponse, window.isoData.site_res);
+const site = window.isoData.site_res;
 initializeSite(site);
 
 const wrapper = (
@@ -13,4 +12,7 @@ const wrapper = (
   </BrowserRouter>
 );
 
-hydrate(wrapper, document.getElementById("root"));
+let root = document.getElementById("root");
+if (root) {
+  hydrate(wrapper, root);
+}
index 7d6200e89658682dca25f22a3bc90e6c844b5c49..048e702f97220d68f5a183cad943c52506648952 100644 (file)
@@ -1,5 +1,3 @@
-import { None, Option } from "@sniptt/monads";
-import { serialize as serializeO } from "class-transformer";
 import express from "express";
 import fs from "fs";
 import { IncomingHttpHeaders } from "http";
@@ -7,7 +5,7 @@ import { Helmet } from "inferno-helmet";
 import { matchPath, StaticRouter } from "inferno-router";
 import { renderToString } from "inferno-server";
 import IsomorphicCookie from "isomorphic-cookie";
-import { GetSite, GetSiteResponse, LemmyHttp, toOption } from "lemmy-js-client";
+import { GetSite, GetSiteResponse, LemmyHttp } from "lemmy-js-client";
 import path from "path";
 import process from "process";
 import serialize from "serialize-javascript";
@@ -114,11 +112,11 @@ server.get("/css/themelist", async (_req, res) => {
 // server.use(cookieParser());
 server.get("/*", async (req, res) => {
   try {
-    const activeRoute = routes.find(route => matchPath(req.path, route)) || {};
+    const activeRoute = routes.find(route => matchPath(req.path, route));
     const context = {} as any;
-    let auth: Option<string> = toOption(IsomorphicCookie.load("jwt", req));
+    let auth: string | undefined = IsomorphicCookie.load("jwt", req);
 
-    let getSiteForm = new GetSite({ auth });
+    let getSiteForm: GetSite = { auth };
 
     let promises: Promise<any>[] = [];
 
@@ -138,14 +136,14 @@ server.get("/*", async (req, res) => {
       console.error(
         "Incorrect JWT token, skipping auth so frontend can remove jwt cookie"
       );
-      getSiteForm.auth = None;
-      initialFetchReq.auth = None;
+      getSiteForm.auth = undefined;
+      initialFetchReq.auth = undefined;
       try_site = await initialFetchReq.client.getSite(getSiteForm);
     }
     let site: GetSiteResponse = try_site;
     initializeSite(site);
 
-    if (activeRoute.fetchInitialData) {
+    if (activeRoute?.fetchInitialData) {
       promises.push(...activeRoute.fetchInitialData(initialFetchReq));
     }
 
@@ -193,7 +191,7 @@ server.get("/*", async (req, res) => {
            <!DOCTYPE html>
            <html ${helmet.htmlAttributes.toString()} lang="en">
            <head>
-           <script>window.isoData = ${serializeO(isoData)}</script>
+           <script>window.isoData = ${JSON.stringify(isoData)}</script>
            <script>window.lemmyConfig = ${serialize(config)}</script>
 
            <!-- A remote debugging utility for mobile -->
@@ -246,14 +244,17 @@ server.listen(Number(port), hostname, () => {
 function setForwardedHeaders(headers: IncomingHttpHeaders): {
   [key: string]: string;
 } {
-  let out = {
-    host: headers.host,
-  };
-  if (headers["x-real-ip"]) {
-    out["x-real-ip"] = headers["x-real-ip"];
+  let out: { [key: string]: string } = {};
+  if (headers.host) {
+    out.host = headers.host;
   }
-  if (headers["x-forwarded-for"]) {
-    out["x-forwarded-for"] = headers["x-forwarded-for"];
+  let realIp = headers["x-real-ip"];
+  if (realIp) {
+    out["x-real-ip"] = realIp as string;
+  }
+  let forwardedFor = headers["x-forwarded-for"];
+  if (forwardedFor) {
+    out["x-forwarded-for"] = forwardedFor as string;
   }
 
   return out;
index a788636193bbb5f2c56dd064e0e546bfb1e7c807..45c67b3294729eac5b8a3e1272267876d8a90c98 100644 (file)
@@ -19,37 +19,37 @@ export class App extends Component<any, any> {
   render() {
     let siteRes = this.isoData.site_res;
     let siteView = siteRes.site_view;
+    let icon = siteView.site.icon;
 
     return (
       <>
         <Provider i18next={i18n}>
           <div id="app">
             <Theme defaultTheme={siteView.local_site.default_theme} />
-            {siteView.site.icon.match({
-              some: icon => (
-                <Helmet>
-                  <link
-                    id="favicon"
-                    rel="shortcut icon"
-                    type="image/x-icon"
-                    href={icon || favIconUrl}
-                  />
-                  <link rel="apple-touch-icon" href={icon || favIconPngUrl} />
-                </Helmet>
-              ),
-              none: <></>,
-            })}
+            {icon && (
+              <Helmet>
+                <link
+                  id="favicon"
+                  rel="shortcut icon"
+                  type="image/x-icon"
+                  href={icon || favIconUrl}
+                />
+                <link rel="apple-touch-icon" href={icon || favIconPngUrl} />
+              </Helmet>
+            )}
             <Navbar siteRes={siteRes} />
             <div className="mt-4 p-0 fl-1">
               <Switch>
-                {routes.map(({ path, exact, component: C, ...rest }) => (
-                  <Route
-                    key={path}
-                    path={path}
-                    exact={exact}
-                    render={props => <C {...props} {...rest} />}
-                  />
-                ))}
+                {routes.map(
+                  ({ path, exact, component: Component, ...rest }) => (
+                    <Route
+                      key={path}
+                      path={path}
+                      exact={exact}
+                      render={props => <Component {...props} {...rest} />}
+                    />
+                  )
+                )}
                 <Route render={props => <NoMatch {...props} />} />
               </Switch>
             </div>
index 8a7f3bd9a8ba614707be548485657e5efb7d1f71..a62a8ace7504a4ceaf9f80fb3da8b5b1bf93fdd7 100644 (file)
@@ -32,7 +32,7 @@ export class Footer extends Component<FooterProps, any> {
                 {i18n.t("modlog")}
               </NavLink>
             </li>
-            {this.props.site.site_view.local_site.legal_information.isSome() && (
+            {this.props.site.site_view.local_site.legal_information && (
               <li className="nav-item">
                 <NavLink className="nav-link" to="/legal">
                   {i18n.t("legal_information")}
index 06da94dfc2514e4bedc13181e08c31231c5c524e..d758af60fea3ab40b6e17296e862318dd351d421 100644 (file)
@@ -1,4 +1,3 @@
-import { None } from "@sniptt/monads";
 import { Component, createRef, linkEvent, RefObject } from "inferno";
 import { NavLink } from "inferno-router";
 import {
@@ -20,10 +19,10 @@ import { i18n } from "../../i18next";
 import { UserService, WebSocketService } from "../../services";
 import {
   amAdmin,
-  auth,
   canCreateCommunity,
   donateLemmyUrl,
   isBrowser,
+  myAuth,
   notifyComment,
   notifyPrivateMessage,
   numToSI,
@@ -57,7 +56,7 @@ export class Navbar extends Component<NavbarProps, NavbarState> {
   private unreadReportCountSub: Subscription;
   private unreadApplicationCountSub: Subscription;
   private searchTextField: RefObject<HTMLInputElement>;
-  emptyState: NavbarState = {
+  state: NavbarState = {
     unreadInboxCount: 0,
     unreadReportCount: 0,
     unreadApplicationCount: 0,
@@ -70,7 +69,6 @@ export class Navbar extends Component<NavbarProps, NavbarState> {
 
   constructor(props: any, context: any) {
     super(props, context);
-    this.state = this.emptyState;
 
     this.parseMessage = this.parseMessage.bind(this);
     this.subscription = wsSubscribe(this.parseMessage);
@@ -82,11 +80,12 @@ export class Navbar extends Component<NavbarProps, NavbarState> {
       this.searchTextField = createRef();
 
       // On the first load, check the unreads
-      if (UserService.Instance.myUserInfo.isSome()) {
+      let auth = myAuth(false);
+      if (auth && UserService.Instance.myUserInfo) {
         this.requestNotificationPermission();
         WebSocketService.Instance.send(
           wsClient.userJoin({
-            auth: auth().unwrap(),
+            auth,
           })
         );
 
@@ -143,22 +142,22 @@ export class Navbar extends Component<NavbarProps, NavbarState> {
   // TODO class active corresponding to current page
   navbar() {
     let siteView = this.props.siteRes.site_view;
+    let person = UserService.Instance.myUserInfo?.local_user_view.person;
     return (
       <nav className="navbar navbar-expand-md navbar-light shadow-sm p-0 px-3">
         <div className="container-lg">
           <NavLink
             to="/"
             onMouseUp={linkEvent(this, this.handleHideExpandNavbar)}
-            title={siteView.site.description.unwrapOr(siteView.site.name)}
+            title={siteView.site.description ?? siteView.site.name}
             className="d-flex align-items-center navbar-brand mr-md-3"
           >
-            {siteView.site.icon.match({
-              some: icon => showAvatars() && <PictrsImage src={icon} icon />,
-              none: <></>,
-            })}
+            {siteView.site.icon && showAvatars() && (
+              <PictrsImage src={siteView.site.icon} icon />
+            )}
             {siteView.site.name}
           </NavLink>
-          {UserService.Instance.myUserInfo.isSome() && (
+          {UserService.Instance.myUserInfo && (
             <>
               <ul className="navbar-nav ml-auto">
                 <li className="nav-item">
@@ -341,7 +340,7 @@ export class Navbar extends Component<NavbarProps, NavbarState> {
                 </li>
               </ul>
             )}
-            {UserService.Instance.myUserInfo.isSome() ? (
+            {UserService.Instance.myUserInfo ? (
               <>
                 <ul className="navbar-nav my-2">
                   <li className="nav-item">
@@ -409,81 +408,69 @@ export class Navbar extends Component<NavbarProps, NavbarState> {
                     </li>
                   </ul>
                 )}
-                {UserService.Instance.myUserInfo
-                  .map(m => m.local_user_view.person)
-                  .match({
-                    some: person => (
-                      <ul className="navbar-nav">
-                        <li className="nav-item dropdown">
-                          <button
-                            className="nav-link btn btn-link dropdown-toggle"
-                            onClick={linkEvent(this, this.handleToggleDropdown)}
-                            id="navbarDropdown"
-                            role="button"
-                            aria-expanded="false"
-                          >
-                            <span>
-                              {showAvatars() &&
-                                person.avatar.match({
-                                  some: avatar => (
-                                    <PictrsImage src={avatar} icon />
-                                  ),
-                                  none: <></>,
-                                })}
-                              {person.display_name.unwrapOr(person.name)}
-                            </span>
-                          </button>
-                          {this.state.showDropdown && (
-                            <div
-                              className="dropdown-content"
-                              onMouseLeave={linkEvent(
-                                this,
-                                this.handleToggleDropdown
-                              )}
-                            >
-                              <li className="nav-item">
-                                <NavLink
-                                  to={`/u/${person.name}`}
-                                  className="nav-link"
-                                  title={i18n.t("profile")}
-                                >
-                                  <Icon icon="user" classes="mr-1" />
-                                  {i18n.t("profile")}
-                                </NavLink>
-                              </li>
-                              <li className="nav-item">
-                                <NavLink
-                                  to="/settings"
-                                  className="nav-link"
-                                  title={i18n.t("settings")}
-                                >
-                                  <Icon icon="settings" classes="mr-1" />
-                                  {i18n.t("settings")}
-                                </NavLink>
-                              </li>
-                              <li>
-                                <hr className="dropdown-divider" />
-                              </li>
-                              <li className="nav-item">
-                                <button
-                                  className="nav-link btn btn-link"
-                                  onClick={linkEvent(
-                                    this,
-                                    this.handleLogoutClick
-                                  )}
-                                  title="test"
-                                >
-                                  <Icon icon="log-out" classes="mr-1" />
-                                  {i18n.t("logout")}
-                                </button>
-                              </li>
-                            </div>
+                {person && (
+                  <ul className="navbar-nav">
+                    <li className="nav-item dropdown">
+                      <button
+                        className="nav-link btn btn-link dropdown-toggle"
+                        onClick={linkEvent(this, this.handleToggleDropdown)}
+                        id="navbarDropdown"
+                        role="button"
+                        aria-expanded="false"
+                      >
+                        <span>
+                          {showAvatars() && person.avatar && (
+                            <PictrsImage src={person.avatar} icon />
+                          )}
+                          {person.display_name ?? person.name}
+                        </span>
+                      </button>
+                      {this.state.showDropdown && (
+                        <div
+                          className="dropdown-content"
+                          onMouseLeave={linkEvent(
+                            this,
+                            this.handleToggleDropdown
                           )}
-                        </li>
-                      </ul>
-                    ),
-                    none: <></>,
-                  })}
+                        >
+                          <li className="nav-item">
+                            <NavLink
+                              to={`/u/${person.name}`}
+                              className="nav-link"
+                              title={i18n.t("profile")}
+                            >
+                              <Icon icon="user" classes="mr-1" />
+                              {i18n.t("profile")}
+                            </NavLink>
+                          </li>
+                          <li className="nav-item">
+                            <NavLink
+                              to="/settings"
+                              className="nav-link"
+                              title={i18n.t("settings")}
+                            >
+                              <Icon icon="settings" classes="mr-1" />
+                              {i18n.t("settings")}
+                            </NavLink>
+                          </li>
+                          <li>
+                            <hr className="dropdown-divider" />
+                          </li>
+                          <li className="nav-item">
+                            <button
+                              className="nav-link btn btn-link"
+                              onClick={linkEvent(this, this.handleLogoutClick)}
+                              title="test"
+                            >
+                              <Icon icon="log-out" classes="mr-1" />
+                              {i18n.t("logout")}
+                            </button>
+                          </li>
+                        </div>
+                      )}
+                    </li>
+                  </ul>
+                )}
               </>
             ) : (
               <ul className="navbar-nav my-2">
@@ -516,11 +503,9 @@ export class Navbar extends Component<NavbarProps, NavbarState> {
   }
 
   get moderatesSomething(): boolean {
-    return (
-      amAdmin() ||
-      UserService.Instance.myUserInfo.map(m => m.moderates).unwrapOr([])
-        .length > 0
-    );
+    let mods = UserService.Instance.myUserInfo?.moderates;
+    let moderatesS = (mods && mods.length > 0) || false;
+    return amAdmin() || moderatesS;
   }
 
   handleToggleExpandNavbar(i: Navbar) {
@@ -544,9 +529,9 @@ export class Navbar extends Component<NavbarProps, NavbarState> {
     event.preventDefault();
     i.setState({ toggleSearch: true });
 
-    i.searchTextField.current.focus();
-    const offsetWidth = i.searchTextField.current.offsetWidth;
-    if (i.state.searchParam && offsetWidth > 100) {
+    i.searchTextField.current?.focus();
+    const offsetWidth = i.searchTextField.current?.offsetWidth;
+    if (i.state.searchParam && offsetWidth && offsetWidth > 100) {
       i.updateUrl();
     }
   }
@@ -576,109 +561,91 @@ export class Navbar extends Component<NavbarProps, NavbarState> {
       return;
     } else if (msg.reconnect) {
       console.log(i18n.t("websocket_reconnected"));
-      if (UserService.Instance.myUserInfo.isSome()) {
+      let auth = myAuth(false);
+      if (UserService.Instance.myUserInfo && auth) {
         WebSocketService.Instance.send(
           wsClient.userJoin({
-            auth: auth().unwrap(),
+            auth,
           })
         );
         this.fetchUnreads();
       }
     } else if (op == UserOperation.GetUnreadCount) {
-      let data = wsJsonToRes<GetUnreadCountResponse>(
-        msg,
-        GetUnreadCountResponse
-      );
+      let data = wsJsonToRes<GetUnreadCountResponse>(msg);
       this.setState({
         unreadInboxCount: data.replies + data.mentions + data.private_messages,
       });
       this.sendUnreadCount();
     } else if (op == UserOperation.GetReportCount) {
-      let data = wsJsonToRes<GetReportCountResponse>(
-        msg,
-        GetReportCountResponse
-      );
+      let data = wsJsonToRes<GetReportCountResponse>(msg);
       this.setState({
         unreadReportCount:
           data.post_reports +
           data.comment_reports +
-          data.private_message_reports.unwrapOr(0),
+          (data.private_message_reports ?? 0),
       });
       this.sendReportUnread();
     } else if (op == UserOperation.GetUnreadRegistrationApplicationCount) {
-      let data = wsJsonToRes<GetUnreadRegistrationApplicationCountResponse>(
-        msg,
-        GetUnreadRegistrationApplicationCountResponse
-      );
+      let data =
+        wsJsonToRes<GetUnreadRegistrationApplicationCountResponse>(msg);
       this.setState({ unreadApplicationCount: data.registration_applications });
       this.sendApplicationUnread();
     } else if (op == UserOperation.CreateComment) {
-      let data = wsJsonToRes<CommentResponse>(msg, CommentResponse);
-
-      UserService.Instance.myUserInfo.match({
-        some: mui => {
-          if (data.recipient_ids.includes(mui.local_user_view.local_user.id)) {
-            this.setState({
-              unreadInboxCount: this.state.unreadInboxCount + 1,
-            });
-            this.sendUnreadCount();
-            notifyComment(data.comment_view, this.context.router);
-          }
-        },
-        none: void 0,
-      });
+      let data = wsJsonToRes<CommentResponse>(msg);
+      let mui = UserService.Instance.myUserInfo;
+      if (
+        mui &&
+        data.recipient_ids.includes(mui.local_user_view.local_user.id)
+      ) {
+        this.setState({
+          unreadInboxCount: this.state.unreadInboxCount + 1,
+        });
+        this.sendUnreadCount();
+        notifyComment(data.comment_view, this.context.router);
+      }
     } else if (op == UserOperation.CreatePrivateMessage) {
-      let data = wsJsonToRes<PrivateMessageResponse>(
-        msg,
-        PrivateMessageResponse
-      );
-
-      UserService.Instance.myUserInfo.match({
-        some: mui => {
-          if (
-            data.private_message_view.recipient.id ==
-            mui.local_user_view.person.id
-          ) {
-            this.setState({
-              unreadInboxCount: this.state.unreadInboxCount + 1,
-            });
-            this.sendUnreadCount();
-            notifyPrivateMessage(
-              data.private_message_view,
-              this.context.router
-            );
-          }
-        },
-        none: void 0,
-      });
+      let data = wsJsonToRes<PrivateMessageResponse>(msg);
+
+      if (
+        data.private_message_view.recipient.id ==
+        UserService.Instance.myUserInfo?.local_user_view.person.id
+      ) {
+        this.setState({
+          unreadInboxCount: this.state.unreadInboxCount + 1,
+        });
+        this.sendUnreadCount();
+        notifyPrivateMessage(data.private_message_view, this.context.router);
+      }
     }
   }
 
   fetchUnreads() {
     console.log("Fetching inbox unreads...");
 
-    let unreadForm = new GetUnreadCount({
-      auth: auth().unwrap(),
-    });
-    WebSocketService.Instance.send(wsClient.getUnreadCount(unreadForm));
+    let auth = myAuth();
+    if (auth) {
+      let unreadForm: GetUnreadCount = {
+        auth,
+      };
+      WebSocketService.Instance.send(wsClient.getUnreadCount(unreadForm));
 
-    console.log("Fetching reports...");
+      console.log("Fetching reports...");
 
-    let reportCountForm = new GetReportCount({
-      community_id: None,
-      auth: auth().unwrap(),
-    });
-    WebSocketService.Instance.send(wsClient.getReportCount(reportCountForm));
+      let reportCountForm: GetReportCount = {
+        auth,
+      };
+      WebSocketService.Instance.send(wsClient.getReportCount(reportCountForm));
 
-    if (amAdmin()) {
-      console.log("Fetching applications...");
+      if (amAdmin()) {
+        console.log("Fetching applications...");
 
-      let applicationCountForm = new GetUnreadRegistrationApplicationCount({
-        auth: auth().unwrap(),
-      });
-      WebSocketService.Instance.send(
-        wsClient.getUnreadRegistrationApplicationCount(applicationCountForm)
-      );
+        let applicationCountForm: GetUnreadRegistrationApplicationCount = {
+          auth,
+        };
+        WebSocketService.Instance.send(
+          wsClient.getUnreadRegistrationApplicationCount(applicationCountForm)
+        );
+      }
     }
   }
 
@@ -703,7 +670,7 @@ export class Navbar extends Component<NavbarProps, NavbarState> {
   }
 
   requestNotificationPermission() {
-    if (UserService.Instance.myUserInfo.isSome()) {
+    if (UserService.Instance.myUserInfo) {
       document.addEventListener("DOMContentLoaded", function () {
         if (!Notification) {
           toast(i18n.t("notifications_error"), "danger");
index 0f2a08302c2eab94ba8ab50b8e0534c236d4e036..4510fe2c458fbc0339216bbd03ebdce97e14a8dc 100644 (file)
@@ -9,19 +9,15 @@ interface Props {
 export class Theme extends Component<Props> {
   render() {
     let user = UserService.Instance.myUserInfo;
-    let hasTheme = user
-      .map(m => m.local_user_view.local_user.theme !== "browser")
-      .unwrapOr(false);
+    let hasTheme = user?.local_user_view.local_user.theme !== "browser";
 
-    if (hasTheme) {
+    if (user && hasTheme) {
       return (
         <Helmet>
           <link
             rel="stylesheet"
             type="text/css"
-            href={`/css/themes/${
-              user.unwrap().local_user_view.local_user.theme
-            }.css`}
+            href={`/css/themes/${user.local_user_view.local_user.theme}.css`}
           />
         </Helmet>
       );
index a74cc8be1d96387bbbd43960cca59b34cd57f2e2..40650db92e5a419501ad91fc3a7609f7bfabb0c8 100644 (file)
@@ -1,4 +1,3 @@
-import { Either, None, Option, Some } from "@sniptt/monads";
 import { Component } from "inferno";
 import { T } from "inferno-i18next-dess";
 import { Link } from "inferno-router";
@@ -16,8 +15,8 @@ import { Subscription } from "rxjs";
 import { i18n } from "../../i18next";
 import { UserService, WebSocketService } from "../../services";
 import {
-  auth,
   capitalizeFirstLetter,
+  myAuth,
   myFirstDiscussionLanguageId,
   wsClient,
   wsSubscribe,
@@ -29,7 +28,7 @@ interface CommentFormProps {
   /**
    * Can either be the parent, or the editable comment. The right side is a postId.
    */
-  node: Either<CommentNodeI, number>;
+  node: CommentNodeI | number;
   edit?: boolean;
   disabled?: boolean;
   focus?: boolean;
@@ -41,19 +40,19 @@ interface CommentFormProps {
 interface CommentFormState {
   buttonTitle: string;
   finished: boolean;
-  formId: Option<string>;
+  formId?: string;
 }
 
 export class CommentForm extends Component<CommentFormProps, CommentFormState> {
-  private subscription: Subscription;
-  private emptyState: CommentFormState = {
-    buttonTitle: this.props.node.isRight()
-      ? capitalizeFirstLetter(i18n.t("post"))
-      : this.props.edit
-      ? capitalizeFirstLetter(i18n.t("save"))
-      : capitalizeFirstLetter(i18n.t("reply")),
+  private subscription?: Subscription;
+  state: CommentFormState = {
+    buttonTitle:
+      typeof this.props.node === "number"
+        ? capitalizeFirstLetter(i18n.t("post"))
+        : this.props.edit
+        ? capitalizeFirstLetter(i18n.t("save"))
+        : capitalizeFirstLetter(i18n.t("reply")),
     finished: false,
-    formId: None,
   };
 
   constructor(props: any, context: any) {
@@ -62,50 +61,46 @@ export class CommentForm extends Component<CommentFormProps, CommentFormState> {
     this.handleCommentSubmit = this.handleCommentSubmit.bind(this);
     this.handleReplyCancel = this.handleReplyCancel.bind(this);
 
-    this.state = this.emptyState;
-
     this.parseMessage = this.parseMessage.bind(this);
     this.subscription = wsSubscribe(this.parseMessage);
   }
 
   componentWillUnmount() {
-    this.subscription.unsubscribe();
+    this.subscription?.unsubscribe();
   }
 
   render() {
-    let initialContent = this.props.node.match({
-      left: node =>
-        this.props.edit ? Some(node.comment_view.comment.content) : None,
-      right: () => None,
-    });
-
-    let selectedLang = this.props.node
-      .left()
-      .map(n => n.comment_view.comment.language_id)
-      .or(
-        myFirstDiscussionLanguageId(
-          this.props.allLanguages,
-          this.props.siteLanguages,
-          UserService.Instance.myUserInfo
-        )
-      );
+    let initialContent =
+      typeof this.props.node !== "number"
+        ? this.props.edit
+          ? this.props.node.comment_view.comment.content
+          : undefined
+        : undefined;
+
+    let selectedLang =
+      typeof this.props.node !== "number"
+        ? this.props.node.comment_view.comment.language_id
+        : myFirstDiscussionLanguageId(
+            this.props.allLanguages,
+            this.props.siteLanguages,
+            UserService.Instance.myUserInfo
+          );
 
     return (
       <div className="mb-3">
-        {UserService.Instance.myUserInfo.isSome() ? (
+        {UserService.Instance.myUserInfo ? (
           <MarkdownTextArea
             initialContent={initialContent}
             initialLanguageId={selectedLang}
             showLanguage
-            buttonTitle={Some(this.state.buttonTitle)}
-            maxLength={None}
+            buttonTitle={this.state.buttonTitle}
             finished={this.state.finished}
-            replyType={this.props.node.isLeft()}
+            replyType={typeof this.props.node !== "number"}
             focus={this.props.focus}
             disabled={this.props.disabled}
             onSubmit={this.handleCommentSubmit}
             onReplyCancel={this.handleReplyCancel}
-            placeholder={Some(i18n.t("comment_here"))}
+            placeholder={i18n.t("comment_here")}
             allLanguages={this.props.allLanguages}
             siteLanguages={this.props.siteLanguages}
           />
@@ -125,55 +120,55 @@ export class CommentForm extends Component<CommentFormProps, CommentFormState> {
   }
 
   handleCommentSubmit(msg: {
-    val: Option<string>;
+    val: string;
     formId: string;
-    languageId: Option<number>;
+    languageId?: number;
   }) {
     let content = msg.val;
     let language_id = msg.languageId;
-    this.setState({ formId: Some(msg.formId) });
+    let node = this.props.node;
+
+    this.setState({ formId: msg.formId });
 
-    this.props.node.match({
-      left: node => {
+    let auth = myAuth();
+    if (auth) {
+      if (typeof node === "number") {
+        let postId = node;
+        let form: CreateComment = {
+          content,
+          form_id: this.state.formId,
+          post_id: postId,
+          language_id,
+          auth,
+        };
+        WebSocketService.Instance.send(wsClient.createComment(form));
+      } else {
         if (this.props.edit) {
-          let form = new EditComment({
+          let form: EditComment = {
             content,
-            distinguished: None,
             form_id: this.state.formId,
             comment_id: node.comment_view.comment.id,
             language_id,
-            auth: auth().unwrap(),
-          });
+            auth,
+          };
           WebSocketService.Instance.send(wsClient.editComment(form));
         } else {
-          let form = new CreateComment({
-            content: content.unwrap(),
+          let form: CreateComment = {
+            content,
             form_id: this.state.formId,
             post_id: node.comment_view.post.id,
-            parent_id: Some(node.comment_view.comment.id),
+            parent_id: node.comment_view.comment.id,
             language_id,
-            auth: auth().unwrap(),
-          });
+            auth,
+          };
           WebSocketService.Instance.send(wsClient.createComment(form));
         }
-      },
-      right: postId => {
-        let form = new CreateComment({
-          content: content.unwrap(),
-          form_id: this.state.formId,
-          post_id: postId,
-          parent_id: None,
-          language_id,
-          auth: auth().unwrap(),
-        });
-        WebSocketService.Instance.send(wsClient.createComment(form));
-      },
-    });
-    this.setState(this.state);
+      }
+    }
   }
 
   handleReplyCancel() {
-    this.props.onReplyCancel();
+    this.props.onReplyCancel?.();
   }
 
   parseMessage(msg: any) {
@@ -181,15 +176,15 @@ export class CommentForm extends Component<CommentFormProps, CommentFormState> {
     console.log(msg);
 
     // Only do the showing and hiding if logged in
-    if (UserService.Instance.myUserInfo.isSome()) {
+    if (UserService.Instance.myUserInfo) {
       if (
         op == UserOperation.CreateComment ||
         op == UserOperation.EditComment
       ) {
-        let data = wsJsonToRes<CommentResponse>(msg, CommentResponse);
+        let data = wsJsonToRes<CommentResponse>(msg);
 
         // This only finishes this form, if the randomly generated form_id matches the one received
-        if (this.state.formId.unwrapOr("") == data.form_id.unwrapOr("")) {
+        if (this.state.formId == data.form_id) {
           this.setState({ finished: true });
 
           // Necessary because it broke tribute for some reason
index e37c68e799b0e01abc3559a5d53282eff326c8b4..50da45614ef9f9ba9b5043a1c1136e737ec1905b 100644 (file)
@@ -1,4 +1,3 @@
-import { Left, None, Option, Some } from "@sniptt/monads";
 import classNames from "classnames";
 import { Component, linkEvent } from "inferno";
 import { Link } from "inferno-router";
@@ -27,7 +26,6 @@ import {
   PurgePerson,
   RemoveComment,
   SaveComment,
-  toUndefined,
   TransferCommunity,
 } from "lemmy-js-client";
 import moment from "moment";
@@ -36,7 +34,6 @@ import { BanType, CommentViewType, PurgeType } from "../../interfaces";
 import { UserService, WebSocketService } from "../../services";
 import {
   amCommunityCreator,
-  auth,
   canAdmin,
   canMod,
   colorList,
@@ -47,6 +44,7 @@ import {
   isMod,
   mdToHtml,
   mdToHtmlNoImages,
+  myAuth,
   numToSI,
   setupTippy,
   showScores,
@@ -63,14 +61,14 @@ interface CommentNodeState {
   showReply: boolean;
   showEdit: boolean;
   showRemoveDialog: boolean;
-  removeReason: Option<string>;
+  removeReason?: string;
   showBanDialog: boolean;
   removeData: boolean;
-  banReason: Option<string>;
-  banExpireDays: Option<number>;
+  banReason?: string;
+  banExpireDays?: number;
   banType: BanType;
   showPurgeDialog: boolean;
-  purgeReason: Option<string>;
+  purgeReason?: string;
   purgeType: PurgeType;
   purgeLoading: boolean;
   showConfirmTransferSite: boolean;
@@ -81,8 +79,8 @@ interface CommentNodeState {
   viewSource: boolean;
   showAdvanced: boolean;
   showReportDialog: boolean;
-  reportReason: string;
-  my_vote: Option<number>;
+  reportReason?: string;
+  my_vote?: number;
   score: number;
   upvotes: number;
   downvotes: number;
@@ -92,8 +90,8 @@ interface CommentNodeState {
 
 interface CommentNodeProps {
   node: CommentNodeI;
-  moderators: Option<CommunityModeratorView[]>;
-  admins: Option<PersonViewSafe[]>;
+  moderators?: CommunityModeratorView[];
+  admins?: PersonViewSafe[];
   noBorder?: boolean;
   noIndent?: boolean;
   viewOnly?: boolean;
@@ -101,7 +99,7 @@ interface CommentNodeProps {
   markable?: boolean;
   showContext?: boolean;
   showCommunity?: boolean;
-  enableDownvotes: boolean;
+  enableDownvotes?: boolean;
   viewType: CommentViewType;
   allLanguages: Language[];
   siteLanguages: number[];
@@ -109,19 +107,15 @@ interface CommentNodeProps {
 }
 
 export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
-  private emptyState: CommentNodeState = {
+  state: CommentNodeState = {
     showReply: false,
     showEdit: false,
     showRemoveDialog: false,
-    removeReason: None,
     showBanDialog: false,
     removeData: false,
-    banReason: None,
-    banExpireDays: None,
     banType: BanType.Community,
     showPurgeDialog: false,
     purgeLoading: false,
-    purgeReason: None,
     purgeType: PurgeType.Person,
     collapsed: false,
     viewSource: false,
@@ -131,7 +125,6 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
     showConfirmAppointAsMod: false,
     showConfirmAppointAsAdmin: false,
     showReportDialog: false,
-    reportReason: null,
     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,
@@ -143,7 +136,6 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
   constructor(props: any, context: any) {
     super(props, context);
 
-    this.state = this.emptyState;
     this.handleReplyCancel = this.handleReplyCancel.bind(this);
     this.handleCommentUpvote = this.handleCommentUpvote.bind(this);
     this.handleCommentDownvote = this.handleCommentDownvote.bind(this);
@@ -166,37 +158,35 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
     let node = this.props.node;
     let cv = this.props.node.comment_view;
 
-    let purgeTypeText: string;
-    if (this.state.purgeType == PurgeType.Comment) {
-      purgeTypeText = i18n.t("purge_comment");
-    } else if (this.state.purgeType == PurgeType.Person) {
-      purgeTypeText = `${i18n.t("purge")} ${cv.creator.name}`;
-    }
+    let purgeTypeText =
+      this.state.purgeType == PurgeType.Comment
+        ? i18n.t("purge_comment")
+        : `${i18n.t("purge")} ${cv.creator.name}`;
 
     let canMod_ = canMod(
+      cv.creator.id,
       this.props.moderators,
-      this.props.admins,
-      cv.creator.id
+      this.props.admins
     );
     let canModOnSelf = canMod(
+      cv.creator.id,
       this.props.moderators,
       this.props.admins,
-      cv.creator.id,
       UserService.Instance.myUserInfo,
       true
     );
-    let canAdmin_ = canAdmin(this.props.admins, cv.creator.id);
+    let canAdmin_ = canAdmin(cv.creator.id, this.props.admins);
     let canAdminOnSelf = canAdmin(
-      this.props.admins,
       cv.creator.id,
+      this.props.admins,
       UserService.Instance.myUserInfo,
       true
     );
-    let isMod_ = isMod(this.props.moderators, cv.creator.id);
-    let isAdmin_ = isAdmin(this.props.admins, cv.creator.id);
+    let isMod_ = isMod(cv.creator.id, this.props.moderators);
+    let isAdmin_ = isAdmin(cv.creator.id, this.props.admins);
     let amCommunityCreator_ = amCommunityCreator(
-      this.props.moderators,
-      cv.creator.id
+      cv.creator.id,
+      this.props.moderators
     );
 
     let borderColor = this.props.node.depth
@@ -227,15 +217,15 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
               this.props.node.comment_view.comment.distinguished,
           })}
           style={
-            !this.props.noIndent &&
-            this.props.node.depth &&
-            `border-left: 2px ${borderColor} solid !important`
+            !this.props.noIndent && this.props.node.depth
+              ? `border-left: 2px ${borderColor} solid !important`
+              : ""
           }
         >
           <div
-            className={`${
-              !this.props.noIndent && this.props.node.depth && "ml-2"
-            }`}
+            className={classNames({
+              "ml-2": !this.props.noIndent && this.props.node.depth,
+            })}
           >
             <div className="d-flex flex-wrap align-items-center text-muted small">
               <span className="mr-2">
@@ -324,7 +314,7 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
             {/* end of user row */}
             {this.state.showEdit && (
               <CommentForm
-                node={Left(node)}
+                node={node}
                 edit
                 onReplyCancel={this.handleReplyCancel}
                 disabled={this.props.locked}
@@ -376,461 +366,454 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
                       )}
                     </button>
                   )}
-                  {UserService.Instance.myUserInfo.isSome() &&
-                    !this.props.viewOnly && (
-                      <>
+                  {UserService.Instance.myUserInfo && !this.props.viewOnly && (
+                    <>
+                      <button
+                        className={`btn btn-link btn-animate ${
+                          this.state.my_vote == 1 ? "text-info" : "text-muted"
+                        }`}
+                        onClick={this.handleCommentUpvote}
+                        data-tippy-content={i18n.t("upvote")}
+                        aria-label={i18n.t("upvote")}
+                      >
+                        <Icon icon="arrow-up1" classes="icon-inline" />
+                        {showScores() &&
+                          this.state.upvotes !== this.state.score && (
+                            <span className="ml-1">
+                              {numToSI(this.state.upvotes)}
+                            </span>
+                          )}
+                      </button>
+                      {this.props.enableDownvotes && (
                         <button
                           className={`btn btn-link btn-animate ${
-                            this.state.my_vote.unwrapOr(0) == 1
-                              ? "text-info"
+                            this.state.my_vote == -1
+                              ? "text-danger"
                               : "text-muted"
                           }`}
-                          onClick={this.handleCommentUpvote}
-                          data-tippy-content={i18n.t("upvote")}
-                          aria-label={i18n.t("upvote")}
+                          onClick={this.handleCommentDownvote}
+                          data-tippy-content={i18n.t("downvote")}
+                          aria-label={i18n.t("downvote")}
                         >
-                          <Icon icon="arrow-up1" classes="icon-inline" />
+                          <Icon icon="arrow-down1" classes="icon-inline" />
                           {showScores() &&
                             this.state.upvotes !== this.state.score && (
                               <span className="ml-1">
-                                {numToSI(this.state.upvotes)}
+                                {numToSI(this.state.downvotes)}
                               </span>
                             )}
                         </button>
-                        {this.props.enableDownvotes && (
-                          <button
-                            className={`btn btn-link btn-animate ${
-                              this.state.my_vote.unwrapOr(0) == -1
-                                ? "text-danger"
-                                : "text-muted"
-                            }`}
-                            onClick={this.handleCommentDownvote}
-                            data-tippy-content={i18n.t("downvote")}
-                            aria-label={i18n.t("downvote")}
-                          >
-                            <Icon icon="arrow-down1" classes="icon-inline" />
-                            {showScores() &&
-                              this.state.upvotes !== this.state.score && (
-                                <span className="ml-1">
-                                  {numToSI(this.state.downvotes)}
-                                </span>
-                              )}
-                          </button>
-                        )}
+                      )}
+                      <button
+                        className="btn btn-link btn-animate text-muted"
+                        onClick={linkEvent(this, this.handleReplyClick)}
+                        data-tippy-content={i18n.t("reply")}
+                        aria-label={i18n.t("reply")}
+                      >
+                        <Icon icon="reply1" classes="icon-inline" />
+                      </button>
+                      {!this.state.showAdvanced ? (
                         <button
                           className="btn btn-link btn-animate text-muted"
-                          onClick={linkEvent(this, this.handleReplyClick)}
-                          data-tippy-content={i18n.t("reply")}
-                          aria-label={i18n.t("reply")}
+                          onClick={linkEvent(this, this.handleShowAdvanced)}
+                          data-tippy-content={i18n.t("more")}
+                          aria-label={i18n.t("more")}
                         >
-                          <Icon icon="reply1" classes="icon-inline" />
+                          <Icon icon="more-vertical" classes="icon-inline" />
                         </button>
-                        {!this.state.showAdvanced ? (
+                      ) : (
+                        <>
+                          {!this.myComment && (
+                            <>
+                              <button className="btn btn-link btn-animate">
+                                <Link
+                                  className="text-muted"
+                                  to={`/create_private_message/recipient/${cv.creator.id}`}
+                                  title={i18n.t("message").toLowerCase()}
+                                >
+                                  <Icon icon="mail" />
+                                </Link>
+                              </button>
+                              <button
+                                className="btn btn-link btn-animate text-muted"
+                                onClick={linkEvent(
+                                  this,
+                                  this.handleShowReportDialog
+                                )}
+                                data-tippy-content={i18n.t(
+                                  "show_report_dialog"
+                                )}
+                                aria-label={i18n.t("show_report_dialog")}
+                              >
+                                <Icon icon="flag" />
+                              </button>
+                              <button
+                                className="btn btn-link btn-animate text-muted"
+                                onClick={linkEvent(
+                                  this,
+                                  this.handleBlockUserClick
+                                )}
+                                data-tippy-content={i18n.t("block_user")}
+                                aria-label={i18n.t("block_user")}
+                              >
+                                <Icon icon="slash" />
+                              </button>
+                            </>
+                          )}
                           <button
                             className="btn btn-link btn-animate text-muted"
-                            onClick={linkEvent(this, this.handleShowAdvanced)}
-                            data-tippy-content={i18n.t("more")}
-                            aria-label={i18n.t("more")}
+                            onClick={linkEvent(
+                              this,
+                              this.handleSaveCommentClick
+                            )}
+                            data-tippy-content={
+                              cv.saved ? i18n.t("unsave") : i18n.t("save")
+                            }
+                            aria-label={
+                              cv.saved ? i18n.t("unsave") : i18n.t("save")
+                            }
                           >
-                            <Icon icon="more-vertical" classes="icon-inline" />
+                            {this.state.saveLoading ? (
+                              this.loadingIcon
+                            ) : (
+                              <Icon
+                                icon="star"
+                                classes={`icon-inline ${
+                                  cv.saved && "text-warning"
+                                }`}
+                              />
+                            )}
                           </button>
-                        ) : (
-                          <>
-                            {!this.myComment && (
-                              <>
-                                <button className="btn btn-link btn-animate">
-                                  <Link
-                                    className="text-muted"
-                                    to={`/create_private_message/recipient/${cv.creator.id}`}
-                                    title={i18n.t("message").toLowerCase()}
-                                  >
-                                    <Icon icon="mail" />
-                                  </Link>
-                                </button>
-                                <button
-                                  className="btn btn-link btn-animate text-muted"
-                                  onClick={linkEvent(
-                                    this,
-                                    this.handleShowReportDialog
-                                  )}
-                                  data-tippy-content={i18n.t(
-                                    "show_report_dialog"
-                                  )}
-                                  aria-label={i18n.t("show_report_dialog")}
-                                >
-                                  <Icon icon="flag" />
-                                </button>
+                          <button
+                            className="btn btn-link btn-animate text-muted"
+                            onClick={linkEvent(this, this.handleViewSource)}
+                            data-tippy-content={i18n.t("view_source")}
+                            aria-label={i18n.t("view_source")}
+                          >
+                            <Icon
+                              icon="file-text"
+                              classes={`icon-inline ${
+                                this.state.viewSource && "text-success"
+                              }`}
+                            />
+                          </button>
+                          {this.myComment && (
+                            <>
+                              <button
+                                className="btn btn-link btn-animate text-muted"
+                                onClick={linkEvent(this, this.handleEditClick)}
+                                data-tippy-content={i18n.t("edit")}
+                                aria-label={i18n.t("edit")}
+                              >
+                                <Icon icon="edit" classes="icon-inline" />
+                              </button>
+                              <button
+                                className="btn btn-link btn-animate text-muted"
+                                onClick={linkEvent(
+                                  this,
+                                  this.handleDeleteClick
+                                )}
+                                data-tippy-content={
+                                  !cv.comment.deleted
+                                    ? i18n.t("delete")
+                                    : i18n.t("restore")
+                                }
+                                aria-label={
+                                  !cv.comment.deleted
+                                    ? i18n.t("delete")
+                                    : i18n.t("restore")
+                                }
+                              >
+                                <Icon
+                                  icon="trash"
+                                  classes={`icon-inline ${
+                                    cv.comment.deleted && "text-danger"
+                                  }`}
+                                />
+                              </button>
+
+                              {(canModOnSelf || canAdminOnSelf) && (
                                 <button
                                   className="btn btn-link btn-animate text-muted"
                                   onClick={linkEvent(
                                     this,
-                                    this.handleBlockUserClick
+                                    this.handleDistinguishClick
                                   )}
-                                  data-tippy-content={i18n.t("block_user")}
-                                  aria-label={i18n.t("block_user")}
+                                  data-tippy-content={
+                                    !cv.comment.distinguished
+                                      ? i18n.t("distinguish")
+                                      : i18n.t("undistinguish")
+                                  }
+                                  aria-label={
+                                    !cv.comment.distinguished
+                                      ? i18n.t("distinguish")
+                                      : i18n.t("undistinguish")
+                                  }
                                 >
-                                  <Icon icon="slash" />
+                                  <Icon
+                                    icon="shield"
+                                    classes={`icon-inline ${
+                                      cv.comment.distinguished && "text-danger"
+                                    }`}
+                                  />
                                 </button>
-                              </>
-                            )}
-                            <button
-                              className="btn btn-link btn-animate text-muted"
-                              onClick={linkEvent(
-                                this,
-                                this.handleSaveCommentClick
-                              )}
-                              data-tippy-content={
-                                cv.saved ? i18n.t("unsave") : i18n.t("save")
-                              }
-                              aria-label={
-                                cv.saved ? i18n.t("unsave") : i18n.t("save")
-                              }
-                            >
-                              {this.state.saveLoading ? (
-                                this.loadingIcon
-                              ) : (
-                                <Icon
-                                  icon="star"
-                                  classes={`icon-inline ${
-                                    cv.saved && "text-warning"
-                                  }`}
-                                />
                               )}
-                            </button>
-                            <button
-                              className="btn btn-link btn-animate text-muted"
-                              onClick={linkEvent(this, this.handleViewSource)}
-                              data-tippy-content={i18n.t("view_source")}
-                              aria-label={i18n.t("view_source")}
-                            >
-                              <Icon
-                                icon="file-text"
-                                classes={`icon-inline ${
-                                  this.state.viewSource && "text-success"
-                                }`}
-                              />
-                            </button>
-                            {this.myComment && (
-                              <>
+                            </>
+                          )}
+                          {/* Admins and mods can remove comments */}
+                          {(canMod_ || canAdmin_) && (
+                            <>
+                              {!cv.comment.removed ? (
                                 <button
                                   className="btn btn-link btn-animate text-muted"
                                   onClick={linkEvent(
                                     this,
-                                    this.handleEditClick
+                                    this.handleModRemoveShow
                                   )}
-                                  data-tippy-content={i18n.t("edit")}
-                                  aria-label={i18n.t("edit")}
+                                  aria-label={i18n.t("remove")}
                                 >
-                                  <Icon icon="edit" classes="icon-inline" />
+                                  {i18n.t("remove")}
                                 </button>
+                              ) : (
                                 <button
                                   className="btn btn-link btn-animate text-muted"
                                   onClick={linkEvent(
                                     this,
-                                    this.handleDeleteClick
+                                    this.handleModRemoveSubmit
                                   )}
-                                  data-tippy-content={
-                                    !cv.comment.deleted
-                                      ? i18n.t("delete")
-                                      : i18n.t("restore")
-                                  }
-                                  aria-label={
-                                    !cv.comment.deleted
-                                      ? i18n.t("delete")
-                                      : i18n.t("restore")
-                                  }
+                                  aria-label={i18n.t("restore")}
                                 >
-                                  <Icon
-                                    icon="trash"
-                                    classes={`icon-inline ${
-                                      cv.comment.deleted && "text-danger"
-                                    }`}
-                                  />
+                                  {i18n.t("restore")}
                                 </button>
-
-                                {(canModOnSelf || canAdminOnSelf) && (
+                              )}
+                            </>
+                          )}
+                          {/* Mods can ban from community, and appoint as mods to community */}
+                          {canMod_ && (
+                            <>
+                              {!isMod_ &&
+                                (!cv.creator_banned_from_community ? (
                                   <button
                                     className="btn btn-link btn-animate text-muted"
                                     onClick={linkEvent(
                                       this,
-                                      this.handleDistinguishClick
+                                      this.handleModBanFromCommunityShow
                                     )}
-                                    data-tippy-content={
-                                      !cv.comment.distinguished
-                                        ? i18n.t("distinguish")
-                                        : i18n.t("undistinguish")
-                                    }
-                                    aria-label={
-                                      !cv.comment.distinguished
-                                        ? i18n.t("distinguish")
-                                        : i18n.t("undistinguish")
-                                    }
+                                    aria-label={i18n.t("ban")}
                                   >
-                                    <Icon
-                                      icon="shield"
-                                      classes={`icon-inline ${
-                                        cv.comment.distinguished &&
-                                        "text-danger"
-                                      }`}
-                                    />
+                                    {i18n.t("ban")}
                                   </button>
-                                )}
-                              </>
-                            )}
-                            {/* Admins and mods can remove comments */}
-                            {(canMod_ || canAdmin_) && (
-                              <>
-                                {!cv.comment.removed ? (
+                                ) : (
                                   <button
                                     className="btn btn-link btn-animate text-muted"
                                     onClick={linkEvent(
                                       this,
-                                      this.handleModRemoveShow
+                                      this.handleModBanFromCommunitySubmit
                                     )}
-                                    aria-label={i18n.t("remove")}
+                                    aria-label={i18n.t("unban")}
                                   >
-                                    {i18n.t("remove")}
+                                    {i18n.t("unban")}
                                   </button>
-                                ) : (
+                                ))}
+                              {!cv.creator_banned_from_community &&
+                                (!this.state.showConfirmAppointAsMod ? (
                                   <button
                                     className="btn btn-link btn-animate text-muted"
                                     onClick={linkEvent(
                                       this,
-                                      this.handleModRemoveSubmit
+                                      this.handleShowConfirmAppointAsMod
                                     )}
-                                    aria-label={i18n.t("restore")}
+                                    aria-label={
+                                      isMod_
+                                        ? i18n.t("remove_as_mod")
+                                        : i18n.t("appoint_as_mod")
+                                    }
                                   >
-                                    {i18n.t("restore")}
+                                    {isMod_
+                                      ? i18n.t("remove_as_mod")
+                                      : i18n.t("appoint_as_mod")}
                                   </button>
-                                )}
-                              </>
-                            )}
-                            {/* Mods can ban from community, and appoint as mods to community */}
-                            {canMod_ && (
-                              <>
-                                {!isMod_ &&
-                                  (!cv.creator_banned_from_community ? (
+                                ) : (
+                                  <>
                                     <button
                                       className="btn btn-link btn-animate text-muted"
-                                      onClick={linkEvent(
-                                        this,
-                                        this.handleModBanFromCommunityShow
-                                      )}
-                                      aria-label={i18n.t("ban")}
+                                      aria-label={i18n.t("are_you_sure")}
                                     >
-                                      {i18n.t("ban")}
+                                      {i18n.t("are_you_sure")}
                                     </button>
-                                  ) : (
                                     <button
                                       className="btn btn-link btn-animate text-muted"
                                       onClick={linkEvent(
                                         this,
-                                        this.handleModBanFromCommunitySubmit
+                                        this.handleAddModToCommunity
                                       )}
-                                      aria-label={i18n.t("unban")}
+                                      aria-label={i18n.t("yes")}
                                     >
-                                      {i18n.t("unban")}
+                                      {i18n.t("yes")}
                                     </button>
-                                  ))}
-                                {!cv.creator_banned_from_community &&
-                                  (!this.state.showConfirmAppointAsMod ? (
                                     <button
                                       className="btn btn-link btn-animate text-muted"
                                       onClick={linkEvent(
                                         this,
-                                        this.handleShowConfirmAppointAsMod
+                                        this.handleCancelConfirmAppointAsMod
                                       )}
-                                      aria-label={
-                                        isMod_
-                                          ? i18n.t("remove_as_mod")
-                                          : i18n.t("appoint_as_mod")
-                                      }
+                                      aria-label={i18n.t("no")}
                                     >
-                                      {isMod_
-                                        ? i18n.t("remove_as_mod")
-                                        : i18n.t("appoint_as_mod")}
+                                      {i18n.t("no")}
                                     </button>
-                                  ) : (
-                                    <>
-                                      <button
-                                        className="btn btn-link btn-animate text-muted"
-                                        aria-label={i18n.t("are_you_sure")}
-                                      >
-                                        {i18n.t("are_you_sure")}
-                                      </button>
-                                      <button
-                                        className="btn btn-link btn-animate text-muted"
-                                        onClick={linkEvent(
-                                          this,
-                                          this.handleAddModToCommunity
-                                        )}
-                                        aria-label={i18n.t("yes")}
-                                      >
-                                        {i18n.t("yes")}
-                                      </button>
-                                      <button
-                                        className="btn btn-link btn-animate text-muted"
-                                        onClick={linkEvent(
-                                          this,
-                                          this.handleCancelConfirmAppointAsMod
-                                        )}
-                                        aria-label={i18n.t("no")}
-                                      >
-                                        {i18n.t("no")}
-                                      </button>
-                                    </>
-                                  ))}
-                              </>
-                            )}
-                            {/* Community creators and admins can transfer community to another mod */}
-                            {(amCommunityCreator_ || canAdmin_) &&
-                              isMod_ &&
-                              cv.creator.local &&
-                              (!this.state.showConfirmTransferCommunity ? (
+                                  </>
+                                ))}
+                            </>
+                          )}
+                          {/* Community creators and admins can transfer community to another mod */}
+                          {(amCommunityCreator_ || canAdmin_) &&
+                            isMod_ &&
+                            cv.creator.local &&
+                            (!this.state.showConfirmTransferCommunity ? (
+                              <button
+                                className="btn btn-link btn-animate text-muted"
+                                onClick={linkEvent(
+                                  this,
+                                  this.handleShowConfirmTransferCommunity
+                                )}
+                                aria-label={i18n.t("transfer_community")}
+                              >
+                                {i18n.t("transfer_community")}
+                              </button>
+                            ) : (
+                              <>
+                                <button
+                                  className="btn btn-link btn-animate text-muted"
+                                  aria-label={i18n.t("are_you_sure")}
+                                >
+                                  {i18n.t("are_you_sure")}
+                                </button>
                                 <button
                                   className="btn btn-link btn-animate text-muted"
                                   onClick={linkEvent(
                                     this,
-                                    this.handleShowConfirmTransferCommunity
+                                    this.handleTransferCommunity
                                   )}
-                                  aria-label={i18n.t("transfer_community")}
+                                  aria-label={i18n.t("yes")}
                                 >
-                                  {i18n.t("transfer_community")}
+                                  {i18n.t("yes")}
                                 </button>
-                              ) : (
+                                <button
+                                  className="btn btn-link btn-animate text-muted"
+                                  onClick={linkEvent(
+                                    this,
+                                    this
+                                      .handleCancelShowConfirmTransferCommunity
+                                  )}
+                                  aria-label={i18n.t("no")}
+                                >
+                                  {i18n.t("no")}
+                                </button>
+                              </>
+                            ))}
+                          {/* Admins can ban from all, and appoint other admins */}
+                          {canAdmin_ && (
+                            <>
+                              {!isAdmin_ && (
                                 <>
-                                  <button
-                                    className="btn btn-link btn-animate text-muted"
-                                    aria-label={i18n.t("are_you_sure")}
-                                  >
-                                    {i18n.t("are_you_sure")}
-                                  </button>
                                   <button
                                     className="btn btn-link btn-animate text-muted"
                                     onClick={linkEvent(
                                       this,
-                                      this.handleTransferCommunity
+                                      this.handlePurgePersonShow
                                     )}
-                                    aria-label={i18n.t("yes")}
+                                    aria-label={i18n.t("purge_user")}
                                   >
-                                    {i18n.t("yes")}
+                                    {i18n.t("purge_user")}
                                   </button>
                                   <button
                                     className="btn btn-link btn-animate text-muted"
                                     onClick={linkEvent(
                                       this,
-                                      this
-                                        .handleCancelShowConfirmTransferCommunity
+                                      this.handlePurgeCommentShow
                                     )}
-                                    aria-label={i18n.t("no")}
+                                    aria-label={i18n.t("purge_comment")}
                                   >
-                                    {i18n.t("no")}
+                                    {i18n.t("purge_comment")}
                                   </button>
-                                </>
-                              ))}
-                            {/* Admins can ban from all, and appoint other admins */}
-                            {canAdmin_ && (
-                              <>
-                                {!isAdmin_ && (
-                                  <>
+
+                                  {!isBanned(cv.creator) ? (
                                     <button
                                       className="btn btn-link btn-animate text-muted"
                                       onClick={linkEvent(
                                         this,
-                                        this.handlePurgePersonShow
+                                        this.handleModBanShow
                                       )}
-                                      aria-label={i18n.t("purge_user")}
+                                      aria-label={i18n.t("ban_from_site")}
                                     >
-                                      {i18n.t("purge_user")}
+                                      {i18n.t("ban_from_site")}
                                     </button>
+                                  ) : (
                                     <button
                                       className="btn btn-link btn-animate text-muted"
                                       onClick={linkEvent(
                                         this,
-                                        this.handlePurgeCommentShow
+                                        this.handleModBanSubmit
                                       )}
-                                      aria-label={i18n.t("purge_comment")}
+                                      aria-label={i18n.t("unban_from_site")}
                                     >
-                                      {i18n.t("purge_comment")}
+                                      {i18n.t("unban_from_site")}
                                     </button>
-
-                                    {!isBanned(cv.creator) ? (
-                                      <button
-                                        className="btn btn-link btn-animate text-muted"
-                                        onClick={linkEvent(
-                                          this,
-                                          this.handleModBanShow
-                                        )}
-                                        aria-label={i18n.t("ban_from_site")}
-                                      >
-                                        {i18n.t("ban_from_site")}
-                                      </button>
-                                    ) : (
-                                      <button
-                                        className="btn btn-link btn-animate text-muted"
-                                        onClick={linkEvent(
-                                          this,
-                                          this.handleModBanSubmit
-                                        )}
-                                        aria-label={i18n.t("unban_from_site")}
-                                      >
-                                        {i18n.t("unban_from_site")}
-                                      </button>
+                                  )}
+                                </>
+                              )}
+                              {!isBanned(cv.creator) &&
+                                cv.creator.local &&
+                                (!this.state.showConfirmAppointAsAdmin ? (
+                                  <button
+                                    className="btn btn-link btn-animate text-muted"
+                                    onClick={linkEvent(
+                                      this,
+                                      this.handleShowConfirmAppointAsAdmin
                                     )}
-                                  </>
-                                )}
-                                {!isBanned(cv.creator) &&
-                                  cv.creator.local &&
-                                  (!this.state.showConfirmAppointAsAdmin ? (
+                                    aria-label={
+                                      isAdmin_
+                                        ? i18n.t("remove_as_admin")
+                                        : i18n.t("appoint_as_admin")
+                                    }
+                                  >
+                                    {isAdmin_
+                                      ? i18n.t("remove_as_admin")
+                                      : i18n.t("appoint_as_admin")}
+                                  </button>
+                                ) : (
+                                  <>
+                                    <button className="btn btn-link btn-animate text-muted">
+                                      {i18n.t("are_you_sure")}
+                                    </button>
                                     <button
                                       className="btn btn-link btn-animate text-muted"
                                       onClick={linkEvent(
                                         this,
-                                        this.handleShowConfirmAppointAsAdmin
+                                        this.handleAddAdmin
                                       )}
-                                      aria-label={
-                                        isAdmin_
-                                          ? i18n.t("remove_as_admin")
-                                          : i18n.t("appoint_as_admin")
-                                      }
+                                      aria-label={i18n.t("yes")}
                                     >
-                                      {isAdmin_
-                                        ? i18n.t("remove_as_admin")
-                                        : i18n.t("appoint_as_admin")}
+                                      {i18n.t("yes")}
                                     </button>
-                                  ) : (
-                                    <>
-                                      <button className="btn btn-link btn-animate text-muted">
-                                        {i18n.t("are_you_sure")}
-                                      </button>
-                                      <button
-                                        className="btn btn-link btn-animate text-muted"
-                                        onClick={linkEvent(
-                                          this,
-                                          this.handleAddAdmin
-                                        )}
-                                        aria-label={i18n.t("yes")}
-                                      >
-                                        {i18n.t("yes")}
-                                      </button>
-                                      <button
-                                        className="btn btn-link btn-animate text-muted"
-                                        onClick={linkEvent(
-                                          this,
-                                          this.handleCancelConfirmAppointAsAdmin
-                                        )}
-                                        aria-label={i18n.t("no")}
-                                      >
-                                        {i18n.t("no")}
-                                      </button>
-                                    </>
-                                  ))}
-                              </>
-                            )}
-                          </>
-                        )}
-                      </>
-                    )}
+                                    <button
+                                      className="btn btn-link btn-animate text-muted"
+                                      onClick={linkEvent(
+                                        this,
+                                        this.handleCancelConfirmAppointAsAdmin
+                                      )}
+                                      aria-label={i18n.t("no")}
+                                    >
+                                      {i18n.t("no")}
+                                    </button>
+                                  </>
+                                ))}
+                            </>
+                          )}
+                        </>
+                      )}
+                    </>
+                  )}
                 </div>
                 {/* end of button group */}
               </div>
@@ -873,7 +856,7 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
               id={`mod-remove-reason-${cv.comment.id}`}
               className="form-control mr-2"
               placeholder={i18n.t("reason")}
-              value={toUndefined(this.state.removeReason)}
+              value={this.state.removeReason}
               onInput={linkEvent(this, this.handleModRemoveReasonChange)}
             />
             <button
@@ -928,7 +911,7 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
                 id={`mod-ban-reason-${cv.comment.id}`}
                 className="form-control mr-2"
                 placeholder={i18n.t("reason")}
-                value={toUndefined(this.state.banReason)}
+                value={this.state.banReason}
                 onInput={linkEvent(this, this.handleModBanReasonChange)}
               />
               <label
@@ -942,7 +925,7 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
                 id={`mod-ban-expires-${cv.comment.id}`}
                 className="form-control mr-2"
                 placeholder={i18n.t("number_of_days")}
-                value={toUndefined(this.state.banExpireDays)}
+                value={this.state.banExpireDays}
                 onInput={linkEvent(this, this.handleModBanExpireDaysChange)}
               />
               <div className="form-group">
@@ -992,7 +975,7 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
               id="purge-reason"
               className="form-control my-3"
               placeholder={i18n.t("reason")}
-              value={toUndefined(this.state.purgeReason)}
+              value={this.state.purgeReason}
               onInput={linkEvent(this, this.handlePurgeReasonChange)}
             />
             <div className="form-group row col-12">
@@ -1012,7 +995,7 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
         )}
         {this.state.showReply && (
           <CommentForm
-            node={Left(node)}
+            node={node}
             onReplyCancel={this.handleReplyCancel}
             disabled={this.props.locked}
             focus
@@ -1026,7 +1009,6 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
             locked={this.props.locked}
             moderators={this.props.moderators}
             admins={this.props.admins}
-            maxCommentsShown={None}
             enableDownvotes={this.props.enableDownvotes}
             viewType={this.props.viewType}
             allLanguages={this.props.allLanguages}
@@ -1085,12 +1067,10 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
   }
 
   get myComment(): boolean {
-    return UserService.Instance.myUserInfo
-      .map(
-        m =>
-          m.local_user_view.person.id == this.props.node.comment_view.creator.id
-      )
-      .unwrapOr(false);
+    return (
+      UserService.Instance.myUserInfo?.local_user_view.person.id ==
+      this.props.node.comment_view.creator.id
+    );
   }
 
   get isPostCreator(): boolean {
@@ -1118,36 +1098,45 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
   }
 
   handleBlockUserClick(i: CommentNode) {
-    let blockUserForm = new BlockPerson({
-      person_id: i.props.node.comment_view.creator.id,
-      block: true,
-      auth: auth().unwrap(),
-    });
-    WebSocketService.Instance.send(wsClient.blockPerson(blockUserForm));
+    let auth = myAuth();
+    if (auth) {
+      let blockUserForm: BlockPerson = {
+        person_id: i.props.node.comment_view.creator.id,
+        block: true,
+        auth,
+      };
+      WebSocketService.Instance.send(wsClient.blockPerson(blockUserForm));
+    }
   }
 
   handleDeleteClick(i: CommentNode) {
     let comment = i.props.node.comment_view.comment;
-    let deleteForm = new DeleteComment({
-      comment_id: comment.id,
-      deleted: !comment.deleted,
-      auth: auth().unwrap(),
-    });
-    WebSocketService.Instance.send(wsClient.deleteComment(deleteForm));
+    let auth = myAuth();
+    if (auth) {
+      let deleteForm: DeleteComment = {
+        comment_id: comment.id,
+        deleted: !comment.deleted,
+        auth,
+      };
+      WebSocketService.Instance.send(wsClient.deleteComment(deleteForm));
+    }
   }
 
   handleSaveCommentClick(i: CommentNode) {
     let cv = i.props.node.comment_view;
     let save = cv.saved == undefined ? true : !cv.saved;
-    let form = new SaveComment({
-      comment_id: cv.comment.id,
-      save,
-      auth: auth().unwrap(),
-    });
+    let auth = myAuth();
+    if (auth) {
+      let form: SaveComment = {
+        comment_id: cv.comment.id,
+        save,
+        auth,
+      };
 
-    WebSocketService.Instance.send(wsClient.saveComment(form));
+      WebSocketService.Instance.send(wsClient.saveComment(form));
 
-    i.setState({ saveLoading: true });
+      i.setState({ saveLoading: true });
+    }
   }
 
   handleReplyCancel() {
@@ -1156,7 +1145,7 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
 
   handleCommentUpvote(event: any) {
     event.preventDefault();
-    let myVote = this.state.my_vote.unwrapOr(0);
+    let myVote = this.state.my_vote;
     let newVote = myVote == 1 ? 0 : 1;
 
     if (myVote == 1) {
@@ -1177,20 +1166,23 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
       });
     }
 
-    this.setState({ my_vote: Some(newVote) });
-
-    let form = new CreateCommentLike({
-      comment_id: this.props.node.comment_view.comment.id,
-      score: newVote,
-      auth: auth().unwrap(),
-    });
-    WebSocketService.Instance.send(wsClient.likeComment(form));
-    setupTippy();
+    this.setState({ my_vote: newVote });
+
+    let auth = myAuth();
+    if (auth) {
+      let form: CreateCommentLike = {
+        comment_id: this.props.node.comment_view.comment.id,
+        score: newVote,
+        auth,
+      };
+      WebSocketService.Instance.send(wsClient.likeComment(form));
+      setupTippy();
+    }
   }
 
   handleCommentDownvote(event: any) {
     event.preventDefault();
-    let myVote = this.state.my_vote.unwrapOr(0);
+    let myVote = this.state.my_vote;
     let newVote = myVote == -1 ? 0 : -1;
 
     if (myVote == 1) {
@@ -1211,16 +1203,19 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
       });
     }
 
-    this.setState({ my_vote: Some(newVote) });
+    this.setState({ my_vote: newVote });
 
-    let form = new CreateCommentLike({
-      comment_id: this.props.node.comment_view.comment.id,
-      score: newVote,
-      auth: auth().unwrap(),
-    });
+    let auth = myAuth();
+    if (auth) {
+      let form: CreateCommentLike = {
+        comment_id: this.props.node.comment_view.comment.id,
+        score: newVote,
+        auth,
+      };
 
-    WebSocketService.Instance.send(wsClient.likeComment(form));
-    setupTippy();
+      WebSocketService.Instance.send(wsClient.likeComment(form));
+      setupTippy();
+    }
   }
 
   handleShowReportDialog(i: CommentNode) {
@@ -1233,14 +1228,17 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
 
   handleReportSubmit(i: CommentNode) {
     let comment = i.props.node.comment_view.comment;
-    let form = new CreateCommentReport({
-      comment_id: comment.id,
-      reason: i.state.reportReason,
-      auth: auth().unwrap(),
-    });
-    WebSocketService.Instance.send(wsClient.createCommentReport(form));
-
-    i.setState({ showReportDialog: false });
+    let reason = i.state.reportReason;
+    let auth = myAuth();
+    if (reason && auth) {
+      let form: CreateCommentReport = {
+        comment_id: comment.id,
+        reason,
+        auth,
+      };
+      WebSocketService.Instance.send(wsClient.createCommentReport(form));
+      i.setState({ showReportDialog: false });
+    }
   }
 
   handleModRemoveShow(i: CommentNode) {
@@ -1251,7 +1249,7 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
   }
 
   handleModRemoveReasonChange(i: CommentNode, event: any) {
-    i.setState({ removeReason: Some(event.target.value) });
+    i.setState({ removeReason: event.target.value });
   }
 
   handleModRemoveDataChange(i: CommentNode, event: any) {
@@ -1260,29 +1258,32 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
 
   handleModRemoveSubmit(i: CommentNode) {
     let comment = i.props.node.comment_view.comment;
-    let form = new RemoveComment({
-      comment_id: comment.id,
-      removed: !comment.removed,
-      reason: i.state.removeReason,
-      auth: auth().unwrap(),
-    });
-    WebSocketService.Instance.send(wsClient.removeComment(form));
-
-    i.setState({ showRemoveDialog: false });
+    let auth = myAuth();
+    if (auth) {
+      let form: RemoveComment = {
+        comment_id: comment.id,
+        removed: !comment.removed,
+        reason: i.state.removeReason,
+        auth,
+      };
+      WebSocketService.Instance.send(wsClient.removeComment(form));
+
+      i.setState({ showRemoveDialog: false });
+    }
   }
 
   handleDistinguishClick(i: CommentNode) {
     let comment = i.props.node.comment_view.comment;
-    let form = new EditComment({
-      comment_id: comment.id,
-      form_id: None, // TODO not sure about this
-      content: None,
-      distinguished: Some(!comment.distinguished),
-      language_id: Some(comment.language_id),
-      auth: auth().unwrap(),
-    });
-    WebSocketService.Instance.send(wsClient.editComment(form));
-    i.setState(i.state);
+    let auth = myAuth();
+    if (auth) {
+      let form: EditComment = {
+        comment_id: comment.id,
+        distinguished: !comment.distinguished,
+        auth,
+      };
+      WebSocketService.Instance.send(wsClient.editComment(form));
+      i.setState(i.state);
+    }
   }
 
   isPersonMentionType(
@@ -1298,23 +1299,26 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
   }
 
   handleMarkRead(i: CommentNode) {
-    if (i.isPersonMentionType(i.props.node.comment_view)) {
-      let form = new MarkPersonMentionAsRead({
-        person_mention_id: i.props.node.comment_view.person_mention.id,
-        read: !i.props.node.comment_view.person_mention.read,
-        auth: auth().unwrap(),
-      });
-      WebSocketService.Instance.send(wsClient.markPersonMentionAsRead(form));
-    } else if (i.isCommentReplyType(i.props.node.comment_view)) {
-      let form = new MarkCommentReplyAsRead({
-        comment_reply_id: i.props.node.comment_view.comment_reply.id,
-        read: !i.props.node.comment_view.comment_reply.read,
-        auth: auth().unwrap(),
-      });
-      WebSocketService.Instance.send(wsClient.markCommentReplyAsRead(form));
-    }
+    let auth = myAuth();
+    if (auth) {
+      if (i.isPersonMentionType(i.props.node.comment_view)) {
+        let form: MarkPersonMentionAsRead = {
+          person_mention_id: i.props.node.comment_view.person_mention.id,
+          read: !i.props.node.comment_view.person_mention.read,
+          auth,
+        };
+        WebSocketService.Instance.send(wsClient.markPersonMentionAsRead(form));
+      } else if (i.isCommentReplyType(i.props.node.comment_view)) {
+        let form: MarkCommentReplyAsRead = {
+          comment_reply_id: i.props.node.comment_view.comment_reply.id,
+          read: !i.props.node.comment_view.comment_reply.read,
+          auth,
+        };
+        WebSocketService.Instance.send(wsClient.markCommentReplyAsRead(form));
+      }
 
-    i.setState({ readLoading: true });
+      i.setState({ readLoading: true });
+    }
   }
 
   handleModBanFromCommunityShow(i: CommentNode) {
@@ -1334,11 +1338,11 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
   }
 
   handleModBanReasonChange(i: CommentNode, event: any) {
-    i.setState({ banReason: Some(event.target.value) });
+    i.setState({ banReason: event.target.value });
   }
 
   handleModBanExpireDaysChange(i: CommentNode, event: any) {
-    i.setState({ banExpireDays: Some(event.target.value) });
+    i.setState({ banExpireDays: event.target.value });
   }
 
   handleModBanFromCommunitySubmit(i: CommentNode) {
@@ -1353,41 +1357,43 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
 
   handleModBanBothSubmit(i: CommentNode) {
     let cv = i.props.node.comment_view;
-
-    if (i.state.banType == BanType.Community) {
-      // If its an unban, restore all their data
-      let ban = !cv.creator_banned_from_community;
-      if (ban == false) {
-        i.setState({ removeData: false });
-      }
-      let form = new BanFromCommunity({
-        person_id: cv.creator.id,
-        community_id: cv.community.id,
-        ban,
-        remove_data: Some(i.state.removeData),
-        reason: i.state.banReason,
-        expires: i.state.banExpireDays.map(futureDaysToUnixTime),
-        auth: auth().unwrap(),
-      });
-      WebSocketService.Instance.send(wsClient.banFromCommunity(form));
-    } else {
-      // If its an unban, restore all their data
-      let ban = !cv.creator.banned;
-      if (ban == false) {
-        i.setState({ removeData: false });
+    let auth = myAuth();
+    if (auth) {
+      if (i.state.banType == BanType.Community) {
+        // If its an unban, restore all their data
+        let ban = !cv.creator_banned_from_community;
+        if (ban == false) {
+          i.setState({ removeData: false });
+        }
+        let form: BanFromCommunity = {
+          person_id: cv.creator.id,
+          community_id: cv.community.id,
+          ban,
+          remove_data: i.state.removeData,
+          reason: i.state.banReason,
+          expires: futureDaysToUnixTime(i.state.banExpireDays),
+          auth,
+        };
+        WebSocketService.Instance.send(wsClient.banFromCommunity(form));
+      } else {
+        // If its an unban, restore all their data
+        let ban = !cv.creator.banned;
+        if (ban == false) {
+          i.setState({ removeData: false });
+        }
+        let form: BanPerson = {
+          person_id: cv.creator.id,
+          ban,
+          remove_data: i.state.removeData,
+          reason: i.state.banReason,
+          expires: futureDaysToUnixTime(i.state.banExpireDays),
+          auth,
+        };
+        WebSocketService.Instance.send(wsClient.banPerson(form));
       }
-      let form = new BanPerson({
-        person_id: cv.creator.id,
-        ban,
-        remove_data: Some(i.state.removeData),
-        reason: i.state.banReason,
-        expires: i.state.banExpireDays.map(futureDaysToUnixTime),
-        auth: auth().unwrap(),
-      });
-      WebSocketService.Instance.send(wsClient.banPerson(form));
-    }
 
-    i.setState({ showBanDialog: false });
+      i.setState({ showBanDialog: false });
+    }
   }
 
   handlePurgePersonShow(i: CommentNode) {
@@ -1407,29 +1413,31 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
   }
 
   handlePurgeReasonChange(i: CommentNode, event: any) {
-    i.setState({ purgeReason: Some(event.target.value) });
+    i.setState({ purgeReason: event.target.value });
   }
 
   handlePurgeSubmit(i: CommentNode, event: any) {
     event.preventDefault();
+    let auth = myAuth();
+    if (auth) {
+      if (i.state.purgeType == PurgeType.Person) {
+        let form: PurgePerson = {
+          person_id: i.props.node.comment_view.creator.id,
+          reason: i.state.purgeReason,
+          auth,
+        };
+        WebSocketService.Instance.send(wsClient.purgePerson(form));
+      } else if (i.state.purgeType == PurgeType.Comment) {
+        let form: PurgeComment = {
+          comment_id: i.props.node.comment_view.comment.id,
+          reason: i.state.purgeReason,
+          auth,
+        };
+        WebSocketService.Instance.send(wsClient.purgeComment(form));
+      }
 
-    if (i.state.purgeType == PurgeType.Person) {
-      let form = new PurgePerson({
-        person_id: i.props.node.comment_view.creator.id,
-        reason: i.state.purgeReason,
-        auth: auth().unwrap(),
-      });
-      WebSocketService.Instance.send(wsClient.purgePerson(form));
-    } else if (i.state.purgeType == PurgeType.Comment) {
-      let form = new PurgeComment({
-        comment_id: i.props.node.comment_view.comment.id,
-        reason: i.state.purgeReason,
-        auth: auth().unwrap(),
-      });
-      WebSocketService.Instance.send(wsClient.purgeComment(form));
+      i.setState({ purgeLoading: true });
     }
-
-    i.setState({ purgeLoading: true });
   }
 
   handleShowConfirmAppointAsMod(i: CommentNode) {
@@ -1442,14 +1450,17 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
 
   handleAddModToCommunity(i: CommentNode) {
     let cv = i.props.node.comment_view;
-    let form = new AddModToCommunity({
-      person_id: cv.creator.id,
-      community_id: cv.community.id,
-      added: !isMod(i.props.moderators, cv.creator.id),
-      auth: auth().unwrap(),
-    });
-    WebSocketService.Instance.send(wsClient.addModToCommunity(form));
-    i.setState({ showConfirmAppointAsMod: false });
+    let auth = myAuth();
+    if (auth) {
+      let form: AddModToCommunity = {
+        person_id: cv.creator.id,
+        community_id: cv.community.id,
+        added: !isMod(cv.creator.id, i.props.moderators),
+        auth,
+      };
+      WebSocketService.Instance.send(wsClient.addModToCommunity(form));
+      i.setState({ showConfirmAppointAsMod: false });
+    }
   }
 
   handleShowConfirmAppointAsAdmin(i: CommentNode) {
@@ -1461,14 +1472,17 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
   }
 
   handleAddAdmin(i: CommentNode) {
-    let creatorId = i.props.node.comment_view.creator.id;
-    let form = new AddAdmin({
-      person_id: creatorId,
-      added: !isAdmin(i.props.admins, creatorId),
-      auth: auth().unwrap(),
-    });
-    WebSocketService.Instance.send(wsClient.addAdmin(form));
-    i.setState({ showConfirmAppointAsAdmin: false });
+    let auth = myAuth();
+    if (auth) {
+      let creatorId = i.props.node.comment_view.creator.id;
+      let form: AddAdmin = {
+        person_id: creatorId,
+        added: !isAdmin(creatorId, i.props.admins),
+        auth,
+      };
+      WebSocketService.Instance.send(wsClient.addAdmin(form));
+      i.setState({ showConfirmAppointAsAdmin: false });
+    }
   }
 
   handleShowConfirmTransferCommunity(i: CommentNode) {
@@ -1481,13 +1495,16 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
 
   handleTransferCommunity(i: CommentNode) {
     let cv = i.props.node.comment_view;
-    let form = new TransferCommunity({
-      community_id: cv.community.id,
-      person_id: cv.creator.id,
-      auth: auth().unwrap(),
-    });
-    WebSocketService.Instance.send(wsClient.transferCommunity(form));
-    i.setState({ showConfirmTransferCommunity: false });
+    let auth = myAuth();
+    if (auth) {
+      let form: TransferCommunity = {
+        community_id: cv.community.id,
+        person_id: cv.creator.id,
+        auth,
+      };
+      WebSocketService.Instance.send(wsClient.transferCommunity(form));
+      i.setState({ showConfirmTransferCommunity: false });
+    }
   }
 
   handleShowConfirmTransferSite(i: CommentNode) {
@@ -1519,27 +1536,23 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
   }
 
   handleFetchChildren(i: CommentNode) {
-    let form = new GetComments({
-      post_id: Some(i.props.node.comment_view.post.id),
-      parent_id: Some(i.props.node.comment_view.comment.id),
-      max_depth: Some(commentTreeMaxDepth),
-      page: None,
-      sort: None,
-      limit: Some(999),
-      type_: Some(ListingType.All),
-      community_name: None,
-      community_id: None,
-      saved_only: Some(false),
-      auth: auth(false).ok(),
-    });
+    let form: GetComments = {
+      post_id: i.props.node.comment_view.post.id,
+      parent_id: i.props.node.comment_view.comment.id,
+      max_depth: commentTreeMaxDepth,
+      limit: 999, // TODO
+      type_: ListingType.All,
+      saved_only: false,
+      auth: myAuth(false),
+    };
 
     WebSocketService.Instance.send(wsClient.getComments(form));
   }
 
   get scoreColor() {
-    if (this.state.my_vote.unwrapOr(0) == 1) {
+    if (this.state.my_vote == 1) {
       return "text-info";
-    } else if (this.state.my_vote.unwrapOr(0) == -1) {
+    } else if (this.state.my_vote == -1) {
       return "text-danger";
     } else {
       return "text-muted";
index 9072e72371192a8266a48888175c41b1fca1e637..a053f9929d2f1c193a0201c260ebdcd9d58d0410 100644 (file)
@@ -1,4 +1,3 @@
-import { Option } from "@sniptt/monads";
 import { Component } from "inferno";
 import {
   CommentNode as CommentNodeI,
@@ -11,9 +10,9 @@ import { CommentNode } from "./comment-node";
 
 interface CommentNodesProps {
   nodes: CommentNodeI[];
-  moderators: Option<CommunityModeratorView[]>;
-  admins: Option<PersonViewSafe[]>;
-  maxCommentsShown: Option<number>;
+  moderators?: CommunityModeratorView[];
+  admins?: PersonViewSafe[];
+  maxCommentsShown?: number;
   noBorder?: boolean;
   noIndent?: boolean;
   viewOnly?: boolean;
@@ -34,9 +33,7 @@ export class CommentNodes extends Component<CommentNodesProps, any> {
   }
 
   render() {
-    let maxComments = this.props.maxCommentsShown.unwrapOr(
-      this.props.nodes.length
-    );
+    let maxComments = this.props.maxCommentsShown ?? this.props.nodes.length;
 
     return (
       <div className="comments">
index b90eae41470d9d1d2850821cee974ebecde33b1c..5f7c24cbd82c10ffbc83a8336ba8beab8f94b58d 100644 (file)
@@ -1,4 +1,3 @@
-import { None } from "@sniptt/monads";
 import { Component, linkEvent } from "inferno";
 import { T } from "inferno-i18next-dess";
 import {
@@ -11,7 +10,7 @@ import {
 import { i18n } from "../../i18next";
 import { CommentViewType } from "../../interfaces";
 import { WebSocketService } from "../../services";
-import { auth, wsClient } from "../../utils";
+import { myAuth, wsClient } from "../../utils";
 import { Icon } from "../common/icon";
 import { PersonListing } from "../person/person-listing";
 import { CommentNode } from "./comment-node";
@@ -59,8 +58,6 @@ export class CommentReport extends Component<CommentReportProps, any> {
         <CommentNode
           node={node}
           viewType={CommentViewType.Flat}
-          moderators={None}
-          admins={None}
           enableDownvotes={true}
           viewOnly={true}
           showCommunity={true}
@@ -74,24 +71,21 @@ export class CommentReport extends Component<CommentReportProps, any> {
         <div>
           {i18n.t("reason")}: {r.comment_report.reason}
         </div>
-        {r.resolver.match({
-          some: resolver => (
-            <div>
-              {r.comment_report.resolved ? (
-                <T i18nKey="resolved_by">
-                  #
-                  <PersonListing person={resolver} />
-                </T>
-              ) : (
-                <T i18nKey="unresolved_by">
-                  #
-                  <PersonListing person={resolver} />
-                </T>
-              )}
-            </div>
-          ),
-          none: <></>,
-        })}
+        {r.resolver && (
+          <div>
+            {r.comment_report.resolved ? (
+              <T i18nKey="resolved_by">
+                #
+                <PersonListing person={r.resolver} />
+              </T>
+            ) : (
+              <T i18nKey="unresolved_by">
+                #
+                <PersonListing person={r.resolver} />
+              </T>
+            )}
+          </div>
+        )}
         <button
           className="btn btn-link btn-animate text-muted py-0"
           onClick={linkEvent(this, this.handleResolveReport)}
@@ -110,11 +104,14 @@ export class CommentReport extends Component<CommentReportProps, any> {
   }
 
   handleResolveReport(i: CommentReport) {
-    let form = new ResolveCommentReport({
-      report_id: i.props.report.comment_report.id,
-      resolved: !i.props.report.comment_report.resolved,
-      auth: auth().unwrap(),
-    });
-    WebSocketService.Instance.send(wsClient.resolveCommentReport(form));
+    let auth = myAuth();
+    if (auth) {
+      let form: ResolveCommentReport = {
+        report_id: i.props.report.comment_report.id,
+        resolved: !i.props.report.comment_report.resolved,
+        auth,
+      };
+      WebSocketService.Instance.send(wsClient.resolveCommentReport(form));
+    }
   }
 }
index a72400a3dc824cd075ea39607d1bbb663a6350da..e29014525a4b7fce038c30c0909e2680aaebaa7a 100644 (file)
@@ -1,10 +1,9 @@
-import { Option } from "@sniptt/monads";
 import { Component } from "inferno";
 import { PictrsImage } from "./pictrs-image";
 
 interface BannerIconHeaderProps {
-  banner: Option<string>;
-  icon: Option<string>;
+  banner?: string;
+  icon?: string;
 }
 
 export class BannerIconHeader extends Component<BannerIconHeaderProps, any> {
@@ -13,23 +12,19 @@ export class BannerIconHeader extends Component<BannerIconHeaderProps, any> {
   }
 
   render() {
+    let banner = this.props.banner;
+    let icon = this.props.icon;
     return (
       <div className="position-relative mb-2">
-        {this.props.banner.match({
-          some: banner => <PictrsImage src={banner} banner alt="" />,
-          none: <></>,
-        })}
-        {this.props.icon.match({
-          some: icon => (
-            <PictrsImage
-              src={icon}
-              iconOverlay
-              pushup={this.props.banner.isSome()}
-              alt=""
-            />
-          ),
-          none: <></>,
-        })}
+        {banner && <PictrsImage src={banner} banner alt="" />}
+        {icon && (
+          <PictrsImage
+            src={icon}
+            iconOverlay
+            pushup={!!this.props.banner}
+            alt=""
+          />
+        )}
       </div>
     );
   }
index 0e49d561ed9f0c631ff0c3bfdc2f8af6a5a418f2..1874776c67187a86eca686722756b1f22e5a9b07 100644 (file)
@@ -18,13 +18,12 @@ export class CommentSortSelect extends Component<
   CommentSortSelectState
 > {
   private id = `sort-select-${randomStr()}`;
-  private emptyState: CommentSortSelectState = {
+  state: CommentSortSelectState = {
     sort: this.props.sort,
   };
 
   constructor(props: any, context: any) {
     super(props, context);
-    this.state = this.emptyState;
   }
 
   static getDerivedStateFromProps(props: any): CommentSortSelectState {
@@ -65,6 +64,6 @@ export class CommentSortSelect extends Component<
   }
 
   handleSortChange(i: CommentSortSelect, event: any) {
-    i.props.onChange(event.target.value);
+    i.props.onChange?.(event.target.value);
   }
 }
index 8a5613e85863553de3830bcc076c92060566fc5b..59c07d759c70588ba989d191efa1cc6f6774256c 100644 (file)
@@ -15,13 +15,12 @@ export class DataTypeSelect extends Component<
   DataTypeSelectProps,
   DataTypeSelectState
 > {
-  private emptyState: DataTypeSelectState = {
+  state: DataTypeSelectState = {
     type_: this.props.type_,
   };
 
   constructor(props: any, context: any) {
     super(props, context);
-    this.state = this.emptyState;
   }
 
   static getDerivedStateFromProps(props: any): DataTypeSelectProps {
@@ -64,6 +63,6 @@ export class DataTypeSelect extends Component<
   }
 
   handleTypeChange(i: DataTypeSelect, event: any) {
-    i.props.onChange(Number(event.target.value));
+    i.props.onChange?.(Number(event.target.value));
   }
 }
index 8f35b12f7335d0d070b99166e5474ef0b28beb30..67abe3a7b87cc31c264a61b91ee503fccac2a503 100644 (file)
@@ -1,4 +1,3 @@
-import { Option } from "@sniptt/monads";
 import { htmlToText } from "html-to-text";
 import { Component } from "inferno";
 import { Helmet } from "inferno-helmet";
@@ -8,14 +7,16 @@ import { md } from "../../utils";
 interface HtmlTagsProps {
   title: string;
   path: string;
-  description: Option<string>;
-  image: Option<string>;
+  description?: string;
+  image?: string;
 }
 
 /// Taken from https://metatags.io/
 export class HtmlTags extends Component<HtmlTagsProps, any> {
   render() {
     let url = httpExternalPath(this.props.path);
+    let desc = this.props.description;
+    let image = this.props.image;
 
     return (
       <Helmet title={this.props.title}>
@@ -33,21 +34,19 @@ export class HtmlTags extends Component<HtmlTagsProps, any> {
         <meta property="twitter:card" content="summary_large_image" />
 
         {/* Optional desc and images */}
-        {this.props.description.isSome() &&
-          ["description", "og:description", "twitter:description"].map(n => (
-            <meta
-              key={n}
-              name={n}
-              content={htmlToText(
-                md.renderInline(this.props.description.unwrap())
-              )}
-            />
-          ))}
-
-        {this.props.image.isSome() &&
-          ["og:image", "twitter:image"].map(p => (
-            <meta key={p} property={p} content={this.props.image.unwrap()} />
-          ))}
+        {["description", "og:description", "twitter:description"].map(
+          n =>
+            desc && (
+              <meta
+                key={n}
+                name={n}
+                content={htmlToText(md.renderInline(desc))}
+              />
+            )
+        )}
+        {["og:image", "twitter:image"].map(
+          p => image && <meta key={p} property={p} content={image} />
+        )}
       </Helmet>
     );
   }
index 432521efa26fac5f849e444ab6985b876d172ab2..6cf4e1479168c7cced5c14f6ee312d9417098186 100644 (file)
@@ -1,4 +1,3 @@
-import { Option } from "@sniptt/monads";
 import { Component, linkEvent } from "inferno";
 import { pictrsUri } from "../../env";
 import { i18n } from "../../i18next";
@@ -8,7 +7,7 @@ import { Icon } from "./icon";
 
 interface ImageUploadFormProps {
   uploadTitle: string;
-  imageSrc: Option<string>;
+  imageSrc?: string;
   onUpload(url: string): any;
   onRemove(): any;
   rounded?: boolean;
@@ -39,31 +38,26 @@ export class ImageUploadForm extends Component<
           htmlFor={this.id}
           className="pointer text-muted small font-weight-bold"
         >
-          {this.props.imageSrc.match({
-            some: imageSrc => (
-              <span className="d-inline-block position-relative">
-                <img
-                  src={imageSrc}
-                  height={this.props.rounded ? 60 : ""}
-                  width={this.props.rounded ? 60 : ""}
-                  className={`img-fluid ${
-                    this.props.rounded ? "rounded-circle" : ""
-                  }`}
-                />
-                <a
-                  onClick={linkEvent(this, this.handleRemoveImage)}
-                  aria-label={i18n.t("remove")}
-                >
-                  <Icon icon="x" classes="mini-overlay" />
-                </a>
-              </span>
-            ),
-            none: (
-              <span className="btn btn-secondary">
-                {this.props.uploadTitle}
-              </span>
-            ),
-          })}
+          {this.props.imageSrc ? (
+            <span className="d-inline-block position-relative">
+              <img
+                src={this.props.imageSrc}
+                height={this.props.rounded ? 60 : ""}
+                width={this.props.rounded ? 60 : ""}
+                className={`img-fluid ${
+                  this.props.rounded ? "rounded-circle" : ""
+                }`}
+              />
+              <a
+                onClick={linkEvent(this, this.handleRemoveImage)}
+                aria-label={i18n.t("remove")}
+              >
+                <Icon icon="x" classes="mini-overlay" />
+              </a>
+            </span>
+          ) : (
+            <span className="btn btn-secondary">{this.props.uploadTitle}</span>
+          )}
         </label>
         <input
           id={this.id}
@@ -71,7 +65,7 @@ export class ImageUploadForm extends Component<
           accept="image/*,video/*"
           name={this.id}
           className="d-none"
-          disabled={UserService.Instance.myUserInfo.isNone()}
+          disabled={!UserService.Instance.myUserInfo}
           onChange={linkEvent(this, this.handleImageUpload)}
         />
       </form>
index c7da2f05b38769b283254ea704bd7214b9bbde4d..1182531a03b0944a4d484a08654432bd2e488db6 100644 (file)
@@ -1,4 +1,3 @@
-import { Option } from "@sniptt/monads";
 import classNames from "classnames";
 import { Component, linkEvent } from "inferno";
 import { Language } from "lemmy-js-client";
@@ -10,7 +9,7 @@ import { Icon } from "./icon";
 interface LanguageSelectProps {
   allLanguages: Language[];
   siteLanguages: number[];
-  selectedLanguageIds: Option<number[]>;
+  selectedLanguageIds?: number[];
   multiple: boolean;
   onChange(val: number[]): any;
   showAll?: boolean;
@@ -31,19 +30,17 @@ export class LanguageSelect extends Component<LanguageSelectProps, any> {
 
   // Necessary because there is no HTML way to set selected for multiple in value=
   setSelectedValues() {
-    this.props.selectedLanguageIds.map(toString).match({
-      some: ids => {
-        var select = (document.getElementById(this.id) as HTMLSelectElement)
-          .options;
-        for (let i = 0; i < select.length; i++) {
-          let o = select[i];
-          if (ids.includes(o.value)) {
-            o.selected = true;
-          }
+    let ids = this.props.selectedLanguageIds?.map(toString);
+    if (ids) {
+      let select = (document.getElementById(this.id) as HTMLSelectElement)
+        .options;
+      for (let i = 0; i < select.length; i++) {
+        let o = select[i];
+        if (ids.includes(o.value)) {
+          o.selected = true;
         }
-      },
-      none: void 0,
-    });
+      }
+    }
   }
 
   render() {
@@ -107,7 +104,7 @@ export class LanguageSelect extends Component<LanguageSelectProps, any> {
           <option
             key={l.id}
             value={l.id}
-            selected={selectedLangs.unwrapOr([]).includes(l.id)}
+            selected={selectedLangs?.includes(l.id)}
           >
             {l.name}
           </option>
index 38e6acddc639ee6f79b97d3e995cc330d5c5ad16..c214d44ca8077fa87b971faa4e2e233dcf41ac00 100644 (file)
@@ -21,13 +21,12 @@ export class ListingTypeSelect extends Component<
 > {
   private id = `listing-type-input-${randomStr()}`;
 
-  private emptyState: ListingTypeSelectState = {
+  state: ListingTypeSelectState = {
     type_: this.props.type_,
   };
 
   constructor(props: any, context: any) {
     super(props, context);
-    this.state = this.emptyState;
   }
 
   static getDerivedStateFromProps(props: any): ListingTypeSelectProps {
@@ -46,7 +45,7 @@ export class ListingTypeSelect extends Component<
             title={i18n.t("subscribed_description")}
             className={`btn btn-outline-secondary 
             ${this.state.type_ == ListingType.Subscribed && "active"}
-            ${UserService.Instance.myUserInfo.isNone() ? "disabled" : "pointer"}
+            ${!UserService.Instance.myUserInfo ? "disabled" : "pointer"}
           `}
           >
             <input
@@ -55,7 +54,7 @@ export class ListingTypeSelect extends Component<
               value={ListingType.Subscribed}
               checked={this.state.type_ == ListingType.Subscribed}
               onChange={linkEvent(this, this.handleTypeChange)}
-              disabled={UserService.Instance.myUserInfo.isNone()}
+              disabled={!UserService.Instance.myUserInfo}
             />
             {i18n.t("subscribed")}
           </label>
@@ -100,6 +99,6 @@ export class ListingTypeSelect extends Component<
   }
 
   handleTypeChange(i: ListingTypeSelect, event: any) {
-    i.props.onChange(event.target.value);
+    i.props.onChange?.(event.target.value);
   }
 }
index 6b517d7749a03329c6ed68252f34d173602ee690..9b51a7ebffe67597e7d5030dcfed57cd95785cf7 100644 (file)
@@ -1,8 +1,7 @@
-import { None, Option, Some } from "@sniptt/monads";
 import autosize from "autosize";
 import { Component, linkEvent } from "inferno";
 import { Prompt } from "inferno-router";
-import { Language, toUndefined } from "lemmy-js-client";
+import { Language } from "lemmy-js-client";
 import { pictrsUri } from "../../env";
 import { i18n } from "../../i18next";
 import { UserService } from "../../services";
@@ -22,11 +21,11 @@ import { Icon, Spinner } from "./icon";
 import { LanguageSelect } from "./language-select";
 
 interface MarkdownTextAreaProps {
-  initialContent: Option<string>;
-  initialLanguageId: Option<number>;
-  placeholder: Option<string>;
-  buttonTitle: Option<string>;
-  maxLength: Option<number>;
+  initialContent?: string;
+  initialLanguageId?: number;
+  placeholder?: string;
+  buttonTitle?: string;
+  maxLength?: number;
   replyType?: boolean;
   focus?: boolean;
   disabled?: boolean;
@@ -35,18 +34,14 @@ interface MarkdownTextAreaProps {
   hideNavigationWarnings?: boolean;
   onContentChange?(val: string): any;
   onReplyCancel?(): any;
-  onSubmit?(msg: {
-    val: Option<string>;
-    formId: string;
-    languageId: Option<number>;
-  }): any;
-  allLanguages: Language[];
-  siteLanguages: number[];
+  onSubmit?(msg: { val?: string; formId: string; languageId?: number }): any;
+  allLanguages: Language[]; // TODO should probably be nullable
+  siteLanguages: number[]; // TODO same
 }
 
 interface MarkdownTextAreaState {
-  content: Option<string>;
-  languageId: Option<number>;
+  content?: string;
+  languageId?: number;
   previewMode: boolean;
   loading: boolean;
   imageLoading: boolean;
@@ -59,7 +54,7 @@ export class MarkdownTextArea extends Component<
   private id = `comment-textarea-${randomStr()}`;
   private formId = `comment-form-${randomStr()}`;
   private tribute: any;
-  private emptyState: MarkdownTextAreaState = {
+  state: MarkdownTextAreaState = {
     content: this.props.initialContent,
     languageId: this.props.initialLanguageId,
     previewMode: false,
@@ -75,7 +70,6 @@ export class MarkdownTextArea extends Component<
     if (isBrowser()) {
       this.tribute = setupTribute();
     }
-    this.state = this.emptyState;
   }
 
   componentDidMount() {
@@ -84,7 +78,7 @@ export class MarkdownTextArea extends Component<
       autosize(textarea);
       this.tribute.attach(textarea);
       textarea.addEventListener("tribute-replaced", () => {
-        this.setState({ content: Some(textarea.value) });
+        this.setState({ content: textarea.value });
         autosize.update(textarea);
       });
 
@@ -100,18 +94,18 @@ export class MarkdownTextArea extends Component<
   }
 
   componentDidUpdate() {
-    if (!this.props.hideNavigationWarnings && this.state.content.isSome()) {
+    if (!this.props.hideNavigationWarnings && this.state.content) {
       window.onbeforeunload = () => true;
     } else {
-      window.onbeforeunload = undefined;
+      window.onbeforeunload = null;
     }
   }
 
   componentWillReceiveProps(nextProps: MarkdownTextAreaProps) {
     if (nextProps.finished) {
-      this.setState({ previewMode: false, loading: false, content: None });
+      this.setState({ previewMode: false, loading: false, content: undefined });
       if (this.props.replyType) {
-        this.props.onReplyCancel();
+        this.props.onReplyCancel?.();
       }
 
       let textarea: any = document.getElementById(this.id);
@@ -126,12 +120,12 @@ export class MarkdownTextArea extends Component<
   }
 
   render() {
+    let languageId = this.state.languageId;
+
     return (
       <form id={this.formId} onSubmit={linkEvent(this, this.handleSubmit)}>
         <Prompt
-          when={
-            !this.props.hideNavigationWarnings && this.state.content.isSome()
-          }
+          when={!this.props.hideNavigationWarnings && this.state.content}
           message={i18n.t("block_leaving")}
         />
         <div className="form-group row">
@@ -139,27 +133,21 @@ export class MarkdownTextArea extends Component<
             <textarea
               id={this.id}
               className={`form-control ${this.state.previewMode && "d-none"}`}
-              value={toUndefined(this.state.content)}
+              value={this.state.content}
               onInput={linkEvent(this, this.handleContentChange)}
               onPaste={linkEvent(this, this.handleImageUploadPaste)}
               required
               disabled={this.props.disabled}
               rows={2}
-              maxLength={this.props.maxLength.unwrapOr(
-                markdownFieldCharacterLimit
-              )}
-              placeholder={toUndefined(this.props.placeholder)}
+              maxLength={this.props.maxLength ?? markdownFieldCharacterLimit}
+              placeholder={this.props.placeholder}
             />
-            {this.state.previewMode &&
-              this.state.content.match({
-                some: content => (
-                  <div
-                    className="card border-secondary card-body md-div"
-                    dangerouslySetInnerHTML={mdToHtml(content)}
-                  />
-                ),
-                none: <></>,
-              })}
+            {this.state.previewMode && this.state.content && (
+              <div
+                className="card border-secondary card-body md-div"
+                dangerouslySetInnerHTML={mdToHtml(this.state.content)}
+              />
+            )}
           </div>
           <label className="sr-only" htmlFor={this.id}>
             {i18n.t("body")}
@@ -167,22 +155,19 @@ export class MarkdownTextArea extends Component<
         </div>
         <div className="row">
           <div className="col-sm-12 d-flex flex-wrap">
-            {this.props.buttonTitle.match({
-              some: buttonTitle => (
-                <button
-                  type="submit"
-                  className="btn btn-sm btn-secondary mr-2"
-                  disabled={this.props.disabled || this.state.loading}
-                >
-                  {this.state.loading ? (
-                    <Spinner />
-                  ) : (
-                    <span>{buttonTitle}</span>
-                  )}
-                </button>
-              ),
-              none: <></>,
-            })}
+            {this.props.buttonTitle && (
+              <button
+                type="submit"
+                className="btn btn-sm btn-secondary mr-2"
+                disabled={this.props.disabled || this.state.loading}
+              >
+                {this.state.loading ? (
+                  <Spinner />
+                ) : (
+                  <span>{this.props.buttonTitle}</span>
+                )}
+              </button>
+            )}
             {this.props.replyType && (
               <button
                 type="button"
@@ -192,7 +177,7 @@ export class MarkdownTextArea extends Component<
                 {i18n.t("cancel")}
               </button>
             )}
-            {this.state.content.isSome() && (
+            {this.state.content && (
               <button
                 className={`btn btn-sm btn-secondary mr-2 ${
                   this.state.previewMode && "active"
@@ -209,7 +194,9 @@ export class MarkdownTextArea extends Component<
               <LanguageSelect
                 iconVersion
                 allLanguages={this.props.allLanguages}
-                selectedLanguageIds={this.state.languageId.map(Array.of)}
+                selectedLanguageIds={
+                  languageId ? Array.of(languageId) : undefined
+                }
                 siteLanguages={this.props.siteLanguages}
                 multiple={false}
                 onChange={this.handleLanguageChange}
@@ -243,7 +230,7 @@ export class MarkdownTextArea extends Component<
               <label
                 htmlFor={`file-upload-${this.id}`}
                 className={`mb-0 ${
-                  UserService.Instance.myUserInfo.isSome() && "pointer"
+                  UserService.Instance.myUserInfo && "pointer"
                 }`}
                 data-tippy-content={i18n.t("upload_image")}
               >
@@ -259,7 +246,7 @@ export class MarkdownTextArea extends Component<
                 accept="image/*,video/*"
                 name="file"
                 className="d-none"
-                disabled={UserService.Instance.myUserInfo.isNone()}
+                disabled={!UserService.Instance.myUserInfo}
                 onChange={linkEvent(this, this.handleImageUpload)}
               />
             </form>
@@ -376,13 +363,9 @@ export class MarkdownTextArea extends Component<
           let deleteToken = res.files[0].delete_token;
           let deleteUrl = `${pictrsUri}/delete/${deleteToken}/${hash}`;
           let imageMarkdown = `![](${url})`;
+          let content = i.state.content;
           i.setState({
-            content: Some(
-              i.state.content.match({
-                some: content => `${content}\n${imageMarkdown}`,
-                none: imageMarkdown,
-              })
-            ),
+            content: content ? `${content}\n${imageMarkdown}` : imageMarkdown,
             imageLoading: false,
           });
           i.contentChange();
@@ -407,13 +390,13 @@ export class MarkdownTextArea extends Component<
   }
 
   contentChange() {
-    if (this.props.onContentChange) {
-      this.props.onContentChange(toUndefined(this.state.content));
+    if (this.state.content) {
+      this.props.onContentChange?.(this.state.content);
     }
   }
 
   handleContentChange(i: MarkdownTextArea, event: any) {
-    i.setState({ content: Some(event.target.value) });
+    i.setState({ content: event.target.value });
     i.contentChange();
   }
 
@@ -423,7 +406,7 @@ export class MarkdownTextArea extends Component<
   }
 
   handleLanguageChange(val: number[]) {
-    this.setState({ languageId: Some(val[0]) });
+    this.setState({ languageId: val[0] });
   }
 
   handleSubmit(i: MarkdownTextArea, event: any) {
@@ -434,11 +417,11 @@ export class MarkdownTextArea extends Component<
       formId: i.formId,
       languageId: i.state.languageId,
     };
-    i.props.onSubmit(msg);
+    i.props.onSubmit?.(msg);
   }
 
   handleReplyCancel(i: MarkdownTextArea) {
-    i.props.onReplyCancel();
+    i.props.onReplyCancel?.();
   }
 
   handleInsertLink(i: MarkdownTextArea, event: any) {
@@ -448,25 +431,24 @@ export class MarkdownTextArea extends Component<
     let start: number = textarea.selectionStart;
     let end: number = textarea.selectionEnd;
 
-    if (i.state.content.isNone()) {
-      i.setState({ content: Some("") });
-    }
+    let content = i.state.content;
 
-    let content = i.state.content.unwrap();
+    if (!i.state.content) {
+      i.setState({ content: "" });
+    }
 
     if (start !== end) {
-      let selectedText = content.substring(start, end);
+      let selectedText = content?.substring(start, end);
       i.setState({
-        content: Some(
-          `${content.substring(0, start)}[${selectedText}]()${content.substring(
-            end
-          )}`
-        ),
+        content: `${content?.substring(
+          0,
+          start
+        )}[${selectedText}]()${content?.substring(end)}`,
       });
       textarea.focus();
       setTimeout(() => (textarea.selectionEnd = end + 3), 10);
     } else {
-      i.setState({ content: Some(`${content} []()`) });
+      i.setState({ content: `${content} []()` });
       textarea.focus();
       setTimeout(() => (textarea.selectionEnd -= 1), 10);
     }
@@ -486,28 +468,25 @@ export class MarkdownTextArea extends Component<
     afterChars: string,
     emptyChars = "___"
   ) {
-    if (this.state.content.isNone()) {
-      this.setState({ content: Some("") });
+    let content = this.state.content;
+    if (!this.state.content) {
+      this.setState({ content: "" });
     }
     let textarea: any = document.getElementById(this.id);
     let start: number = textarea.selectionStart;
     let end: number = textarea.selectionEnd;
 
-    let content = this.state.content.unwrap();
-
     if (start !== end) {
-      let selectedText = content.substring(start, end);
+      let selectedText = content?.substring(start, end);
       this.setState({
-        content: Some(
-          `${content.substring(
-            0,
-            start
-          )}${beforeChars}${selectedText}${afterChars}${content.substring(end)}`
-        ),
+        content: `${content?.substring(
+          0,
+          start
+        )}${beforeChars}${selectedText}${afterChars}${content?.substring(end)}`,
       });
     } else {
       this.setState({
-        content: Some(`${content}${beforeChars}${emptyChars}${afterChars}`),
+        content: `${content}${beforeChars}${emptyChars}${afterChars}`,
       });
     }
     this.contentChange();
@@ -581,11 +560,12 @@ export class MarkdownTextArea extends Component<
   }
 
   simpleInsert(chars: string) {
-    if (this.state.content.isNone()) {
-      this.setState({ content: Some(`${chars} `) });
+    let content = this.state.content;
+    if (!content) {
+      this.setState({ content: `${chars} ` });
     } else {
       this.setState({
-        content: Some(`${this.state.content.unwrap()}\n${chars} `),
+        content: `${content}\n${chars} `,
       });
     }
 
@@ -606,20 +586,21 @@ export class MarkdownTextArea extends Component<
 
   quoteInsert() {
     let textarea: any = document.getElementById(this.id);
-    let selectedText = window.getSelection().toString();
+    let selectedText = window.getSelection()?.toString();
+    let content = this.state.content;
     if (selectedText) {
       let quotedText =
         selectedText
           .split("\n")
           .map(t => `> ${t}`)
           .join("\n") + "\n\n";
-      if (this.state.content.isNone()) {
-        this.setState({ content: Some("") });
+      if (!content) {
+        this.setState({ content: "" });
       } else {
-        this.setState({ content: Some(`${this.state.content.unwrap()}\n`) });
+        this.setState({ content: `${content}\n` });
       }
       this.setState({
-        content: Some(`${this.state.content.unwrap()}${quotedText}`),
+        content: `${content}${quotedText}`,
       });
       this.contentChange();
       // Not sure why this needs a delay
@@ -631,8 +612,6 @@ export class MarkdownTextArea extends Component<
     let textarea: any = document.getElementById(this.id);
     let start: number = textarea.selectionStart;
     let end: number = textarea.selectionEnd;
-    return start !== end
-      ? this.state.content.unwrap().substring(start, end)
-      : "";
+    return start !== end ? this.state.content?.substring(start, end) ?? "" : "";
   }
 }
index 3d3c5485c9058176b276477a34a2d149fb1ef9a6..81aedd1df2c6b82c02c57a2071832f90471b2e27 100644 (file)
@@ -1,4 +1,3 @@
-import { Option } from "@sniptt/monads";
 import { Component } from "inferno";
 import moment from "moment";
 import { i18n } from "../../i18next";
@@ -7,7 +6,7 @@ import { Icon } from "./icon";
 
 interface MomentTimeProps {
   published: string;
-  updated: Option<string>;
+  updated?: string;
   showAgo?: boolean;
   ignoreUpdated?: boolean;
 }
@@ -22,22 +21,27 @@ export class MomentTime extends Component<MomentTimeProps, any> {
   }
 
   createdAndModifiedTimes() {
-    return `${capitalizeFirstLetter(i18n.t("created"))}: ${this.format(
+    let updated = this.props.updated;
+    let line = `${capitalizeFirstLetter(i18n.t("created"))}: ${this.format(
       this.props.published
-    )}\n\n\n${
-      this.props.updated.isSome() && capitalizeFirstLetter(i18n.t("modified"))
-    } ${this.format(this.props.updated.unwrap())}`;
+    )}`;
+    if (updated) {
+      line += `\n\n\n${capitalizeFirstLetter(i18n.t("modified"))} ${this.format(
+        updated
+      )}`;
+    }
+    return line;
   }
 
   render() {
-    if (!this.props.ignoreUpdated && this.props.updated.isSome()) {
+    if (!this.props.ignoreUpdated && this.props.updated) {
       return (
         <span
           data-tippy-content={this.createdAndModifiedTimes()}
           className="font-italics pointer unselectable"
         >
           <Icon icon="edit-2" classes="icon-inline mr-1" />
-          {moment.utc(this.props.updated.unwrap()).fromNow(!this.props.showAgo)}
+          {moment.utc(this.props.updated).fromNow(!this.props.showAgo)}
         </span>
       );
     } else {
index 050fe9613f1f91fc1f2cbe3dd82c670010dac00b..cbd8ffae247f9196a9e4134ab74894fd3f816588 100644 (file)
@@ -1,4 +1,3 @@
-import { None, Option, Some } from "@sniptt/monads";
 import { Component, linkEvent } from "inferno";
 import { T } from "inferno-i18next-dess";
 import {
@@ -7,7 +6,7 @@ import {
 } from "lemmy-js-client";
 import { i18n } from "../../i18next";
 import { WebSocketService } from "../../services";
-import { auth, mdToHtml, wsClient } from "../../utils";
+import { mdToHtml, myAuth, wsClient } from "../../utils";
 import { PersonListing } from "../person/person-listing";
 import { MarkdownTextArea } from "./markdown-textarea";
 import { MomentTime } from "./moment-time";
@@ -17,7 +16,7 @@ interface RegistrationApplicationProps {
 }
 
 interface RegistrationApplicationState {
-  denyReason: Option<string>;
+  denyReason?: string;
   denyExpanded: boolean;
 }
 
@@ -25,15 +24,13 @@ export class RegistrationApplication extends Component<
   RegistrationApplicationProps,
   RegistrationApplicationState
 > {
-  private emptyState: RegistrationApplicationState = {
+  state: RegistrationApplicationState = {
     denyReason: this.props.application.registration_application.deny_reason,
     denyExpanded: false,
   };
 
   constructor(props: any, context: any) {
     super(props, context);
-
-    this.state = this.emptyState;
     this.handleDenyReasonChange = this.handleDenyReasonChange.bind(this);
   }
 
@@ -48,44 +45,37 @@ export class RegistrationApplication extends Component<
           {i18n.t("applicant")}: <PersonListing person={a.creator} />
         </div>
         <div>
-          {i18n.t("created")}:{" "}
-          <MomentTime showAgo published={ra.published} updated={None} />
+          {i18n.t("created")}: <MomentTime showAgo published={ra.published} />
         </div>
         <div>{i18n.t("answer")}:</div>
         <div className="md-div" dangerouslySetInnerHTML={mdToHtml(ra.answer)} />
 
-        {a.admin.match({
-          some: admin => (
-            <div>
-              {accepted ? (
-                <T i18nKey="approved_by">
+        {a.admin && (
+          <div>
+            {accepted ? (
+              <T i18nKey="approved_by">
+                #
+                <PersonListing person={a.admin} />
+              </T>
+            ) : (
+              <div>
+                <T i18nKey="denied_by">
                   #
-                  <PersonListing person={admin} />
+                  <PersonListing person={a.admin} />
                 </T>
-              ) : (
-                <div>
-                  <T i18nKey="denied_by">
-                    #
-                    <PersonListing person={admin} />
-                  </T>
-                  {ra.deny_reason.match({
-                    some: deny_reason => (
-                      <div>
-                        {i18n.t("deny_reason")}:{" "}
-                        <div
-                          className="md-div d-inline-flex"
-                          dangerouslySetInnerHTML={mdToHtml(deny_reason)}
-                        />
-                      </div>
-                    ),
-                    none: <></>,
-                  })}
-                </div>
-              )}
-            </div>
-          ),
-          none: <></>,
-        })}
+                {ra.deny_reason && (
+                  <div>
+                    {i18n.t("deny_reason")}:{" "}
+                    <div
+                      className="md-div d-inline-flex"
+                      dangerouslySetInnerHTML={mdToHtml(ra.deny_reason)}
+                    />
+                  </div>
+                )}
+              </div>
+            )}
+          </div>
+        )}
 
         {this.state.denyExpanded && (
           <div className="form-group row">
@@ -95,11 +85,7 @@ export class RegistrationApplication extends Component<
             <div className="col-sm-10">
               <MarkdownTextArea
                 initialContent={this.state.denyReason}
-                initialLanguageId={None}
                 onContentChange={this.handleDenyReasonChange}
-                placeholder={None}
-                buttonTitle={None}
-                maxLength={None}
                 hideNavigationWarnings
                 allLanguages={[]}
                 siteLanguages={[]}
@@ -107,7 +93,7 @@ export class RegistrationApplication extends Component<
             </div>
           </div>
         )}
-        {(ra.admin_id.isNone() || (ra.admin_id.isSome() && !accepted)) && (
+        {(!ra.admin_id || (ra.admin_id && !accepted)) && (
           <button
             className="btn btn-secondary mr-2 my-2"
             onClick={linkEvent(this, this.handleApprove)}
@@ -116,7 +102,7 @@ export class RegistrationApplication extends Component<
             {i18n.t("approve")}
           </button>
         )}
-        {(ra.admin_id.isNone() || (ra.admin_id.isSome() && accepted)) && (
+        {(!ra.admin_id || (ra.admin_id && accepted)) && (
           <button
             className="btn btn-secondary mr-2"
             onClick={linkEvent(this, this.handleDeny)}
@@ -130,36 +116,41 @@ export class RegistrationApplication extends Component<
   }
 
   handleApprove(i: RegistrationApplication) {
-    i.setState({ denyExpanded: false });
-    let form = new ApproveRegistrationApplication({
-      id: i.props.application.registration_application.id,
-      deny_reason: None,
-      approve: true,
-      auth: auth().unwrap(),
-    });
-    WebSocketService.Instance.send(
-      wsClient.approveRegistrationApplication(form)
-    );
-  }
-
-  handleDeny(i: RegistrationApplication) {
-    if (i.state.denyExpanded) {
+    let auth = myAuth();
+    if (auth) {
       i.setState({ denyExpanded: false });
-      let form = new ApproveRegistrationApplication({
+      let form: ApproveRegistrationApplication = {
         id: i.props.application.registration_application.id,
-        approve: false,
-        deny_reason: i.state.denyReason,
-        auth: auth().unwrap(),
-      });
+        approve: true,
+        auth,
+      };
       WebSocketService.Instance.send(
         wsClient.approveRegistrationApplication(form)
       );
+    }
+  }
+
+  handleDeny(i: RegistrationApplication) {
+    if (i.state.denyExpanded) {
+      i.setState({ denyExpanded: false });
+      let auth = myAuth();
+      if (auth) {
+        let form: ApproveRegistrationApplication = {
+          id: i.props.application.registration_application.id,
+          approve: false,
+          deny_reason: i.state.denyReason,
+          auth,
+        };
+        WebSocketService.Instance.send(
+          wsClient.approveRegistrationApplication(form)
+        );
+      }
     } else {
       i.setState({ denyExpanded: true });
     }
   }
 
   handleDenyReasonChange(val: string) {
-    this.setState({ denyReason: Some(val) });
+    this.setState({ denyReason: val });
   }
 }
index facc16f47bebf79d1be04e3653ebaf1613be65c8..c0d277f0811bd3de37e9765b22104980d3723d66 100644 (file)
@@ -17,13 +17,12 @@ interface SortSelectState {
 
 export class SortSelect extends Component<SortSelectProps, SortSelectState> {
   private id = `sort-select-${randomStr()}`;
-  private emptyState: SortSelectState = {
+  state: SortSelectState = {
     sort: this.props.sort,
   };
 
   constructor(props: any, context: any) {
     super(props, context);
-    this.state = this.emptyState;
   }
 
   static getDerivedStateFromProps(props: any): SortSelectState {
@@ -86,6 +85,6 @@ export class SortSelect extends Component<SortSelectProps, SortSelectState> {
   }
 
   handleSortChange(i: SortSelect, event: any) {
-    i.props.onChange(event.target.value);
+    i.props.onChange?.(event.target.value);
   }
 }
index c33963a08c7afa18a1dc6f2353deb57cc3ea9b49..e6cdc454eaf7b267dc74d4ff9d88dee60ef52e9b 100644 (file)
@@ -1,4 +1,3 @@
-import { None, Option, Some } from "@sniptt/monads";
 import { Component, linkEvent } from "inferno";
 import {
   CommunityResponse,
@@ -18,10 +17,10 @@ import { InitialFetchRequest } from "shared/interfaces";
 import { i18n } from "../../i18next";
 import { WebSocketService } from "../../services";
 import {
-  auth,
   getListingTypeFromPropsNoDefault,
   getPageFromProps,
   isBrowser,
+  myAuth,
   numToSI,
   setIsoData,
   showLocal,
@@ -38,7 +37,7 @@ import { CommunityLink } from "./community-link";
 const communityLimit = 50;
 
 interface CommunitiesState {
-  listCommunitiesResponse: Option<ListCommunitiesResponse>;
+  listCommunitiesResponse?: ListCommunitiesResponse;
   page: number;
   loading: boolean;
   siteRes: GetSiteResponse;
@@ -52,10 +51,9 @@ interface CommunitiesProps {
 }
 
 export class Communities extends Component<any, CommunitiesState> {
-  private subscription: Subscription;
-  private isoData = setIsoData(this.context, ListCommunitiesResponse);
-  private emptyState: CommunitiesState = {
-    listCommunitiesResponse: None,
+  private subscription?: Subscription;
+  private isoData = setIsoData(this.context);
+  state: CommunitiesState = {
     loading: true,
     page: getPageFromProps(this.props),
     listingType: getListingTypeFromPropsNoDefault(this.props),
@@ -65,7 +63,6 @@ export class Communities extends Component<any, CommunitiesState> {
 
   constructor(props: any, context: any) {
     super(props, context);
-    this.state = this.emptyState;
     this.handlePageChange = this.handlePageChange.bind(this);
     this.handleListingTypeChange = this.handleListingTypeChange.bind(this);
 
@@ -74,7 +71,7 @@ export class Communities extends Component<any, CommunitiesState> {
 
     // Only fetch the data if coming from another route
     if (this.isoData.path == this.context.router.route.match.url) {
-      let listRes = Some(this.isoData.routeData[0] as ListCommunitiesResponse);
+      let listRes = this.isoData.routeData[0] as ListCommunitiesResponse;
       this.state = {
         ...this.state,
         listCommunitiesResponse: listRes,
@@ -87,7 +84,7 @@ export class Communities extends Component<any, CommunitiesState> {
 
   componentWillUnmount() {
     if (isBrowser()) {
-      this.subscription.unsubscribe();
+      this.subscription?.unsubscribe();
     }
   }
 
@@ -120,8 +117,6 @@ export class Communities extends Component<any, CommunitiesState> {
         <HtmlTags
           title={this.documentTitle}
           path={this.context.router.route.match.url}
-          description={None}
-          image={None}
         />
         {this.state.loading ? (
           <h5>
@@ -168,57 +163,54 @@ export class Communities extends Component<any, CommunitiesState> {
                   </tr>
                 </thead>
                 <tbody>
-                  {this.state.listCommunitiesResponse
-                    .map(l => l.communities)
-                    .unwrapOr([])
-                    .map(cv => (
-                      <tr key={cv.community.id}>
-                        <td>
-                          <CommunityLink community={cv.community} />
-                        </td>
-                        <td className="text-right">
-                          {numToSI(cv.counts.subscribers)}
-                        </td>
-                        <td className="text-right">
-                          {numToSI(cv.counts.users_active_month)}
-                        </td>
-                        <td className="text-right d-none d-lg-table-cell">
-                          {numToSI(cv.counts.posts)}
-                        </td>
-                        <td className="text-right d-none d-lg-table-cell">
-                          {numToSI(cv.counts.comments)}
-                        </td>
-                        <td className="text-right">
-                          {cv.subscribed == SubscribedType.Subscribed && (
-                            <button
-                              className="btn btn-link d-inline-block"
-                              onClick={linkEvent(
-                                cv.community.id,
-                                this.handleUnsubscribe
-                              )}
-                            >
-                              {i18n.t("unsubscribe")}
-                            </button>
-                          )}
-                          {cv.subscribed == SubscribedType.NotSubscribed && (
-                            <button
-                              className="btn btn-link d-inline-block"
-                              onClick={linkEvent(
-                                cv.community.id,
-                                this.handleSubscribe
-                              )}
-                            >
-                              {i18n.t("subscribe")}
-                            </button>
-                          )}
-                          {cv.subscribed == SubscribedType.Pending && (
-                            <div className="text-warning d-inline-block">
-                              {i18n.t("subscribe_pending")}
-                            </div>
-                          )}
-                        </td>
-                      </tr>
-                    ))}
+                  {this.state.listCommunitiesResponse?.communities.map(cv => (
+                    <tr key={cv.community.id}>
+                      <td>
+                        <CommunityLink community={cv.community} />
+                      </td>
+                      <td className="text-right">
+                        {numToSI(cv.counts.subscribers)}
+                      </td>
+                      <td className="text-right">
+                        {numToSI(cv.counts.users_active_month)}
+                      </td>
+                      <td className="text-right d-none d-lg-table-cell">
+                        {numToSI(cv.counts.posts)}
+                      </td>
+                      <td className="text-right d-none d-lg-table-cell">
+                        {numToSI(cv.counts.comments)}
+                      </td>
+                      <td className="text-right">
+                        {cv.subscribed == SubscribedType.Subscribed && (
+                          <button
+                            className="btn btn-link d-inline-block"
+                            onClick={linkEvent(
+                              cv.community.id,
+                              this.handleUnsubscribe
+                            )}
+                          >
+                            {i18n.t("unsubscribe")}
+                          </button>
+                        )}
+                        {cv.subscribed == SubscribedType.NotSubscribed && (
+                          <button
+                            className="btn btn-link d-inline-block"
+                            onClick={linkEvent(
+                              cv.community.id,
+                              this.handleSubscribe
+                            )}
+                          >
+                            {i18n.t("subscribe")}
+                          </button>
+                        )}
+                        {cv.subscribed == SubscribedType.Pending && (
+                          <div className="text-warning d-inline-block">
+                            {i18n.t("subscribe_pending")}
+                          </div>
+                        )}
+                      </td>
+                    </tr>
+                  ))}
                 </tbody>
               </table>
             </div>
@@ -278,21 +270,27 @@ export class Communities extends Component<any, CommunitiesState> {
   }
 
   handleUnsubscribe(communityId: number) {
-    let form = new FollowCommunity({
-      community_id: communityId,
-      follow: false,
-      auth: auth().unwrap(),
-    });
-    WebSocketService.Instance.send(wsClient.followCommunity(form));
+    let auth = myAuth();
+    if (auth) {
+      let form: FollowCommunity = {
+        community_id: communityId,
+        follow: false,
+        auth,
+      };
+      WebSocketService.Instance.send(wsClient.followCommunity(form));
+    }
   }
 
   handleSubscribe(communityId: number) {
-    let form = new FollowCommunity({
-      community_id: communityId,
-      follow: true,
-      auth: auth().unwrap(),
-    });
-    WebSocketService.Instance.send(wsClient.followCommunity(form));
+    let auth = myAuth();
+    if (auth) {
+      let form: FollowCommunity = {
+        community_id: communityId,
+        follow: true,
+        auth,
+      };
+      WebSocketService.Instance.send(wsClient.followCommunity(form));
+    }
   }
 
   handleSearchChange(i: Communities, event: any) {
@@ -307,13 +305,13 @@ export class Communities extends Component<any, CommunitiesState> {
   }
 
   refetch() {
-    let listCommunitiesForm = new ListCommunities({
-      type_: Some(this.state.listingType),
-      sort: Some(SortType.TopMonth),
-      limit: Some(communityLimit),
-      page: Some(this.state.page),
-      auth: auth(false).ok(),
-    });
+    let listCommunitiesForm: ListCommunities = {
+      type_: this.state.listingType,
+      sort: SortType.TopMonth,
+      limit: communityLimit,
+      page: this.state.page,
+      auth: myAuth(false),
+    };
 
     WebSocketService.Instance.send(
       wsClient.listCommunities(listCommunitiesForm)
@@ -322,17 +320,17 @@ export class Communities extends Component<any, CommunitiesState> {
 
   static fetchInitialData(req: InitialFetchRequest): Promise<any>[] {
     let pathSplit = req.path.split("/");
-    let type_: Option<ListingType> = Some(
-      pathSplit[3] ? ListingType[pathSplit[3]] : ListingType.Local
-    );
-    let page = Some(pathSplit[5] ? Number(pathSplit[5]) : 1);
-    let listCommunitiesForm = new ListCommunities({
+    let type_: ListingType = pathSplit[3]
+      ? ListingType[pathSplit[3]]
+      : ListingType.Local;
+    let page = pathSplit[5] ? Number(pathSplit[5]) : 1;
+    let listCommunitiesForm: ListCommunities = {
       type_,
-      sort: Some(SortType.TopMonth),
-      limit: Some(communityLimit),
+      sort: SortType.TopMonth,
+      limit: communityLimit,
       page,
       auth: req.auth,
-    });
+    };
 
     return [req.client.listCommunities(listCommunitiesForm)];
   }
@@ -344,25 +342,20 @@ export class Communities extends Component<any, CommunitiesState> {
       toast(i18n.t(msg.error), "danger");
       return;
     } else if (op == UserOperation.ListCommunities) {
-      let data = wsJsonToRes<ListCommunitiesResponse>(
-        msg,
-        ListCommunitiesResponse
-      );
-      this.setState({ listCommunitiesResponse: Some(data), loading: false });
+      let data = wsJsonToRes<ListCommunitiesResponse>(msg);
+      this.setState({ listCommunitiesResponse: data, loading: false });
       window.scrollTo(0, 0);
     } else if (op == UserOperation.FollowCommunity) {
-      let data = wsJsonToRes<CommunityResponse>(msg, CommunityResponse);
-      this.state.listCommunitiesResponse.match({
-        some: res => {
-          let found = res.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;
-        },
-        none: void 0,
-      });
-      this.setState(this.state);
+      let data = wsJsonToRes<CommunityResponse>(msg);
+      let res = this.state.listCommunitiesResponse;
+      let found = res?.communities.find(
+        c => c.community.id == data.community_view.community.id
+      );
+      if (found) {
+        found.subscribed = data.community_view.subscribed;
+        found.counts.subscribers = data.community_view.counts.subscribers;
+        this.setState(this.state);
+      }
     }
   }
 }
index 805e49638c457f0adf73759d85a3a6d82e711504..c6ba5d850d79bc4c4bed1d14ed7e3afac8fcab0d 100644 (file)
@@ -1,4 +1,3 @@
-import { None, Option, Some } from "@sniptt/monads";
 import { Component, linkEvent } from "inferno";
 import { Prompt } from "inferno-router";
 import {
@@ -7,7 +6,6 @@ import {
   CreateCommunity,
   EditCommunity,
   Language,
-  toUndefined,
   UserOperation,
   wsJsonToRes,
   wsUserOp,
@@ -16,8 +14,8 @@ import { Subscription } from "rxjs";
 import { i18n } from "../../i18next";
 import { UserService, WebSocketService } from "../../services";
 import {
-  auth,
   capitalizeFirstLetter,
+  myAuth,
   randomStr,
   wsClient,
   wsSubscribe,
@@ -28,10 +26,10 @@ import { LanguageSelect } from "../common/language-select";
 import { MarkdownTextArea } from "../common/markdown-textarea";
 
 interface CommunityFormProps {
-  community_view: Option<CommunityView>; // 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
   allLanguages: Language[];
   siteLanguages: number[];
-  communityLanguages: Option<number[]>;
+  communityLanguages?: number[];
   onCancel?(): any;
   onCreate?(community: CommunityView): any;
   onEdit?(community: CommunityView): any;
@@ -39,7 +37,16 @@ interface CommunityFormProps {
 }
 
 interface CommunityFormState {
-  communityForm: CreateCommunity;
+  form: {
+    name?: string;
+    title?: string;
+    description?: string;
+    icon?: string;
+    banner?: string;
+    nsfw?: boolean;
+    posting_restricted_to_mods?: boolean;
+    discussion_languages?: number[];
+  };
   loading: boolean;
 }
 
@@ -48,28 +55,16 @@ export class CommunityForm extends Component<
   CommunityFormState
 > {
   private id = `community-form-${randomStr()}`;
-  private subscription: Subscription;
-
-  private emptyState: CommunityFormState = {
-    communityForm: new CreateCommunity({
-      name: undefined,
-      title: undefined,
-      description: None,
-      discussion_languages: this.props.communityLanguages,
-      nsfw: None,
-      icon: None,
-      banner: None,
-      posting_restricted_to_mods: None,
-      auth: undefined,
-    }),
+  private subscription?: Subscription;
+
+  state: CommunityFormState = {
+    form: {},
     loading: false,
   };
 
   constructor(props: any, context: any) {
     super(props, context);
 
-    this.state = this.emptyState;
-
     this.handleCommunityDescriptionChange =
       this.handleCommunityDescriptionChange.bind(this);
 
@@ -84,24 +79,21 @@ export class CommunityForm extends Component<
 
     this.parseMessage = this.parseMessage.bind(this);
     this.subscription = wsSubscribe(this.parseMessage);
+    let cv = this.props.community_view;
 
-    if (this.props.community_view.isSome()) {
-      let cv = this.props.community_view.unwrap();
+    if (cv) {
       this.state = {
-        ...this.state,
-        communityForm: new CreateCommunity({
+        form: {
           name: cv.community.name,
           title: cv.community.title,
           description: cv.community.description,
-          nsfw: Some(cv.community.nsfw),
+          nsfw: cv.community.nsfw,
           icon: cv.community.icon,
           banner: cv.community.banner,
-          posting_restricted_to_mods: Some(
-            cv.community.posting_restricted_to_mods
-          ),
+          posting_restricted_to_mods: cv.community.posting_restricted_to_mods,
           discussion_languages: this.props.communityLanguages,
-          auth: undefined,
-        }),
+        },
+        loading: false,
       };
     }
   }
@@ -109,18 +101,18 @@ export class CommunityForm extends Component<
   componentDidUpdate() {
     if (
       !this.state.loading &&
-      (this.state.communityForm.name ||
-        this.state.communityForm.title ||
-        this.state.communityForm.description.isSome())
+      (this.state.form.name ||
+        this.state.form.title ||
+        this.state.form.description)
     ) {
       window.onbeforeunload = () => true;
     } else {
-      window.onbeforeunload = undefined;
+      window.onbeforeunload = null;
     }
   }
 
   componentWillUnmount() {
-    this.subscription.unsubscribe();
+    this.subscription?.unsubscribe();
     window.onbeforeunload = null;
   }
 
@@ -130,14 +122,14 @@ export class CommunityForm extends Component<
         <Prompt
           when={
             !this.state.loading &&
-            (this.state.communityForm.name ||
-              this.state.communityForm.title ||
-              this.state.communityForm.description.isSome())
+            (this.state.form.name ||
+              this.state.form.title ||
+              this.state.form.description)
           }
           message={i18n.t("block_leaving")}
         />
         <form onSubmit={linkEvent(this, this.handleCreateCommunitySubmit)}>
-          {this.props.community_view.isNone() && (
+          {!this.props.community_view && (
             <div className="form-group row">
               <label
                 className="col-12 col-sm-2 col-form-label"
@@ -156,7 +148,7 @@ export class CommunityForm extends Component<
                   type="text"
                   id="community-name"
                   className="form-control"
-                  value={this.state.communityForm.name}
+                  value={this.state.form.name}
                   onInput={linkEvent(this, this.handleCommunityNameChange)}
                   required
                   minLength={3}
@@ -183,7 +175,7 @@ export class CommunityForm extends Component<
               <input
                 type="text"
                 id="community-title"
-                value={this.state.communityForm.title}
+                value={this.state.form.title}
                 onInput={linkEvent(this, this.handleCommunityTitleChange)}
                 className="form-control"
                 required
@@ -197,7 +189,7 @@ export class CommunityForm extends Component<
             <div className="col-12 col-sm-10">
               <ImageUploadForm
                 uploadTitle={i18n.t("upload_icon")}
-                imageSrc={this.state.communityForm.icon}
+                imageSrc={this.state.form.icon}
                 onUpload={this.handleIconUpload}
                 onRemove={this.handleIconRemove}
                 rounded
@@ -209,7 +201,7 @@ export class CommunityForm extends Component<
             <div className="col-12 col-sm-10">
               <ImageUploadForm
                 uploadTitle={i18n.t("upload_banner")}
-                imageSrc={this.state.communityForm.banner}
+                imageSrc={this.state.form.banner}
                 onUpload={this.handleBannerUpload}
                 onRemove={this.handleBannerRemove}
               />
@@ -221,11 +213,8 @@ export class CommunityForm extends Component<
             </label>
             <div className="col-12 col-sm-10">
               <MarkdownTextArea
-                initialContent={this.state.communityForm.description}
-                initialLanguageId={None}
-                placeholder={Some("description")}
-                buttonTitle={None}
-                maxLength={None}
+                initialContent={this.state.form.description}
+                placeholder={i18n.t("description")}
                 onContentChange={this.handleCommunityDescriptionChange}
                 allLanguages={[]}
                 siteLanguages={[]}
@@ -244,7 +233,7 @@ export class CommunityForm extends Component<
                     className="form-check-input position-static"
                     id="community-nsfw"
                     type="checkbox"
-                    checked={toUndefined(this.state.communityForm.nsfw)}
+                    checked={this.state.form.nsfw}
                     onChange={linkEvent(this, this.handleCommunityNsfwChange)}
                   />
                 </div>
@@ -261,9 +250,7 @@ export class CommunityForm extends Component<
                   className="form-check-input position-static"
                   id="community-only-mods-can-post"
                   type="checkbox"
-                  checked={toUndefined(
-                    this.state.communityForm.posting_restricted_to_mods
-                  )}
+                  checked={this.state.form.posting_restricted_to_mods}
                   onChange={linkEvent(
                     this,
                     this.handleCommunityPostingRestrictedToMods
@@ -276,7 +263,7 @@ export class CommunityForm extends Component<
             allLanguages={this.props.allLanguages}
             siteLanguages={this.props.siteLanguages}
             showSite
-            selectedLanguageIds={this.state.communityForm.discussion_languages}
+            selectedLanguageIds={this.state.form.discussion_languages}
             multiple={true}
             onChange={this.handleDiscussionLanguageChange}
           />
@@ -289,13 +276,13 @@ export class CommunityForm extends Component<
               >
                 {this.state.loading ? (
                   <Spinner />
-                ) : this.props.community_view.isSome() ? (
+                ) : this.props.community_view ? (
                   capitalizeFirstLetter(i18n.t("save"))
                 ) : (
                   capitalizeFirstLetter(i18n.t("create"))
                 )}
               </button>
-              {this.props.community_view.isSome() && (
+              {this.props.community_view && (
                 <button
                   type="button"
                   className="btn btn-secondary"
@@ -314,82 +301,92 @@ export class CommunityForm extends Component<
   handleCreateCommunitySubmit(i: CommunityForm, event: any) {
     event.preventDefault();
     i.setState({ loading: true });
-    let cForm = i.state.communityForm;
-    cForm.auth = auth().unwrap();
+    let cForm = i.state.form;
+    let auth = myAuth();
 
-    i.props.community_view.match({
-      some: cv => {
-        let form = new EditCommunity({
+    let cv = i.props.community_view;
+
+    if (auth) {
+      if (cv) {
+        let form: EditCommunity = {
           community_id: cv.community.id,
-          title: Some(cForm.title),
+          title: cForm.title,
           description: cForm.description,
           icon: cForm.icon,
           banner: cForm.banner,
           nsfw: cForm.nsfw,
           posting_restricted_to_mods: cForm.posting_restricted_to_mods,
           discussion_languages: cForm.discussion_languages,
-          auth: cForm.auth,
-        });
+          auth,
+        };
 
         WebSocketService.Instance.send(wsClient.editCommunity(form));
-      },
-      none: () => {
-        WebSocketService.Instance.send(
-          wsClient.createCommunity(i.state.communityForm)
-        );
-      },
-    });
+      } else {
+        if (cForm.title && cForm.name) {
+          let form: CreateCommunity = {
+            name: cForm.name,
+            title: cForm.title,
+            description: cForm.description,
+            icon: cForm.icon,
+            banner: cForm.banner,
+            nsfw: cForm.nsfw,
+            posting_restricted_to_mods: cForm.posting_restricted_to_mods,
+            discussion_languages: cForm.discussion_languages,
+            auth,
+          };
+          WebSocketService.Instance.send(wsClient.createCommunity(form));
+        }
+      }
+    }
     i.setState(i.state);
   }
 
   handleCommunityNameChange(i: CommunityForm, event: any) {
-    i.state.communityForm.name = event.target.value;
+    i.state.form.name = event.target.value;
     i.setState(i.state);
   }
 
   handleCommunityTitleChange(i: CommunityForm, event: any) {
-    i.state.communityForm.title = event.target.value;
+    i.state.form.title = event.target.value;
     i.setState(i.state);
   }
 
   handleCommunityDescriptionChange(val: string) {
-    this.setState(s => ((s.communityForm.description = Some(val)), s));
+    this.setState(s => ((s.form.description = val), s));
   }
 
   handleCommunityNsfwChange(i: CommunityForm, event: any) {
-    i.state.communityForm.nsfw = Some(event.target.checked);
+    i.state.form.nsfw = event.target.checked;
     i.setState(i.state);
   }
 
   handleCommunityPostingRestrictedToMods(i: CommunityForm, event: any) {
-    i.state.communityForm.posting_restricted_to_mods = Some(
-      event.target.checked
-    );
+    i.state.form.posting_restricted_to_mods = event.target.checked;
     i.setState(i.state);
   }
 
   handleCancel(i: CommunityForm) {
-    i.props.onCancel();
+    i.props.onCancel?.();
   }
 
   handleIconUpload(url: string) {
-    this.setState(s => ((s.communityForm.icon = Some(url)), s));
+    this.setState(s => ((s.form.icon = url), s));
   }
 
   handleIconRemove() {
-    this.setState(s => ((s.communityForm.icon = Some("")), s));
+    this.setState(s => ((s.form.icon = ""), s));
   }
 
   handleBannerUpload(url: string) {
-    this.setState(s => ((s.communityForm.banner = Some(url)), s));
+    this.setState(s => ((s.form.banner = url), s));
   }
 
   handleBannerRemove() {
-    this.setState(s => ((s.communityForm.banner = Some("")), s));
+    this.setState(s => ((s.form.banner = ""), s));
   }
 
   handleDiscussionLanguageChange(val: number[]) {
-    this.setState(s => ((s.communityForm.discussion_languages = Some(val)), s));
+    this.setState(s => ((s.form.discussion_languages = val), s));
   }
 
   parseMessage(msg: any) {
@@ -401,50 +398,46 @@ export class CommunityForm extends Component<
       this.setState({ loading: false });
       return;
     } else if (op == UserOperation.CreateCommunity) {
-      let data = wsJsonToRes<CommunityResponse>(msg, CommunityResponse);
-      this.props.onCreate(data.community_view);
+      let data = wsJsonToRes<CommunityResponse>(msg);
+      this.props.onCreate?.(data.community_view);
 
       // Update myUserInfo
       let community = data.community_view.community;
 
-      UserService.Instance.myUserInfo.match({
-        some: mui => {
-          let person = mui.local_user_view.person;
-          mui.follows.push({
-            community,
-            follower: person,
-          });
-          mui.moderates.push({
-            community,
-            moderator: person,
-          });
-        },
-        none: void 0,
-      });
+      let mui = UserService.Instance.myUserInfo;
+      if (mui) {
+        let person = mui.local_user_view.person;
+        mui.follows.push({
+          community,
+          follower: person,
+        });
+        mui.moderates.push({
+          community,
+          moderator: person,
+        });
+      }
     } else if (op == UserOperation.EditCommunity) {
-      let data = wsJsonToRes<CommunityResponse>(msg, CommunityResponse);
+      let data = wsJsonToRes<CommunityResponse>(msg);
       this.setState({ loading: false });
-      this.props.onEdit(data.community_view);
+      this.props.onEdit?.(data.community_view);
       let community = data.community_view.community;
 
-      UserService.Instance.myUserInfo.match({
-        some: mui => {
-          let followFound = mui.follows.findIndex(
-            f => f.community.id == community.id
-          );
-          if (followFound) {
-            mui.follows[followFound].community = community;
-          }
+      let mui = UserService.Instance.myUserInfo;
+      if (mui) {
+        let followFound = mui.follows.findIndex(
+          f => f.community.id == community.id
+        );
+        if (followFound) {
+          mui.follows[followFound].community = community;
+        }
 
-          let moderatesFound = mui.moderates.findIndex(
-            f => f.community.id == community.id
-          );
-          if (moderatesFound) {
-            mui.moderates[moderatesFound].community = community;
-          }
-        },
-        none: void 0,
-      });
+        let moderatesFound = mui.moderates.findIndex(
+          f => f.community.id == community.id
+        );
+        if (moderatesFound) {
+          mui.moderates[moderatesFound].community = community;
+        }
+      }
     }
   }
 }
index b051ed8aac36d85e72eafa7a9b981c30301362e3..b65e3d1071e0b60d35741651e6a49a23688ea975 100644 (file)
@@ -5,7 +5,6 @@ import { hostname, relTags, showAvatars } from "../../utils";
 import { PictrsImage } from "../common/pictrs-image";
 
 interface CommunityLinkProps {
-  // TODO figure this out better
   community: CommunitySafe;
   realLink?: boolean;
   useApubName?: boolean;
@@ -56,14 +55,12 @@ export class CommunityLink extends Component<CommunityLinkProps, any> {
   }
 
   avatarAndName(displayName: string) {
+    let icon = this.props.community.icon;
     return (
       <>
-        {!this.props.hideAvatar &&
-          showAvatars() &&
-          this.props.community.icon.match({
-            some: icon => <PictrsImage src={icon} icon />,
-            none: <></>,
-          })}
+        {!this.props.hideAvatar && showAvatars() && icon && (
+          <PictrsImage src={icon} icon />
+        )}
         <span className="overflow-wrap-anywhere">{displayName}</span>
       </>
     );
index 7ce6099988d9c9a866a3906532ea4e0fac3d88ba..5880ee7f7c7da8d6baf7637d972c06b8e25f2e12 100644 (file)
@@ -1,4 +1,3 @@
-import { None, Option, Some } from "@sniptt/monads";
 import { Component, linkEvent } from "inferno";
 import {
   AddModToCommunityResponse,
@@ -22,7 +21,6 @@ import {
   PostView,
   PurgeItemResponse,
   SortType,
-  toOption,
   UserOperation,
   wsJsonToRes,
   wsUserOp,
@@ -36,7 +34,6 @@ import {
 } from "../../interfaces";
 import { UserService, WebSocketService } from "../../services";
 import {
-  auth,
   commentsToFlatNodes,
   communityRSSUrl,
   createCommentLikeRes,
@@ -50,6 +47,7 @@ import {
   getPageFromProps,
   getSortTypeFromProps,
   isPostBlocked,
+  myAuth,
   notifyPost,
   nsfwCheck,
   postToCommentSortType,
@@ -79,7 +77,7 @@ import { PostListings } from "../post/post-listings";
 import { CommunityLink } from "./community-link";
 
 interface State {
-  communityRes: Option<GetCommunityResponse>;
+  communityRes?: GetCommunityResponse;
   siteRes: GetSiteResponse;
   communityName: string;
   communityLoading: boolean;
@@ -106,15 +104,9 @@ interface UrlParams {
 }
 
 export class Community extends Component<any, State> {
-  private isoData = setIsoData(
-    this.context,
-    GetCommunityResponse,
-    GetPostsResponse,
-    GetCommentsResponse
-  );
-  private subscription: Subscription;
-  private emptyState: State = {
-    communityRes: None,
+  private isoData = setIsoData(this.context);
+  private subscription?: Subscription;
+  state: State = {
     communityName: this.props.match.params.name,
     communityLoading: true,
     postsLoading: true,
@@ -131,7 +123,6 @@ export class Community extends Component<any, State> {
   constructor(props: any, context: any) {
     super(props, context);
 
-    this.state = this.emptyState;
     this.handleSortChange = this.handleSortChange.bind(this);
     this.handleDataTypeChange = this.handleDataTypeChange.bind(this);
     this.handlePageChange = this.handlePageChange.bind(this);
@@ -143,17 +134,19 @@ export class Community extends Component<any, State> {
     if (this.isoData.path == this.context.router.route.match.url) {
       this.state = {
         ...this.state,
-        communityRes: Some(this.isoData.routeData[0] as GetCommunityResponse),
+        communityRes: this.isoData.routeData[0] as GetCommunityResponse,
       };
-      let postsRes = Some(this.isoData.routeData[1] as GetPostsResponse);
-      let commentsRes = Some(this.isoData.routeData[2] as GetCommentsResponse);
+      let postsRes = this.isoData.routeData[1] as GetPostsResponse | undefined;
+      let commentsRes = this.isoData.routeData[2] as
+        | GetCommentsResponse
+        | undefined;
 
-      if (postsRes.isSome()) {
-        this.state = { ...this.state, posts: postsRes.unwrap().posts };
+      if (postsRes) {
+        this.state = { ...this.state, posts: postsRes.posts };
       }
 
-      if (commentsRes.isSome()) {
-        this.state = { ...this.state, comments: commentsRes.unwrap().comments };
+      if (commentsRes) {
+        this.state = { ...this.state, comments: commentsRes.comments };
       }
 
       this.state = {
@@ -169,11 +162,10 @@ export class Community extends Component<any, State> {
   }
 
   fetchCommunity() {
-    let form = new GetCommunity({
-      name: Some(this.state.communityName),
-      id: None,
-      auth: auth(false).ok(),
-    });
+    let form: GetCommunity = {
+      name: this.state.communityName,
+      auth: myAuth(false),
+    };
     WebSocketService.Instance.send(wsClient.getCommunity(form));
   }
 
@@ -183,7 +175,7 @@ export class Community extends Component<any, State> {
 
   componentWillUnmount() {
     saveScrollPosition(this.context);
-    this.subscription.unsubscribe();
+    this.subscription?.unsubscribe();
   }
 
   static getDerivedStateFromProps(props: any): CommunityProps {
@@ -199,58 +191,50 @@ export class Community extends Component<any, State> {
     let promises: Promise<any>[] = [];
 
     let communityName = pathSplit[2];
-    let communityForm = new GetCommunity({
-      name: Some(communityName),
-      id: None,
+    let communityForm: GetCommunity = {
+      name: communityName,
       auth: req.auth,
-    });
+    };
     promises.push(req.client.getCommunity(communityForm));
 
     let dataType: DataType = pathSplit[4]
       ? DataType[pathSplit[4]]
       : DataType.Post;
 
-    let sort: Option<SortType> = toOption(
-      pathSplit[6]
-        ? SortType[pathSplit[6]]
-        : UserService.Instance.myUserInfo.match({
-            some: mui =>
-              Object.values(SortType)[
-                mui.local_user_view.local_user.default_sort_type
-              ],
-            none: SortType.Active,
-          })
-    );
+    let mui = UserService.Instance.myUserInfo;
+
+    let sort: SortType = pathSplit[6]
+      ? SortType[pathSplit[6]]
+      : mui
+      ? Object.values(SortType)[
+          mui.local_user_view.local_user.default_sort_type
+        ]
+      : SortType.Active;
 
-    let page = toOption(pathSplit[8] ? Number(pathSplit[8]) : 1);
+    let page = pathSplit[8] ? Number(pathSplit[8]) : 1;
 
     if (dataType == DataType.Post) {
-      let getPostsForm = new GetPosts({
-        community_name: Some(communityName),
-        community_id: None,
+      let getPostsForm: GetPosts = {
+        community_name: communityName,
         page,
-        limit: Some(fetchLimit),
+        limit: fetchLimit,
         sort,
-        type_: Some(ListingType.All),
-        saved_only: Some(false),
+        type_: ListingType.All,
+        saved_only: false,
         auth: req.auth,
-      });
+      };
       promises.push(req.client.getPosts(getPostsForm));
       promises.push(Promise.resolve());
     } else {
-      let getCommentsForm = new GetComments({
-        community_name: Some(communityName),
-        community_id: None,
+      let getCommentsForm: GetComments = {
+        community_name: communityName,
         page,
-        limit: Some(fetchLimit),
-        max_depth: None,
-        sort: sort.map(postToCommentSortType),
-        type_: Some(ListingType.All),
-        saved_only: Some(false),
-        post_id: None,
-        parent_id: None,
+        limit: fetchLimit,
+        sort: postToCommentSortType(sort),
+        type_: ListingType.All,
+        saved_only: false,
         auth: req.auth,
-      });
+      };
       promises.push(Promise.resolve());
       promises.push(req.client.getComments(getCommentsForm));
     }
@@ -270,23 +254,19 @@ export class Community extends Component<any, State> {
   }
 
   get documentTitle(): string {
-    return this.state.communityRes.match({
-      some: res =>
-        `${res.community_view.community.title} - ${this.state.siteRes.site_view.site.name}`,
-      none: "",
-    });
+    let cRes = this.state.communityRes;
+    return cRes
+      ? `${cRes.community_view.community.title} - ${this.state.siteRes.site_view.site.name}`
+      : "";
   }
 
   render() {
     // For some reason, this returns an empty vec if it matches the site langs
-    let communityLangs = this.state.communityRes.map(r => {
-      let langs = r.discussion_languages;
-      if (langs.length == 0) {
-        return this.state.siteRes.all_languages.map(l => l.id);
-      } else {
-        return langs;
-      }
-    });
+    let res = this.state.communityRes;
+    let communityLangs =
+      res?.discussion_languages.length == 0
+        ? this.state.siteRes.all_languages.map(l => l.id)
+        : res?.discussion_languages;
 
     return (
       <div className="container-lg">
@@ -295,103 +275,86 @@ export class Community extends Component<any, State> {
             <Spinner large />
           </h5>
         ) : (
-          this.state.communityRes.match({
-            some: res => (
-              <>
-                <HtmlTags
-                  title={this.documentTitle}
-                  path={this.context.router.route.match.url}
-                  description={res.community_view.community.description}
-                  image={res.community_view.community.icon}
-                />
-
-                <div className="row">
-                  <div className="col-12 col-md-8">
-                    {this.communityInfo()}
-                    <div className="d-block d-md-none">
-                      <button
-                        className="btn btn-secondary d-inline-block mb-2 mr-3"
-                        onClick={linkEvent(this, this.handleShowSidebarMobile)}
-                      >
-                        {i18n.t("sidebar")}{" "}
-                        <Icon
-                          icon={
-                            this.state.showSidebarMobile
-                              ? `minus-square`
-                              : `plus-square`
+          res && (
+            <>
+              <HtmlTags
+                title={this.documentTitle}
+                path={this.context.router.route.match.url}
+                description={res.community_view.community.description}
+                image={res.community_view.community.icon}
+              />
+
+              <div className="row">
+                <div className="col-12 col-md-8">
+                  {this.communityInfo()}
+                  <div className="d-block d-md-none">
+                    <button
+                      className="btn btn-secondary d-inline-block mb-2 mr-3"
+                      onClick={linkEvent(this, this.handleShowSidebarMobile)}
+                    >
+                      {i18n.t("sidebar")}{" "}
+                      <Icon
+                        icon={
+                          this.state.showSidebarMobile
+                            ? `minus-square`
+                            : `plus-square`
+                        }
+                        classes="icon-inline"
+                      />
+                    </button>
+                    {this.state.showSidebarMobile && (
+                      <>
+                        <Sidebar
+                          community_view={res.community_view}
+                          moderators={res.moderators}
+                          admins={this.state.siteRes.admins}
+                          online={res.online}
+                          enableNsfw={enableNsfw(this.state.siteRes)}
+                          editable
+                          allLanguages={this.state.siteRes.all_languages}
+                          siteLanguages={
+                            this.state.siteRes.discussion_languages
                           }
-                          classes="icon-inline"
+                          communityLanguages={communityLangs}
                         />
-                      </button>
-                      {this.state.showSidebarMobile && (
-                        <>
-                          <Sidebar
-                            community_view={res.community_view}
-                            moderators={res.moderators}
-                            admins={this.state.siteRes.admins}
-                            online={res.online}
-                            enableNsfw={enableNsfw(this.state.siteRes)}
-                            editable
-                            allLanguages={this.state.siteRes.all_languages}
-                            siteLanguages={
-                              this.state.siteRes.discussion_languages
-                            }
-                            communityLanguages={communityLangs}
-                          />
-                          {!res.community_view.community.local &&
-                            res.site.match({
-                              some: site => (
-                                <SiteSidebar
-                                  site={site}
-                                  showLocal={showLocal(this.isoData)}
-                                  admins={None}
-                                  counts={None}
-                                  online={None}
-                                />
-                              ),
-                              none: <></>,
-                            })}
-                        </>
-                      )}
-                    </div>
-                    {this.selects()}
-                    {this.listings()}
-                    <Paginator
-                      page={this.state.page}
-                      onChange={this.handlePageChange}
-                    />
-                  </div>
-                  <div className="d-none d-md-block col-md-4">
-                    <Sidebar
-                      community_view={res.community_view}
-                      moderators={res.moderators}
-                      admins={this.state.siteRes.admins}
-                      online={res.online}
-                      enableNsfw={enableNsfw(this.state.siteRes)}
-                      editable
-                      allLanguages={this.state.siteRes.all_languages}
-                      siteLanguages={this.state.siteRes.discussion_languages}
-                      communityLanguages={communityLangs}
-                    />
-                    {!res.community_view.community.local &&
-                      res.site.match({
-                        some: site => (
+                        {!res.community_view.community.local && res.site && (
                           <SiteSidebar
-                            site={site}
+                            site={res.site}
                             showLocal={showLocal(this.isoData)}
-                            admins={None}
-                            counts={None}
-                            online={None}
                           />
-                        ),
-                        none: <></>,
-                      })}
+                        )}
+                      </>
+                    )}
                   </div>
+                  {this.selects()}
+                  {this.listings()}
+                  <Paginator
+                    page={this.state.page}
+                    onChange={this.handlePageChange}
+                  />
                 </div>
-              </>
-            ),
-            none: <></>,
-          })
+                <div className="d-none d-md-block col-md-4">
+                  <Sidebar
+                    community_view={res.community_view}
+                    moderators={res.moderators}
+                    admins={this.state.siteRes.admins}
+                    online={res.online}
+                    enableNsfw={enableNsfw(this.state.siteRes)}
+                    editable
+                    allLanguages={this.state.siteRes.all_languages}
+                    siteLanguages={this.state.siteRes.discussion_languages}
+                    communityLanguages={communityLangs}
+                  />
+                  {!res.community_view.community.local && res.site && (
+                    <SiteSidebar
+                      site={res.site}
+                      showLocal={showLocal(this.isoData)}
+                    />
+                  )}
+                </div>
+              </div>
+            </>
+          )
         )}
       </div>
     );
@@ -424,9 +387,8 @@ export class Community extends Component<any, State> {
         noIndent
         showContext
         enableDownvotes={enableDownvotes(this.state.siteRes)}
-        moderators={this.state.communityRes.map(r => r.moderators)}
-        admins={Some(this.state.siteRes.admins)}
-        maxCommentsShown={None}
+        moderators={this.state.communityRes?.moderators}
+        admins={this.state.siteRes.admins}
         allLanguages={this.state.siteRes.all_languages}
         siteLanguages={this.state.siteRes.discussion_languages}
       />
@@ -434,30 +396,33 @@ export class Community extends Component<any, State> {
   }
 
   communityInfo() {
-    return this.state.communityRes
-      .map(r => r.community_view.community)
-      .match({
-        some: community => (
-          <div className="mb-2">
-            <BannerIconHeader banner={community.banner} icon={community.icon} />
-            <h5 className="mb-0 overflow-wrap-anywhere">{community.title}</h5>
-            <CommunityLink
-              community={community}
-              realLink
-              useApubName
-              muted
-              hideAvatar
-            />
-          </div>
-        ),
-        none: <></>,
-      });
+    let community = this.state.communityRes?.community_view.community;
+    return (
+      community && (
+        <div className="mb-2">
+          <BannerIconHeader banner={community.banner} icon={community.icon} />
+          <h5 className="mb-0 overflow-wrap-anywhere">{community.title}</h5>
+          <CommunityLink
+            community={community}
+            realLink
+            useApubName
+            muted
+            hideAvatar
+          />
+        </div>
+      )
+    );
   }
 
   selects() {
-    let communityRss = this.state.communityRes.map(r =>
-      communityRSSUrl(r.community_view.community.actor_id, this.state.sort)
-    );
+    // let communityRss = this.state.communityRes.map(r =>
+    //   communityRSSUrl(r.community_view.community.actor_id, this.state.sort)
+    // );
+    let res = this.state.communityRes;
+    let communityRss = res
+      ? communityRSSUrl(res.community_view.community.actor_id, this.state.sort)
+      : undefined;
+
     return (
       <div className="mb-3">
         <span className="mr-3">
@@ -469,17 +434,18 @@ export class Community extends Component<any, State> {
         <span className="mr-2">
           <SortSelect sort={this.state.sort} onChange={this.handleSortChange} />
         </span>
-        {communityRss.match({
-          some: rss => (
-            <>
-              <a href={rss} title="RSS" rel={relTags}>
-                <Icon icon="rss" classes="text-muted small" />
-              </a>
-              <link rel="alternate" type="application/atom+xml" href={rss} />
-            </>
-          ),
-          none: <></>,
-        })}
+        {communityRss && (
+          <>
+            <a href={communityRss} title="RSS" rel={relTags}>
+              <Icon icon="rss" classes="text-muted small" />
+            </a>
+            <link
+              rel="alternate"
+              type="application/atom+xml"
+              href={communityRss}
+            />
+          </>
+        )}
       </div>
     );
   }
@@ -517,31 +483,26 @@ export class Community extends Component<any, State> {
 
   fetchData() {
     if (this.state.dataType == DataType.Post) {
-      let form = new GetPosts({
-        page: Some(this.state.page),
-        limit: Some(fetchLimit),
-        sort: Some(this.state.sort),
-        type_: Some(ListingType.All),
-        community_name: Some(this.state.communityName),
-        community_id: None,
-        saved_only: Some(false),
-        auth: auth(false).ok(),
-      });
+      let form: GetPosts = {
+        page: this.state.page,
+        limit: fetchLimit,
+        sort: this.state.sort,
+        type_: ListingType.All,
+        community_name: this.state.communityName,
+        saved_only: false,
+        auth: myAuth(false),
+      };
       WebSocketService.Instance.send(wsClient.getPosts(form));
     } else {
-      let form = new GetComments({
-        page: Some(this.state.page),
-        limit: Some(fetchLimit),
-        max_depth: None,
-        sort: Some(postToCommentSortType(this.state.sort)),
-        type_: Some(ListingType.All),
-        community_name: Some(this.state.communityName),
-        community_id: None,
-        saved_only: Some(false),
-        post_id: None,
-        parent_id: None,
-        auth: auth(false).ok(),
-      });
+      let form: GetComments = {
+        page: this.state.page,
+        limit: fetchLimit,
+        sort: postToCommentSortType(this.state.sort),
+        type_: ListingType.All,
+        community_name: this.state.communityName,
+        saved_only: false,
+        auth: myAuth(false),
+      };
       WebSocketService.Instance.send(wsClient.getComments(form));
     }
   }
@@ -549,25 +510,23 @@ export class Community extends Component<any, State> {
   parseMessage(msg: any) {
     let op = wsUserOp(msg);
     console.log(msg);
+    let res = this.state.communityRes;
     if (msg.error) {
       toast(i18n.t(msg.error), "danger");
       this.context.router.history.push("/");
       return;
     } else if (msg.reconnect) {
-      this.state.communityRes.match({
-        some: res => {
-          WebSocketService.Instance.send(
-            wsClient.communityJoin({
-              community_id: res.community_view.community.id,
-            })
-          );
-        },
-        none: void 0,
-      });
+      if (res) {
+        WebSocketService.Instance.send(
+          wsClient.communityJoin({
+            community_id: res.community_view.community.id,
+          })
+        );
+      }
       this.fetchData();
     } else if (op == UserOperation.GetCommunity) {
-      let data = wsJsonToRes<GetCommunityResponse>(msg, GetCommunityResponse);
-      this.setState({ communityRes: Some(data), communityLoading: false });
+      let data = wsJsonToRes<GetCommunityResponse>(msg);
+      this.setState({ communityRes: data, communityLoading: false });
       // TODO why is there no auth in this form?
       WebSocketService.Instance.send(
         wsClient.communityJoin({
@@ -579,28 +538,22 @@ export class Community extends Component<any, State> {
       op == UserOperation.DeleteCommunity ||
       op == UserOperation.RemoveCommunity
     ) {
-      let data = wsJsonToRes<CommunityResponse>(msg, CommunityResponse);
-      this.state.communityRes.match({
-        some: res => {
-          res.community_view = data.community_view;
-          res.discussion_languages = data.discussion_languages;
-        },
-        none: void 0,
-      });
+      let data = wsJsonToRes<CommunityResponse>(msg);
+      if (res) {
+        res.community_view = data.community_view;
+        res.discussion_languages = data.discussion_languages;
+      }
       this.setState(this.state);
     } else if (op == UserOperation.FollowCommunity) {
-      let data = wsJsonToRes<CommunityResponse>(msg, CommunityResponse);
-      this.state.communityRes.match({
-        some: res => {
-          res.community_view.subscribed = data.community_view.subscribed;
-          res.community_view.counts.subscribers =
-            data.community_view.counts.subscribers;
-        },
-        none: void 0,
-      });
+      let data = wsJsonToRes<CommunityResponse>(msg);
+      if (res) {
+        res.community_view.subscribed = data.community_view.subscribed;
+        res.community_view.counts.subscribers =
+          data.community_view.counts.subscribers;
+      }
       this.setState(this.state);
     } else if (op == UserOperation.GetPosts) {
-      let data = wsJsonToRes<GetPostsResponse>(msg, GetPostsResponse);
+      let data = wsJsonToRes<GetPostsResponse>(msg);
       this.setState({ posts: data.posts, postsLoading: false });
       restoreScrollPosition(this.context);
       setupTippy();
@@ -612,15 +565,15 @@ export class Community extends Component<any, State> {
       op == UserOperation.FeaturePost ||
       op == UserOperation.SavePost
     ) {
-      let data = wsJsonToRes<PostResponse>(msg, PostResponse);
+      let data = wsJsonToRes<PostResponse>(msg);
       editPostFindRes(data.post_view, this.state.posts);
       this.setState(this.state);
     } else if (op == UserOperation.CreatePost) {
-      let data = wsJsonToRes<PostResponse>(msg, PostResponse);
+      let data = wsJsonToRes<PostResponse>(msg);
 
-      let showPostNotifs = UserService.Instance.myUserInfo
-        .map(m => m.local_user_view.local_user.show_new_post_notifs)
-        .unwrapOr(false);
+      let showPostNotifs =
+        UserService.Instance.myUserInfo?.local_user_view.local_user
+          .show_new_post_notifs;
 
       // Only push these if you're on the first page, you pass the nsfw check, and it isn't blocked
       //
@@ -636,24 +589,17 @@ export class Community extends Component<any, State> {
         this.setState(this.state);
       }
     } else if (op == UserOperation.CreatePostLike) {
-      let data = wsJsonToRes<PostResponse>(msg, PostResponse);
+      let data = wsJsonToRes<PostResponse>(msg);
       createPostLikeFindRes(data.post_view, this.state.posts);
       this.setState(this.state);
     } else if (op == UserOperation.AddModToCommunity) {
-      let data = wsJsonToRes<AddModToCommunityResponse>(
-        msg,
-        AddModToCommunityResponse
-      );
-      this.state.communityRes.match({
-        some: res => (res.moderators = data.moderators),
-        none: void 0,
-      });
+      let data = wsJsonToRes<AddModToCommunityResponse>(msg);
+      if (res) {
+        res.moderators = data.moderators;
+      }
       this.setState(this.state);
     } else if (op == UserOperation.BanFromCommunity) {
-      let data = wsJsonToRes<BanFromCommunityResponse>(
-        msg,
-        BanFromCommunityResponse
-      );
+      let data = wsJsonToRes<BanFromCommunityResponse>(msg);
 
       // TODO this might be incorrect
       this.state.posts
@@ -662,18 +608,18 @@ export class Community extends Component<any, State> {
 
       this.setState(this.state);
     } else if (op == UserOperation.GetComments) {
-      let data = wsJsonToRes<GetCommentsResponse>(msg, GetCommentsResponse);
+      let data = wsJsonToRes<GetCommentsResponse>(msg);
       this.setState({ comments: data.comments, commentsLoading: false });
     } else if (
       op == UserOperation.EditComment ||
       op == UserOperation.DeleteComment ||
       op == UserOperation.RemoveComment
     ) {
-      let data = wsJsonToRes<CommentResponse>(msg, CommentResponse);
+      let data = wsJsonToRes<CommentResponse>(msg);
       editCommentRes(data.comment_view, this.state.comments);
       this.setState(this.state);
     } else if (op == UserOperation.CreateComment) {
-      let data = wsJsonToRes<CommentResponse>(msg, CommentResponse);
+      let data = wsJsonToRes<CommentResponse>(msg);
 
       // Necessary since it might be a user reply
       if (data.form_id) {
@@ -681,41 +627,37 @@ export class Community extends Component<any, State> {
         this.setState(this.state);
       }
     } else if (op == UserOperation.SaveComment) {
-      let data = wsJsonToRes<CommentResponse>(msg, CommentResponse);
+      let data = wsJsonToRes<CommentResponse>(msg);
       saveCommentRes(data.comment_view, this.state.comments);
       this.setState(this.state);
     } else if (op == UserOperation.CreateCommentLike) {
-      let data = wsJsonToRes<CommentResponse>(msg, CommentResponse);
+      let data = wsJsonToRes<CommentResponse>(msg);
       createCommentLikeRes(data.comment_view, this.state.comments);
       this.setState(this.state);
     } else if (op == UserOperation.BlockPerson) {
-      let data = wsJsonToRes<BlockPersonResponse>(msg, BlockPersonResponse);
+      let data = wsJsonToRes<BlockPersonResponse>(msg);
       updatePersonBlock(data);
     } else if (op == UserOperation.CreatePostReport) {
-      let data = wsJsonToRes<PostReportResponse>(msg, PostReportResponse);
+      let data = wsJsonToRes<PostReportResponse>(msg);
       if (data) {
         toast(i18n.t("report_created"));
       }
     } else if (op == UserOperation.CreateCommentReport) {
-      let data = wsJsonToRes<CommentReportResponse>(msg, CommentReportResponse);
+      let data = wsJsonToRes<CommentReportResponse>(msg);
       if (data) {
         toast(i18n.t("report_created"));
       }
     } else if (op == UserOperation.PurgeCommunity) {
-      let data = wsJsonToRes<PurgeItemResponse>(msg, PurgeItemResponse);
+      let data = wsJsonToRes<PurgeItemResponse>(msg);
       if (data.success) {
         toast(i18n.t("purge_success"));
         this.context.router.history.push(`/`);
       }
     } else if (op == UserOperation.BlockCommunity) {
-      let data = wsJsonToRes<BlockCommunityResponse>(
-        msg,
-        BlockCommunityResponse
-      );
-      this.state.communityRes.match({
-        some: res => (res.community_view.blocked = data.blocked),
-        none: void 0,
-      });
+      let data = wsJsonToRes<BlockCommunityResponse>(msg);
+      if (res) {
+        res.community_view.blocked = data.blocked;
+      }
       updateCommunityBlock(data);
       this.setState(this.state);
     }
index 5981a9f8aa8c38e3e31e63c0e75a41934aed41c2..da8ef09d81d742139070d7cbddb96b3a95af8abd 100644 (file)
@@ -1,4 +1,3 @@
-import { None, Some } from "@sniptt/monads";
 import { Component } from "inferno";
 import { CommunityView, GetSiteResponse } from "lemmy-js-client";
 import { Subscription } from "rxjs";
@@ -22,20 +21,19 @@ interface CreateCommunityState {
 
 export class CreateCommunity extends Component<any, CreateCommunityState> {
   private isoData = setIsoData(this.context);
-  private subscription: Subscription;
-  private emptyState: CreateCommunityState = {
+  private subscription?: Subscription;
+  state: CreateCommunityState = {
     siteRes: this.isoData.site_res,
     loading: false,
   };
   constructor(props: any, context: any) {
     super(props, context);
     this.handleCommunityCreate = this.handleCommunityCreate.bind(this);
-    this.state = this.emptyState;
 
     this.parseMessage = this.parseMessage.bind(this);
     this.subscription = wsSubscribe(this.parseMessage);
 
-    if (UserService.Instance.myUserInfo.isNone() && isBrowser()) {
+    if (!UserService.Instance.myUserInfo && isBrowser()) {
       toast(i18n.t("not_logged_in"), "danger");
       this.context.router.history.push(`/login`);
     }
@@ -43,7 +41,7 @@ export class CreateCommunity extends Component<any, CreateCommunityState> {
 
   componentWillUnmount() {
     if (isBrowser()) {
-      this.subscription.unsubscribe();
+      this.subscription?.unsubscribe();
     }
   }
 
@@ -59,8 +57,6 @@ export class CreateCommunity extends Component<any, CreateCommunityState> {
         <HtmlTags
           title={this.documentTitle}
           path={this.context.router.route.match.url}
-          description={None}
-          image={None}
         />
         {this.state.loading ? (
           <h5>
@@ -71,14 +67,11 @@ export class CreateCommunity extends Component<any, CreateCommunityState> {
             <div className="col-12 col-lg-6 offset-lg-3 mb-4">
               <h5>{i18n.t("create_community")}</h5>
               <CommunityForm
-                community_view={None}
                 onCreate={this.handleCommunityCreate}
                 enableNsfw={enableNsfw(this.state.siteRes)}
                 allLanguages={this.state.siteRes.all_languages}
                 siteLanguages={this.state.siteRes.discussion_languages}
-                communityLanguages={Some(
-                  this.state.siteRes.discussion_languages
-                )}
+                communityLanguages={this.state.siteRes.discussion_languages}
               />
             </div>
           </div>
index 845ef9e444975183437761bc5cd25dc0ea532273..144075b5196bb46b832ff2508165b949988c4f89 100644 (file)
@@ -1,4 +1,3 @@
-import { None, Option, Some } from "@sniptt/monads";
 import { Component, linkEvent } from "inferno";
 import { Link } from "inferno-router";
 import {
@@ -13,7 +12,6 @@ import {
   PurgeCommunity,
   RemoveCommunity,
   SubscribedType,
-  toUndefined,
 } from "lemmy-js-client";
 import { i18n } from "../../i18next";
 import { UserService, WebSocketService } from "../../services";
@@ -21,9 +19,9 @@ import {
   amAdmin,
   amMod,
   amTopMod,
-  auth,
   getUnixTime,
   mdToHtml,
+  myAuth,
   numToSI,
   wsClient,
 } from "../../utils";
@@ -39,7 +37,7 @@ interface SidebarProps {
   admins: PersonViewSafe[];
   allLanguages: Language[];
   siteLanguages: number[];
-  communityLanguages: Option<number[]>;
+  communityLanguages?: number[];
   online: number;
   enableNsfw?: boolean;
   showIcon?: boolean;
@@ -47,31 +45,27 @@ interface SidebarProps {
 }
 
 interface SidebarState {
-  removeReason: Option<string>;
-  removeExpires: Option<string>;
+  removeReason?: string;
+  removeExpires?: string;
   showEdit: boolean;
   showRemoveDialog: boolean;
   showPurgeDialog: boolean;
-  purgeReason: Option<string>;
+  purgeReason?: string;
   purgeLoading: boolean;
   showConfirmLeaveModTeam: boolean;
 }
 
 export class Sidebar extends Component<SidebarProps, SidebarState> {
-  private emptyState: SidebarState = {
+  state: SidebarState = {
     showEdit: false,
     showRemoveDialog: false,
-    removeReason: None,
-    removeExpires: None,
     showPurgeDialog: false,
-    purgeReason: None,
     purgeLoading: false,
     showConfirmLeaveModTeam: false,
   };
 
   constructor(props: any, context: any) {
     super(props, context);
-    this.state = this.emptyState;
     this.handleEditCommunity = this.handleEditCommunity.bind(this);
     this.handleEditCancel = this.handleEditCancel.bind(this);
   }
@@ -83,7 +77,7 @@ export class Sidebar extends Component<SidebarProps, SidebarState> {
           this.sidebar()
         ) : (
           <CommunityForm
-            community_view={Some(this.props.community_view)}
+            community_view={this.props.community_view}
             allLanguages={this.props.allLanguages}
             siteLanguages={this.props.siteLanguages}
             communityLanguages={this.props.communityLanguages}
@@ -336,13 +330,12 @@ export class Sidebar extends Component<SidebarProps, SidebarState> {
   }
 
   description() {
-    let description = this.props.community_view.community.description;
-    return description.match({
-      some: desc => (
+    let desc = this.props.community_view.community.description;
+    return (
+      desc && (
         <div className="md-div" dangerouslySetInnerHTML={mdToHtml(desc)} />
-      ),
-      none: <></>,
-    });
+      )
+    );
   }
 
   adminButtons() {
@@ -350,7 +343,7 @@ export class Sidebar extends Component<SidebarProps, SidebarState> {
     return (
       <>
         <ul className="list-inline mb-1 text-muted font-weight-bold">
-          {amMod(Some(this.props.moderators)) && (
+          {amMod(this.props.moderators) && (
             <>
               <li className="list-inline-item-action">
                 <button
@@ -362,7 +355,7 @@ export class Sidebar extends Component<SidebarProps, SidebarState> {
                   <Icon icon="edit" classes="icon-inline" />
                 </button>
               </li>
-              {!amTopMod(Some(this.props.moderators)) &&
+              {!amTopMod(this.props.moderators) &&
                 (!this.state.showConfirmLeaveModTeam ? (
                   <li className="list-inline-item-action">
                     <button
@@ -401,7 +394,7 @@ export class Sidebar extends Component<SidebarProps, SidebarState> {
                     </li>
                   </>
                 ))}
-              {amTopMod(Some(this.props.moderators)) && (
+              {amTopMod(this.props.moderators) && (
                 <li className="list-inline-item-action">
                   <button
                     className="btn btn-link text-muted d-inline-block"
@@ -466,7 +459,7 @@ export class Sidebar extends Component<SidebarProps, SidebarState> {
                 id="remove-reason"
                 className="form-control mr-2"
                 placeholder={i18n.t("optional")}
-                value={toUndefined(this.state.removeReason)}
+                value={this.state.removeReason}
                 onInput={linkEvent(this, this.handleModRemoveReasonChange)}
               />
             </div>
@@ -496,7 +489,7 @@ export class Sidebar extends Component<SidebarProps, SidebarState> {
                 id="purge-reason"
                 className="form-control mr-2"
                 placeholder={i18n.t("reason")}
-                value={toUndefined(this.state.purgeReason)}
+                value={this.state.purgeReason}
                 onInput={linkEvent(this, this.handlePurgeReasonChange)}
               />
             </div>
@@ -533,12 +526,15 @@ export class Sidebar extends Component<SidebarProps, SidebarState> {
 
   handleDeleteClick(i: Sidebar, event: any) {
     event.preventDefault();
-    let deleteForm = new DeleteCommunity({
-      community_id: i.props.community_view.community.id,
-      deleted: !i.props.community_view.community.deleted,
-      auth: auth().unwrap(),
-    });
-    WebSocketService.Instance.send(wsClient.deleteCommunity(deleteForm));
+    let auth = myAuth();
+    if (auth) {
+      let deleteForm: DeleteCommunity = {
+        community_id: i.props.community_view.community.id,
+        deleted: !i.props.community_view.community.deleted,
+        auth,
+      };
+      WebSocketService.Instance.send(wsClient.deleteCommunity(deleteForm));
+    }
   }
 
   handleShowConfirmLeaveModTeamClick(i: Sidebar) {
@@ -546,19 +542,18 @@ export class Sidebar extends Component<SidebarProps, SidebarState> {
   }
 
   handleLeaveModTeamClick(i: Sidebar) {
-    UserService.Instance.myUserInfo.match({
-      some: mui => {
-        let form = new AddModToCommunity({
-          person_id: mui.local_user_view.person.id,
-          community_id: i.props.community_view.community.id,
-          added: false,
-          auth: auth().unwrap(),
-        });
-        WebSocketService.Instance.send(wsClient.addModToCommunity(form));
-        i.setState({ showConfirmLeaveModTeam: false });
-      },
-      none: void 0,
-    });
+    let mui = UserService.Instance.myUserInfo;
+    let auth = myAuth();
+    if (auth && mui) {
+      let form: AddModToCommunity = {
+        person_id: mui.local_user_view.person.id,
+        community_id: i.props.community_view.community.id,
+        added: false,
+        auth,
+      };
+      WebSocketService.Instance.send(wsClient.addModToCommunity(form));
+      i.setState({ showConfirmLeaveModTeam: false });
+    }
   }
 
   handleCancelLeaveModTeamClick(i: Sidebar) {
@@ -568,46 +563,50 @@ export class Sidebar extends Component<SidebarProps, SidebarState> {
   handleUnsubscribe(i: Sidebar, event: any) {
     event.preventDefault();
     let community_id = i.props.community_view.community.id;
-    let form = new FollowCommunity({
-      community_id,
-      follow: false,
-      auth: auth().unwrap(),
-    });
-    WebSocketService.Instance.send(wsClient.followCommunity(form));
+    let auth = myAuth();
+    if (auth) {
+      let form: FollowCommunity = {
+        community_id,
+        follow: false,
+        auth,
+      };
+      WebSocketService.Instance.send(wsClient.followCommunity(form));
+    }
 
     // Update myUserInfo
-    UserService.Instance.myUserInfo.match({
-      some: mui =>
-        (mui.follows = mui.follows.filter(i => i.community.id != community_id)),
-      none: void 0,
-    });
+    let mui = UserService.Instance.myUserInfo;
+    if (mui) {
+      mui.follows = mui.follows.filter(i => i.community.id != community_id);
+    }
   }
 
   handleSubscribe(i: Sidebar, event: any) {
     event.preventDefault();
     let community_id = i.props.community_view.community.id;
-    let form = new FollowCommunity({
-      community_id,
-      follow: true,
-      auth: auth().unwrap(),
-    });
-    WebSocketService.Instance.send(wsClient.followCommunity(form));
+    let auth = myAuth();
+    if (auth) {
+      let form: FollowCommunity = {
+        community_id,
+        follow: true,
+        auth,
+      };
+      WebSocketService.Instance.send(wsClient.followCommunity(form));
+    }
 
     // Update myUserInfo
-    UserService.Instance.myUserInfo.match({
-      some: mui =>
-        mui.follows.push({
-          community: i.props.community_view.community,
-          follower: mui.local_user_view.person,
-        }),
-      none: void 0,
-    });
+    let mui = UserService.Instance.myUserInfo;
+    if (mui) {
+      mui.follows.push({
+        community: i.props.community_view.community,
+        follower: mui.local_user_view.person,
+      });
+    }
   }
 
   get canPost(): boolean {
     return (
       !this.props.community_view.community.posting_restricted_to_mods ||
-      amMod(Some(this.props.moderators)) ||
+      amMod(this.props.moderators) ||
       amAdmin()
     );
   }
@@ -617,25 +616,28 @@ export class Sidebar extends Component<SidebarProps, SidebarState> {
   }
 
   handleModRemoveReasonChange(i: Sidebar, event: any) {
-    i.setState({ removeReason: Some(event.target.value) });
+    i.setState({ removeReason: event.target.value });
   }
 
   handleModRemoveExpiresChange(i: Sidebar, event: any) {
-    i.setState({ removeExpires: Some(event.target.value) });
+    i.setState({ removeExpires: event.target.value });
   }
 
   handleModRemoveSubmit(i: Sidebar, event: any) {
     event.preventDefault();
-    let removeForm = new RemoveCommunity({
-      community_id: i.props.community_view.community.id,
-      removed: !i.props.community_view.community.removed,
-      reason: i.state.removeReason,
-      expires: i.state.removeExpires.map(getUnixTime),
-      auth: auth().unwrap(),
-    });
-    WebSocketService.Instance.send(wsClient.removeCommunity(removeForm));
-
-    i.setState({ showRemoveDialog: false });
+    let auth = myAuth();
+    if (auth) {
+      let removeForm: RemoveCommunity = {
+        community_id: i.props.community_view.community.id,
+        removed: !i.props.community_view.community.removed,
+        reason: i.state.removeReason,
+        expires: getUnixTime(i.state.removeExpires),
+        auth,
+      };
+      WebSocketService.Instance.send(wsClient.removeCommunity(removeForm));
+
+      i.setState({ showRemoveDialog: false });
+    }
   }
 
   handlePurgeCommunityShow(i: Sidebar) {
@@ -643,39 +645,51 @@ export class Sidebar extends Component<SidebarProps, SidebarState> {
   }
 
   handlePurgeReasonChange(i: Sidebar, event: any) {
-    i.setState({ purgeReason: Some(event.target.value) });
+    i.setState({ purgeReason: event.target.value });
   }
 
   handlePurgeSubmit(i: Sidebar, event: any) {
     event.preventDefault();
 
-    let form = new PurgeCommunity({
-      community_id: i.props.community_view.community.id,
-      reason: i.state.purgeReason,
-      auth: auth().unwrap(),
-    });
-    WebSocketService.Instance.send(wsClient.purgeCommunity(form));
-
-    i.setState({ purgeLoading: true });
+    let auth = myAuth();
+    if (auth) {
+      let form: PurgeCommunity = {
+        community_id: i.props.community_view.community.id,
+        reason: i.state.purgeReason,
+        auth,
+      };
+      WebSocketService.Instance.send(wsClient.purgeCommunity(form));
+      i.setState({ purgeLoading: true });
+    }
   }
 
   handleBlock(i: Sidebar, event: any) {
     event.preventDefault();
-    let blockCommunityForm = new BlockCommunity({
-      community_id: i.props.community_view.community.id,
-      block: true,
-      auth: auth().unwrap(),
-    });
-    WebSocketService.Instance.send(wsClient.blockCommunity(blockCommunityForm));
+    let auth = myAuth();
+    if (auth) {
+      let blockCommunityForm: BlockCommunity = {
+        community_id: i.props.community_view.community.id,
+        block: true,
+        auth,
+      };
+      WebSocketService.Instance.send(
+        wsClient.blockCommunity(blockCommunityForm)
+      );
+    }
   }
 
   handleUnblock(i: Sidebar, event: any) {
     event.preventDefault();
-    let blockCommunityForm = new BlockCommunity({
-      community_id: i.props.community_view.community.id,
-      block: false,
-      auth: auth().unwrap(),
-    });
-    WebSocketService.Instance.send(wsClient.blockCommunity(blockCommunityForm));
+    let auth = myAuth();
+    if (auth) {
+      let blockCommunityForm: BlockCommunity = {
+        community_id: i.props.community_view.community.id,
+        block: false,
+        auth,
+      };
+      WebSocketService.Instance.send(
+        wsClient.blockCommunity(blockCommunityForm)
+      );
+    }
   }
 }
index b077e8e720d276b85e23329785c6fcf3e381ef20..b590988ab6b19779d1ea2d8368920ccf307a93c1 100644 (file)
@@ -1,4 +1,3 @@
-import { None } from "@sniptt/monads";
 import autosize from "autosize";
 import { Component, linkEvent } from "inferno";
 import {
@@ -16,9 +15,9 @@ import { i18n } from "../../i18next";
 import { InitialFetchRequest } from "../../interfaces";
 import { WebSocketService } from "../../services";
 import {
-  auth,
   capitalizeFirstLetter,
   isBrowser,
+  myAuth,
   randomStr,
   setIsoData,
   showLocal,
@@ -40,20 +39,18 @@ interface AdminSettingsState {
 
 export class AdminSettings extends Component<any, AdminSettingsState> {
   private siteConfigTextAreaId = `site-config-${randomStr()}`;
-  private isoData = setIsoData(this.context, BannedPersonsResponse);
-  private subscription: Subscription;
-  private emptyState: AdminSettingsState = {
+  private isoData = setIsoData(this.context);
+  private subscription?: Subscription;
+  state: AdminSettingsState = {
     siteRes: this.isoData.site_res,
     banned: [],
     loading: true,
-    leaveAdminTeamLoading: null,
+    leaveAdminTeamLoading: false,
   };
 
   constructor(props: any, context: any) {
     super(props, context);
 
-    this.state = this.emptyState;
-
     this.parseMessage = this.parseMessage.bind(this);
     this.subscription = wsSubscribe(this.parseMessage);
 
@@ -65,19 +62,25 @@ export class AdminSettings extends Component<any, AdminSettingsState> {
         loading: false,
       };
     } else {
-      WebSocketService.Instance.send(
-        wsClient.getBannedPersons({
-          auth: auth().unwrap(),
-        })
-      );
+      let cAuth = myAuth();
+      if (cAuth) {
+        WebSocketService.Instance.send(
+          wsClient.getBannedPersons({
+            auth: cAuth,
+          })
+        );
+      }
     }
   }
 
   static fetchInitialData(req: InitialFetchRequest): Promise<any>[] {
     let promises: Promise<any>[] = [];
 
-    let bannedPersonsForm = new GetBannedPersons({ auth: req.auth.unwrap() });
-    promises.push(req.client.getBannedPersons(bannedPersonsForm));
+    let auth = req.auth;
+    if (auth) {
+      let bannedPersonsForm: GetBannedPersons = { auth };
+      promises.push(req.client.getBannedPersons(bannedPersonsForm));
+    }
 
     return promises;
   }
@@ -91,7 +94,7 @@ export class AdminSettings extends Component<any, AdminSettingsState> {
 
   componentWillUnmount() {
     if (isBrowser()) {
-      this.subscription.unsubscribe();
+      this.subscription?.unsubscribe();
     }
   }
 
@@ -114,8 +117,6 @@ export class AdminSettings extends Component<any, AdminSettingsState> {
               <HtmlTags
                 title={this.documentTitle}
                 path={this.context.router.route.match.url}
-                description={None}
-                image={None}
               />
               <SiteForm
                 siteRes={this.state.siteRes}
@@ -179,10 +180,11 @@ export class AdminSettings extends Component<any, AdminSettingsState> {
   }
 
   handleLeaveAdminTeam(i: AdminSettings) {
-    i.setState({ leaveAdminTeamLoading: true });
-    WebSocketService.Instance.send(
-      wsClient.leaveAdmin({ auth: auth().unwrap() })
-    );
+    let auth = myAuth();
+    if (auth) {
+      i.setState({ leaveAdminTeamLoading: true });
+      WebSocketService.Instance.send(wsClient.leaveAdmin({ auth }));
+    }
   }
 
   parseMessage(msg: any) {
@@ -194,14 +196,14 @@ export class AdminSettings extends Component<any, AdminSettingsState> {
       this.setState({ loading: false });
       return;
     } else if (op == UserOperation.EditSite) {
-      let data = wsJsonToRes<SiteResponse>(msg, SiteResponse);
+      let data = wsJsonToRes<SiteResponse>(msg);
       this.setState(s => ((s.siteRes.site_view = data.site_view), s));
       toast(i18n.t("site_saved"));
     } else if (op == UserOperation.GetBannedPersons) {
-      let data = wsJsonToRes<BannedPersonsResponse>(msg, BannedPersonsResponse);
+      let data = wsJsonToRes<BannedPersonsResponse>(msg);
       this.setState({ banned: data.banned, loading: false });
     } else if (op == UserOperation.LeaveAdmin) {
-      let data = wsJsonToRes<GetSiteResponse>(msg, GetSiteResponse);
+      let data = wsJsonToRes<GetSiteResponse>(msg);
       this.setState(s => ((s.siteRes.site_view = data.site_view), s));
       this.setState({ leaveAdminTeamLoading: false });
 
index 2b51e8ed49744e7a9c5befe874b15f518c98c1c6..b0187c9a5d3085328641e9096d4a7dd8084db2ed 100644 (file)
@@ -1,4 +1,3 @@
-import { None, Option, Some } from "@sniptt/monads";
 import { Component, linkEvent } from "inferno";
 import { T } from "inferno-i18next-dess";
 import { Link } from "inferno-router";
@@ -37,7 +36,6 @@ import {
 } from "../../interfaces";
 import { UserService, WebSocketService } from "../../services";
 import {
-  auth,
   canCreateCommunity,
   commentsToFlatNodes,
   createCommentLikeRes,
@@ -55,6 +53,7 @@ import {
   isBrowser,
   isPostBlocked,
   mdToHtml,
+  myAuth,
   notifyPost,
   nsfwCheck,
   postToCommentSortType,
@@ -96,7 +95,7 @@ interface HomeState {
   showSidebarMobile: boolean;
   subscribedCollapsed: boolean;
   loading: boolean;
-  tagline: Option<string>;
+  tagline?: string;
 }
 
 interface HomeProps {
@@ -114,14 +113,9 @@ interface UrlParams {
 }
 
 export class Home extends Component<any, HomeState> {
-  private isoData = setIsoData(
-    this.context,
-    GetPostsResponse,
-    GetCommentsResponse,
-    ListCommunitiesResponse
-  );
-  private subscription: Subscription;
-  private emptyState: HomeState = {
+  private isoData = setIsoData(this.context);
+  private subscription?: Subscription;
+  state: HomeState = {
     trendingCommunities: [],
     siteRes: this.isoData.site_res,
     showSubscribedMobile: false,
@@ -140,13 +134,11 @@ export class Home extends Component<any, HomeState> {
     dataType: getDataTypeFromProps(this.props),
     sort: getSortTypeFromProps(this.props),
     page: getPageFromProps(this.props),
-    tagline: None,
   };
 
   constructor(props: any, context: any) {
     super(props, context);
 
-    this.state = this.emptyState;
     this.handleSortChange = this.handleSortChange.bind(this);
     this.handleListingTypeChange = this.handleListingTypeChange.bind(this);
     this.handleDataTypeChange = this.handleDataTypeChange.bind(this);
@@ -157,16 +149,20 @@ export class Home extends Component<any, HomeState> {
 
     // Only fetch the data if coming from another route
     if (this.isoData.path == this.context.router.route.match.url) {
-      let postsRes = Some(this.isoData.routeData[0] as GetPostsResponse);
-      let commentsRes = Some(this.isoData.routeData[1] as GetCommentsResponse);
-      let trendingRes = this.isoData.routeData[2] as ListCommunitiesResponse;
-
-      if (postsRes.isSome()) {
-        this.state = { ...this.state, posts: postsRes.unwrap().posts };
+      let postsRes = this.isoData.routeData[0] as GetPostsResponse | undefined;
+      let commentsRes = this.isoData.routeData[1] as
+        | GetCommentsResponse
+        | undefined;
+      let trendingRes = this.isoData.routeData[2] as
+        | ListCommunitiesResponse
+        | undefined;
+
+      if (postsRes) {
+        this.state = { ...this.state, posts: postsRes.posts };
       }
 
-      if (commentsRes.isSome()) {
-        this.state = { ...this.state, comments: commentsRes.unwrap().comments };
+      if (commentsRes) {
+        this.state = { ...this.state, comments: commentsRes.comments };
       }
 
       if (isBrowser()) {
@@ -177,9 +173,9 @@ export class Home extends Component<any, HomeState> {
       const taglines = this.state.siteRes.taglines;
       this.state = {
         ...this.state,
-        trendingCommunities: trendingRes.communities,
+        trendingCommunities: trendingRes?.communities ?? [],
         loading: false,
-        tagline: taglines.map(tls => getRandomFromList(tls).content),
+        tagline: getRandomFromList(taglines)?.content,
       };
     } else {
       this.fetchTrendingCommunities();
@@ -188,13 +184,12 @@ export class Home extends Component<any, HomeState> {
   }
 
   fetchTrendingCommunities() {
-    let listCommunitiesForm = new ListCommunities({
-      type_: Some(ListingType.Local),
-      sort: Some(SortType.Hot),
-      limit: Some(trendingFetchLimit),
-      page: None,
-      auth: auth(false).ok(),
-    });
+    let listCommunitiesForm: ListCommunities = {
+      type_: ListingType.Local,
+      sort: SortType.Hot,
+      limit: trendingFetchLimit,
+      auth: myAuth(false),
+    };
     WebSocketService.Instance.send(
       wsClient.listCommunities(listCommunitiesForm)
     );
@@ -210,7 +205,7 @@ export class Home extends Component<any, HomeState> {
 
   componentWillUnmount() {
     saveScrollPosition(this.context);
-    this.subscription.unsubscribe();
+    this.subscription?.unsubscribe();
   }
 
   static getDerivedStateFromProps(
@@ -230,74 +225,60 @@ export class Home extends Component<any, HomeState> {
     let dataType: DataType = pathSplit[3]
       ? DataType[pathSplit[3]]
       : DataType.Post;
+    let mui = UserService.Instance.myUserInfo;
+    let auth = req.auth;
 
     // TODO figure out auth default_listingType, default_sort_type
-    let type_: Option<ListingType> = Some(
-      pathSplit[5]
-        ? ListingType[pathSplit[5]]
-        : UserService.Instance.myUserInfo.match({
-            some: mui =>
-              Object.values(ListingType)[
-                mui.local_user_view.local_user.default_listing_type
-              ],
-            none: ListingType.Local,
-          })
-    );
-    let sort: Option<SortType> = Some(
-      pathSplit[7]
-        ? SortType[pathSplit[7]]
-        : UserService.Instance.myUserInfo.match({
-            some: mui =>
-              Object.values(SortType)[
-                mui.local_user_view.local_user.default_sort_type
-              ],
-            none: SortType.Active,
-          })
-    );
-
-    let page = Some(pathSplit[9] ? Number(pathSplit[9]) : 1);
+    let type_: ListingType = pathSplit[5]
+      ? ListingType[pathSplit[5]]
+      : mui
+      ? Object.values(ListingType)[
+          mui.local_user_view.local_user.default_listing_type
+        ]
+      : ListingType.Local;
+    let sort: SortType = pathSplit[7]
+      ? SortType[pathSplit[7]]
+      : mui
+      ? (Object.values(SortType)[
+          mui.local_user_view.local_user.default_sort_type
+        ] as SortType)
+      : SortType.Active;
+
+    let page = pathSplit[9] ? Number(pathSplit[9]) : 1;
 
     let promises: Promise<any>[] = [];
 
     if (dataType == DataType.Post) {
-      let getPostsForm = new GetPosts({
-        community_id: None,
-        community_name: None,
+      let getPostsForm: GetPosts = {
         type_,
         page,
-        limit: Some(fetchLimit),
+        limit: fetchLimit,
         sort,
-        saved_only: Some(false),
-        auth: req.auth,
-      });
+        saved_only: false,
+        auth,
+      };
 
       promises.push(req.client.getPosts(getPostsForm));
       promises.push(Promise.resolve());
     } else {
-      let getCommentsForm = new GetComments({
-        community_id: None,
-        community_name: None,
+      let getCommentsForm: GetComments = {
         page,
-        limit: Some(fetchLimit),
-        max_depth: None,
-        sort: sort.map(postToCommentSortType),
+        limit: fetchLimit,
+        sort: postToCommentSortType(sort),
         type_,
-        saved_only: Some(false),
-        post_id: None,
-        parent_id: None,
-        auth: req.auth,
-      });
+        saved_only: false,
+        auth,
+      };
       promises.push(Promise.resolve());
       promises.push(req.client.getComments(getCommentsForm));
     }
 
-    let trendingCommunitiesForm = new ListCommunities({
-      type_: Some(ListingType.Local),
-      sort: Some(SortType.Hot),
-      limit: Some(trendingFetchLimit),
-      page: None,
-      auth: req.auth,
-    });
+    let trendingCommunitiesForm: ListCommunities = {
+      type_: ListingType.Local,
+      sort: SortType.Hot,
+      limit: trendingFetchLimit,
+      auth,
+    };
     promises.push(req.client.listCommunities(trendingCommunitiesForm));
 
     return promises;
@@ -317,33 +298,28 @@ export class Home extends Component<any, HomeState> {
 
   get documentTitle(): string {
     let siteView = this.state.siteRes.site_view;
-    return this.state.siteRes.site_view.site.description.match({
-      some: desc => `${siteView.site.name} - ${desc}`,
-      none: siteView.site.name,
-    });
+    let desc = this.state.siteRes.site_view.site.description;
+    return desc ? `${siteView.site.name} - ${desc}` : siteView.site.name;
   }
 
   render() {
+    let tagline = this.state.tagline;
+
     return (
       <div className="container-lg">
         <HtmlTags
           title={this.documentTitle}
           path={this.context.router.route.match.url}
-          description={None}
-          image={None}
         />
         {this.state.siteRes.site_view.local_site.site_setup && (
           <div className="row">
             <main role="main" className="col-12 col-md-8">
-              {this.state.tagline.match({
-                some: tagline => (
-                  <div
-                    id="tagline"
-                    dangerouslySetInnerHTML={mdToHtml(tagline)}
-                  ></div>
-                ),
-                none: <></>,
-              })}
+              {tagline && (
+                <div
+                  id="tagline"
+                  dangerouslySetInnerHTML={mdToHtml(tagline)}
+                ></div>
+              )}
               <div className="d-block d-md-none">{this.mobileView()}</div>
               {this.posts()}
             </main>
@@ -357,10 +333,8 @@ export class Home extends Component<any, HomeState> {
   }
 
   get hasFollows(): boolean {
-    return UserService.Instance.myUserInfo.match({
-      some: mui => mui.follows.length > 0,
-      none: false,
-    });
+    let mui = UserService.Instance.myUserInfo;
+    return !!mui && mui.follows.length > 0;
   }
 
   mobileView() {
@@ -412,9 +386,9 @@ export class Home extends Component<any, HomeState> {
           {this.state.showSidebarMobile && (
             <SiteSidebar
               site={siteView.site}
-              admins={Some(siteRes.admins)}
-              counts={Some(siteView.counts)}
-              online={Some(siteRes.online)}
+              admins={siteRes.admins}
+              counts={siteView.counts}
+              online={siteRes.online}
               showLocal={showLocal(this.isoData)}
             />
           )}
@@ -450,9 +424,9 @@ export class Home extends Component<any, HomeState> {
             </div>
             <SiteSidebar
               site={siteView.site}
-              admins={Some(siteRes.admins)}
-              counts={Some(siteView.counts)}
-              online={Some(siteRes.online)}
+              admins={siteRes.admins}
+              counts={siteView.counts}
+              online={siteRes.online}
               showLocal={showLocal(this.isoData)}
             />
             {this.hasFollows && (
@@ -532,17 +506,14 @@ export class Home extends Component<any, HomeState> {
         </h5>
         {!this.state.subscribedCollapsed && (
           <ul className="list-inline mb-0">
-            {UserService.Instance.myUserInfo
-              .map(m => m.follows)
-              .unwrapOr([])
-              .map(cfv => (
-                <li
-                  key={cfv.community.id}
-                  className="list-inline-item d-inline-block"
-                >
-                  <CommunityLink community={cfv.community} />
-                </li>
-              ))}
+            {UserService.Instance.myUserInfo?.follows.map(cfv => (
+              <li
+                key={cfv.community.id}
+                className="list-inline-item d-inline-block"
+              >
+                <CommunityLink community={cfv.community} />
+              </li>
+            ))}
           </ul>
         )}
       </div>
@@ -595,9 +566,6 @@ export class Home extends Component<any, HomeState> {
       <CommentNodes
         nodes={commentsToFlatNodes(this.state.comments)}
         viewType={CommentViewType.Flat}
-        moderators={None}
-        admins={None}
-        maxCommentsShown={None}
         noIndent
         showCommunity
         showContext
@@ -611,9 +579,10 @@ export class Home extends Component<any, HomeState> {
   selects() {
     let allRss = `/feeds/all.xml?sort=${this.state.sort}`;
     let localRss = `/feeds/local.xml?sort=${this.state.sort}`;
-    let frontRss = auth(false)
-      .ok()
-      .map(auth => `/feeds/front/${auth}.xml?sort=${this.state.sort}`);
+    let auth = myAuth(false);
+    let frontRss = auth
+      ? `/feeds/front/${auth}.xml?sort=${this.state.sort}`
+      : undefined;
 
     return (
       <div className="mb-3">
@@ -650,18 +619,14 @@ export class Home extends Component<any, HomeState> {
             <link rel="alternate" type="application/atom+xml" href={localRss} />
           </>
         )}
-        {this.state.listingType == ListingType.Subscribed &&
-          frontRss.match({
-            some: rss => (
-              <>
-                <a href={rss} title="RSS" rel={relTags}>
-                  <Icon icon="rss" classes="text-muted small" />
-                </a>
-                <link rel="alternate" type="application/atom+xml" href={rss} />
-              </>
-            ),
-            none: <></>,
-          })}
+        {this.state.listingType == ListingType.Subscribed && frontRss && (
+          <>
+            <a href={frontRss} title="RSS" rel={relTags}>
+              <Icon icon="rss" classes="text-muted small" />
+            </a>
+            <link rel="alternate" type="application/atom+xml" href={frontRss} />
+          </>
+        )}
       </div>
     );
   }
@@ -703,33 +668,27 @@ export class Home extends Component<any, HomeState> {
   }
 
   fetchData() {
+    let auth = myAuth(false);
     if (this.state.dataType == DataType.Post) {
-      let getPostsForm = new GetPosts({
-        community_id: None,
-        community_name: None,
-        page: Some(this.state.page),
-        limit: Some(fetchLimit),
-        sort: Some(this.state.sort),
-        saved_only: Some(false),
-        auth: auth(false).ok(),
-        type_: Some(this.state.listingType),
-      });
+      let getPostsForm: GetPosts = {
+        page: this.state.page,
+        limit: fetchLimit,
+        sort: this.state.sort,
+        saved_only: false,
+        type_: this.state.listingType,
+        auth,
+      };
 
       WebSocketService.Instance.send(wsClient.getPosts(getPostsForm));
     } else {
-      let getCommentsForm = new GetComments({
-        community_id: None,
-        community_name: None,
-        page: Some(this.state.page),
-        limit: Some(fetchLimit),
-        max_depth: None,
-        sort: Some(postToCommentSortType(this.state.sort)),
-        saved_only: Some(false),
-        post_id: None,
-        parent_id: None,
-        auth: auth(false).ok(),
-        type_: Some(this.state.listingType),
-      });
+      let getCommentsForm: GetComments = {
+        page: this.state.page,
+        limit: fetchLimit,
+        sort: postToCommentSortType(this.state.sort),
+        saved_only: false,
+        type_: this.state.listingType,
+        auth,
+      };
       WebSocketService.Instance.send(wsClient.getComments(getCommentsForm));
     }
   }
@@ -746,17 +705,14 @@ export class Home extends Component<any, HomeState> {
       );
       this.fetchData();
     } else if (op == UserOperation.ListCommunities) {
-      let data = wsJsonToRes<ListCommunitiesResponse>(
-        msg,
-        ListCommunitiesResponse
-      );
+      let data = wsJsonToRes<ListCommunitiesResponse>(msg);
       this.setState({ trendingCommunities: data.communities });
     } else if (op == UserOperation.EditSite) {
-      let data = wsJsonToRes<SiteResponse>(msg, SiteResponse);
+      let data = wsJsonToRes<SiteResponse>(msg);
       this.setState(s => ((s.siteRes.site_view = data.site_view), s));
       toast(i18n.t("site_saved"));
     } else if (op == UserOperation.GetPosts) {
-      let data = wsJsonToRes<GetPostsResponse>(msg, GetPostsResponse);
+      let data = wsJsonToRes<GetPostsResponse>(msg);
       this.setState({ posts: data.posts, loading: false });
       WebSocketService.Instance.send(
         wsClient.communityJoin({ community_id: 0 })
@@ -764,11 +720,10 @@ export class Home extends Component<any, HomeState> {
       restoreScrollPosition(this.context);
       setupTippy();
     } else if (op == UserOperation.CreatePost) {
-      let data = wsJsonToRes<PostResponse>(msg, PostResponse);
+      let data = wsJsonToRes<PostResponse>(msg);
+      let mui = UserService.Instance.myUserInfo;
 
-      let showPostNotifs = UserService.Instance.myUserInfo
-        .map(m => m.local_user_view.local_user.show_new_post_notifs)
-        .unwrapOr(false);
+      let showPostNotifs = mui?.local_user_view.local_user.show_new_post_notifs;
 
       // Only push these if you're on the first page, you pass the nsfw check, and it isn't blocked
       if (
@@ -779,9 +734,7 @@ export class Home extends Component<any, HomeState> {
         // If you're on subscribed, only push it if you're subscribed.
         if (this.state.listingType == ListingType.Subscribed) {
           if (
-            UserService.Instance.myUserInfo
-              .map(m => m.follows)
-              .unwrapOr([])
+            mui?.follows
               .map(c => c.community.id)
               .includes(data.post_view.community.id)
           ) {
@@ -814,45 +767,43 @@ export class Home extends Component<any, HomeState> {
       op == UserOperation.FeaturePost ||
       op == UserOperation.SavePost
     ) {
-      let data = wsJsonToRes<PostResponse>(msg, PostResponse);
+      let data = wsJsonToRes<PostResponse>(msg);
       editPostFindRes(data.post_view, this.state.posts);
       this.setState(this.state);
     } else if (op == UserOperation.CreatePostLike) {
-      let data = wsJsonToRes<PostResponse>(msg, PostResponse);
+      let data = wsJsonToRes<PostResponse>(msg);
       createPostLikeFindRes(data.post_view, this.state.posts);
       this.setState(this.state);
     } else if (op == UserOperation.AddAdmin) {
-      let data = wsJsonToRes<AddAdminResponse>(msg, AddAdminResponse);
+      let data = wsJsonToRes<AddAdminResponse>(msg);
       this.setState(s => ((s.siteRes.admins = data.admins), s));
     } else if (op == UserOperation.BanPerson) {
-      let data = wsJsonToRes<BanPersonResponse>(msg, BanPersonResponse);
+      let data = wsJsonToRes<BanPersonResponse>(msg);
       this.state.posts
         .filter(p => p.creator.id == data.person_view.person.id)
         .forEach(p => (p.creator.banned = data.banned));
 
       this.setState(this.state);
     } else if (op == UserOperation.GetComments) {
-      let data = wsJsonToRes<GetCommentsResponse>(msg, GetCommentsResponse);
+      let data = wsJsonToRes<GetCommentsResponse>(msg);
       this.setState({ comments: data.comments, loading: false });
     } else if (
       op == UserOperation.EditComment ||
       op == UserOperation.DeleteComment ||
       op == UserOperation.RemoveComment
     ) {
-      let data = wsJsonToRes<CommentResponse>(msg, CommentResponse);
+      let data = wsJsonToRes<CommentResponse>(msg);
       editCommentRes(data.comment_view, this.state.comments);
       this.setState(this.state);
     } else if (op == UserOperation.CreateComment) {
-      let data = wsJsonToRes<CommentResponse>(msg, CommentResponse);
+      let data = wsJsonToRes<CommentResponse>(msg);
 
       // Necessary since it might be a user reply
       if (data.form_id) {
         // If you're on subscribed, only push it if you're subscribed.
         if (this.state.listingType == ListingType.Subscribed) {
           if (
-            UserService.Instance.myUserInfo
-              .map(m => m.follows)
-              .unwrapOr([])
+            UserService.Instance.myUserInfo?.follows
               .map(c => c.community.id)
               .includes(data.comment_view.community.id)
           ) {
@@ -864,23 +815,23 @@ export class Home extends Component<any, HomeState> {
         this.setState(this.state);
       }
     } else if (op == UserOperation.SaveComment) {
-      let data = wsJsonToRes<CommentResponse>(msg, CommentResponse);
+      let data = wsJsonToRes<CommentResponse>(msg);
       saveCommentRes(data.comment_view, this.state.comments);
       this.setState(this.state);
     } else if (op == UserOperation.CreateCommentLike) {
-      let data = wsJsonToRes<CommentResponse>(msg, CommentResponse);
+      let data = wsJsonToRes<CommentResponse>(msg);
       createCommentLikeRes(data.comment_view, this.state.comments);
       this.setState(this.state);
     } else if (op == UserOperation.BlockPerson) {
-      let data = wsJsonToRes<BlockPersonResponse>(msg, BlockPersonResponse);
+      let data = wsJsonToRes<BlockPersonResponse>(msg);
       updatePersonBlock(data);
     } else if (op == UserOperation.CreatePostReport) {
-      let data = wsJsonToRes<PostReportResponse>(msg, PostReportResponse);
+      let data = wsJsonToRes<PostReportResponse>(msg);
       if (data) {
         toast(i18n.t("report_created"));
       }
     } else if (op == UserOperation.CreateCommentReport) {
-      let data = wsJsonToRes<CommentReportResponse>(msg, CommentReportResponse);
+      let data = wsJsonToRes<CommentReportResponse>(msg);
       if (data) {
         toast(i18n.t("report_created"));
       }
@@ -890,7 +841,7 @@ export class Home extends Component<any, HomeState> {
       op == UserOperation.PurgeComment ||
       op == UserOperation.PurgeCommunity
     ) {
-      let data = wsJsonToRes<PurgeItemResponse>(msg, PurgeItemResponse);
+      let data = wsJsonToRes<PurgeItemResponse>(msg);
       if (data.success) {
         toast(i18n.t("purge_success"));
         this.context.router.history.push(`/`);
index 434fae9237e8504dfea8c8692064f2a0ac4c1ce3..0870f6533c636f5c080544402e556b5772ab0ea1 100644 (file)
@@ -1,4 +1,3 @@
-import { None } from "@sniptt/monads";
 import { Component } from "inferno";
 import { GetSiteResponse } from "lemmy-js-client";
 import { i18n } from "../../i18next";
@@ -11,13 +10,12 @@ interface InstancesState {
 
 export class Instances extends Component<any, InstancesState> {
   private isoData = setIsoData(this.context);
-  private emptyState: InstancesState = {
+  state: InstancesState = {
     siteRes: this.isoData.site_res,
   };
 
   constructor(props: any, context: any) {
     super(props, context);
-    this.state = this.emptyState;
   }
 
   get documentTitle(): string {
@@ -25,45 +23,37 @@ export class Instances extends Component<any, InstancesState> {
   }
 
   render() {
-    return this.state.siteRes.federated_instances.match({
-      some: federated_instances => (
-        <div className="container-lg">
-          <HtmlTags
-            title={this.documentTitle}
-            path={this.context.router.route.match.url}
-            description={None}
-            image={None}
-          />
-          <div className="row">
-            <div className="col-md-6">
-              <h5>{i18n.t("linked_instances")}</h5>
-              {this.itemList(federated_instances.linked)}
-            </div>
-            {federated_instances.allowed.match({
-              some: allowed =>
-                allowed.length > 0 && (
-                  <div className="col-md-6">
-                    <h5>{i18n.t("allowed_instances")}</h5>
-                    {this.itemList(allowed)}
-                  </div>
-                ),
-              none: <></>,
-            })}
-            {federated_instances.blocked.match({
-              some: blocked =>
-                blocked.length > 0 && (
-                  <div className="col-md-6">
-                    <h5>{i18n.t("blocked_instances")}</h5>
-                    {this.itemList(blocked)}
-                  </div>
-                ),
-              none: <></>,
-            })}
+    let federated_instances = this.state.siteRes.federated_instances;
+    return federated_instances ? (
+      <div className="container-lg">
+        <HtmlTags
+          title={this.documentTitle}
+          path={this.context.router.route.match.url}
+        />
+        <div className="row">
+          <div className="col-md-6">
+            <h5>{i18n.t("linked_instances")}</h5>
+            {this.itemList(federated_instances.linked)}
           </div>
+          {federated_instances.allowed &&
+            federated_instances.allowed.length > 0 && (
+              <div className="col-md-6">
+                <h5>{i18n.t("allowed_instances")}</h5>
+                {this.itemList(federated_instances.allowed)}
+              </div>
+            )}
+          {federated_instances.blocked &&
+            federated_instances.blocked.length > 0 && (
+              <div className="col-md-6">
+                <h5>{i18n.t("blocked_instances")}</h5>
+                {this.itemList(federated_instances.blocked)}
+              </div>
+            )}
         </div>
-      ),
-      none: <></>,
-    });
+      </div>
+    ) : (
+      <></>
+    );
   }
 
   itemList(items: string[]) {
index 3930db20e40e58fda111716e9343ce1802ea2cc3..d2c995324f1969ef187cddbe84f587cf7cd5e521 100644 (file)
@@ -1,4 +1,3 @@
-import { None } from "@sniptt/monads";
 import { Component } from "inferno";
 import { GetSiteResponse } from "lemmy-js-client";
 import { i18n } from "../../i18next";
@@ -11,13 +10,12 @@ interface LegalState {
 
 export class Legal extends Component<any, LegalState> {
   private isoData = setIsoData(this.context);
-  private emptyState: LegalState = {
+  state: LegalState = {
     siteRes: this.isoData.site_res,
   };
 
   constructor(props: any, context: any) {
     super(props, context);
-    this.state = this.emptyState;
   }
 
   get documentTitle(): string {
@@ -25,20 +23,16 @@ export class Legal extends Component<any, LegalState> {
   }
 
   render() {
+    let legal = this.state.siteRes.site_view.local_site.legal_information;
     return (
       <div className="container-lg">
         <HtmlTags
           title={this.documentTitle}
           path={this.context.router.route.match.url}
-          description={None}
-          image={None}
         />
-        {this.state.siteRes.site_view.local_site.legal_information.match({
-          some: legal => (
-            <div className="md-div" dangerouslySetInnerHTML={mdToHtml(legal)} />
-          ),
-          none: <></>,
-        })}
+        {legal && (
+          <div className="md-div" dangerouslySetInnerHTML={mdToHtml(legal)} />
+        )}
       </div>
     );
   }
index 650b3173201926c195c2d95b8d67c29ae5051ae7..7cdde927684f63b9868adbac77221a941a7b68f2 100644 (file)
@@ -1,8 +1,7 @@
-import { None } from "@sniptt/monads";
 import { Component, linkEvent } from "inferno";
 import {
   GetSiteResponse,
-  Login as LoginForm,
+  Login as LoginI,
   LoginResponse,
   PasswordReset,
   UserOperation,
@@ -24,20 +23,20 @@ import { HtmlTags } from "../common/html-tags";
 import { Spinner } from "../common/icon";
 
 interface State {
-  loginForm: LoginForm;
+  form: {
+    username_or_email?: string;
+    password?: string;
+  };
   loginLoading: boolean;
   siteRes: GetSiteResponse;
 }
 
 export class Login extends Component<any, State> {
   private isoData = setIsoData(this.context);
-  private subscription: Subscription;
+  private subscription?: Subscription;
 
-  emptyState: State = {
-    loginForm: new LoginForm({
-      username_or_email: undefined,
-      password: undefined,
-    }),
+  state: State = {
+    form: {},
     loginLoading: false,
     siteRes: this.isoData.site_res,
   };
@@ -45,8 +44,6 @@ export class Login extends Component<any, State> {
   constructor(props: any, context: any) {
     super(props, context);
 
-    this.state = this.emptyState;
-
     this.parseMessage = this.parseMessage.bind(this);
     this.subscription = wsSubscribe(this.parseMessage);
 
@@ -57,14 +54,14 @@ export class Login extends Component<any, State> {
 
   componentDidMount() {
     // Navigate to home if already logged in
-    if (UserService.Instance.myUserInfo.isSome()) {
+    if (UserService.Instance.myUserInfo) {
       this.context.router.history.push("/");
     }
   }
 
   componentWillUnmount() {
     if (isBrowser()) {
-      this.subscription.unsubscribe();
+      this.subscription?.unsubscribe();
     }
   }
 
@@ -82,8 +79,6 @@ export class Login extends Component<any, State> {
         <HtmlTags
           title={this.documentTitle}
           path={this.context.router.route.match.url}
-          description={None}
-          image={None}
         />
         <div className="row">
           <div className="col-12 col-lg-6 offset-lg-3">{this.loginForm()}</div>
@@ -109,7 +104,7 @@ export class Login extends Component<any, State> {
                 type="text"
                 className="form-control"
                 id="login-email-or-username"
-                value={this.state.loginForm.username_or_email}
+                value={this.state.form.username_or_email}
                 onInput={linkEvent(this, this.handleLoginUsernameChange)}
                 autoComplete="email"
                 required
@@ -125,7 +120,7 @@ export class Login extends Component<any, State> {
               <input
                 type="password"
                 id="login-password"
-                value={this.state.loginForm.password}
+                value={this.state.form.password}
                 onInput={linkEvent(this, this.handleLoginPasswordChange)}
                 className="form-control"
                 autoComplete="current-password"
@@ -136,7 +131,10 @@ export class Login extends Component<any, State> {
                 type="button"
                 onClick={linkEvent(this, this.handlePasswordReset)}
                 className="btn p-0 btn-link d-inline-block float-right text-muted small font-weight-bold pointer-events not-allowed"
-                disabled={!validEmail(this.state.loginForm.username_or_email)}
+                disabled={
+                  !!this.state.form.username_or_email &&
+                  !validEmail(this.state.form.username_or_email)
+                }
                 title={i18n.t("no_password_reset")}
               >
                 {i18n.t("forgot_password")}
@@ -158,25 +156,35 @@ export class Login extends Component<any, State> {
   handleLoginSubmit(i: Login, event: any) {
     event.preventDefault();
     i.setState({ loginLoading: true });
-    WebSocketService.Instance.send(wsClient.login(i.state.loginForm));
+    let lForm = i.state.form;
+    let username_or_email = lForm.username_or_email;
+    let password = lForm.password;
+    if (username_or_email && password) {
+      let form: LoginI = {
+        username_or_email,
+        password,
+      };
+      WebSocketService.Instance.send(wsClient.login(form));
+    }
   }
 
   handleLoginUsernameChange(i: Login, event: any) {
-    i.state.loginForm.username_or_email = event.target.value;
+    i.state.form.username_or_email = event.target.value;
     i.setState(i.state);
   }
 
   handleLoginPasswordChange(i: Login, event: any) {
-    i.state.loginForm.password = event.target.value;
+    i.state.form.password = event.target.value;
     i.setState(i.state);
   }
 
   handlePasswordReset(i: Login, event: any) {
     event.preventDefault();
-    let resetForm = new PasswordReset({
-      email: i.state.loginForm.username_or_email,
-    });
-    WebSocketService.Instance.send(wsClient.passwordReset(resetForm));
+    let email = i.state.form.username_or_email;
+    if (email) {
+      let resetForm: PasswordReset = { email };
+      WebSocketService.Instance.send(wsClient.passwordReset(resetForm));
+    }
   }
 
   parseMessage(msg: any) {
@@ -184,19 +192,18 @@ export class Login extends Component<any, State> {
     console.log(msg);
     if (msg.error) {
       toast(i18n.t(msg.error), "danger");
-      this.setState(this.emptyState);
+      this.setState({ form: {} });
       return;
     } else {
       if (op == UserOperation.Login) {
-        let data = wsJsonToRes<LoginResponse>(msg, LoginResponse);
-        this.setState(this.emptyState);
+        let data = wsJsonToRes<LoginResponse>(msg);
         UserService.Instance.login(data);
         this.props.history.push("/");
         location.reload();
       } else if (op == UserOperation.PasswordReset) {
         toast(i18n.t("reset_password_mail_sent"));
       } else if (op == UserOperation.GetSite) {
-        let data = wsJsonToRes<GetSiteResponse>(msg, GetSiteResponse);
+        let data = wsJsonToRes<GetSiteResponse>(msg);
         this.setState({ siteRes: data });
       }
     }
index 943e74a19a507e7cb6355a038d8392f534c36371..726edc314a2e4b2d9b29e46dc54e47c43ba9f750 100644 (file)
@@ -1,11 +1,9 @@
-import { None, Some } from "@sniptt/monads";
 import { Component, linkEvent } from "inferno";
 import { Helmet } from "inferno-helmet";
 import {
   GetSiteResponse,
   LoginResponse,
   Register,
-  toUndefined,
   UserOperation,
   wsJsonToRes,
   wsUserOp,
@@ -19,7 +17,17 @@ import { Spinner } from "../common/icon";
 import { SiteForm } from "./site-form";
 
 interface State {
-  userForm: Register;
+  form: {
+    username?: string;
+    email?: string;
+    password?: string;
+    password_verify?: string;
+    show_nsfw: boolean;
+    captcha_uuid?: string;
+    captcha_answer?: string;
+    honeypot?: string;
+    answer?: string;
+  };
   doneRegisteringUser: boolean;
   userLoading: boolean;
   siteRes: GetSiteResponse;
@@ -29,20 +37,11 @@ export class Setup extends Component<any, State> {
   private subscription: Subscription;
   private isoData = setIsoData(this.context);
 
-  private emptyState: State = {
-    userForm: new Register({
-      username: undefined,
-      password: undefined,
-      password_verify: undefined,
+  state: State = {
+    form: {
       show_nsfw: true,
-      // The first admin signup doesn't need a captcha
-      captcha_uuid: None,
-      captcha_answer: None,
-      email: None,
-      honeypot: None,
-      answer: None,
-    }),
-    doneRegisteringUser: UserService.Instance.myUserInfo.isSome(),
+    },
+    doneRegisteringUser: !!UserService.Instance.myUserInfo,
     userLoading: false,
     siteRes: this.isoData.site_res,
   };
@@ -50,8 +49,6 @@ export class Setup extends Component<any, State> {
   constructor(props: any, context: any) {
     super(props, context);
 
-    this.state = this.emptyState;
-
     this.subscription = WebSocketService.Instance.subject
       .pipe(retryWhen(errors => errors.pipe(delay(3000), take(10))))
       .subscribe(
@@ -100,7 +97,7 @@ export class Setup extends Component<any, State> {
               type="text"
               className="form-control"
               id="username"
-              value={this.state.userForm.username}
+              value={this.state.form.username}
               onInput={linkEvent(this, this.handleRegisterUsernameChange)}
               required
               minLength={3}
@@ -119,7 +116,7 @@ export class Setup extends Component<any, State> {
               id="email"
               className="form-control"
               placeholder={i18n.t("optional")}
-              value={toUndefined(this.state.userForm.email)}
+              value={this.state.form.email}
               onInput={linkEvent(this, this.handleRegisterEmailChange)}
               minLength={3}
             />
@@ -133,7 +130,7 @@ export class Setup extends Component<any, State> {
             <input
               type="password"
               id="password"
-              value={this.state.userForm.password}
+              value={this.state.form.password}
               onInput={linkEvent(this, this.handleRegisterPasswordChange)}
               className="form-control"
               required
@@ -151,7 +148,7 @@ export class Setup extends Component<any, State> {
             <input
               type="password"
               id="verify-password"
-              value={this.state.userForm.password_verify}
+              value={this.state.form.password_verify}
               onInput={linkEvent(this, this.handleRegisterPasswordVerifyChange)}
               className="form-control"
               required
@@ -176,26 +173,40 @@ export class Setup extends Component<any, State> {
     event.preventDefault();
     i.setState({ userLoading: true });
     event.preventDefault();
-    WebSocketService.Instance.send(wsClient.register(i.state.userForm));
+    let cForm = i.state.form;
+    if (cForm.username && cForm.password && cForm.password_verify) {
+      let form: Register = {
+        username: cForm.username,
+        password: cForm.password,
+        password_verify: cForm.password_verify,
+        email: cForm.email,
+        show_nsfw: cForm.show_nsfw,
+        captcha_uuid: cForm.captcha_uuid,
+        captcha_answer: cForm.captcha_answer,
+        honeypot: cForm.honeypot,
+        answer: cForm.answer,
+      };
+      WebSocketService.Instance.send(wsClient.register(form));
+    }
   }
 
   handleRegisterUsernameChange(i: Setup, event: any) {
-    i.state.userForm.username = event.target.value;
+    i.state.form.username = event.target.value;
     i.setState(i.state);
   }
 
   handleRegisterEmailChange(i: Setup, event: any) {
-    i.state.userForm.email = Some(event.target.value);
+    i.state.form.email = event.target.value;
     i.setState(i.state);
   }
 
   handleRegisterPasswordChange(i: Setup, event: any) {
-    i.state.userForm.password = event.target.value;
+    i.state.form.password = event.target.value;
     i.setState(i.state);
   }
 
   handleRegisterPasswordVerifyChange(i: Setup, event: any) {
-    i.state.userForm.password_verify = event.target.value;
+    i.state.form.password_verify = event.target.value;
     i.setState(i.state);
   }
 
@@ -206,10 +217,10 @@ export class Setup extends Component<any, State> {
       this.setState({ userLoading: false });
       return;
     } else if (op == UserOperation.Register) {
-      let data = wsJsonToRes<LoginResponse>(msg, LoginResponse);
+      let data = wsJsonToRes<LoginResponse>(msg);
       this.setState({ userLoading: false });
       UserService.Instance.login(data);
-      if (UserService.Instance.jwtInfo.isSome()) {
+      if (UserService.Instance.jwtInfo) {
         this.setState({ doneRegisteringUser: true });
       }
     } else if (op == UserOperation.CreateSite) {
index 7e35ecd65cea720800c8ff802e14d4f02d01d570..5deb80264e781aa161829f5f26c5649b45759f86 100644 (file)
@@ -1,4 +1,3 @@
-import { None, Option, Some } from "@sniptt/monads";
 import { Options, passwordStrength } from "check-password-strength";
 import { I18nKeys } from "i18next";
 import { Component, linkEvent } from "inferno";
@@ -9,8 +8,8 @@ import {
   GetSiteResponse,
   LoginResponse,
   Register,
+  RegistrationMode,
   SiteView,
-  toUndefined,
   UserOperation,
   wsJsonToRes,
   wsUserOp,
@@ -60,32 +59,33 @@ const passwordStrengthOptions: Options<string> = [
 ];
 
 interface State {
-  registerForm: Register;
+  form: {
+    username?: string;
+    email?: string;
+    password?: string;
+    password_verify?: string;
+    show_nsfw: boolean;
+    captcha_uuid?: string;
+    captcha_answer?: string;
+    honeypot?: string;
+    answer?: string;
+  };
   registerLoading: boolean;
-  captcha: Option<GetCaptchaResponse>;
+  captcha?: GetCaptchaResponse;
   captchaPlaying: boolean;
   siteRes: GetSiteResponse;
 }
 
 export class Signup extends Component<any, State> {
   private isoData = setIsoData(this.context);
-  private subscription: Subscription;
-  private audio: HTMLAudioElement;
-
-  emptyState: State = {
-    registerForm: new Register({
-      username: undefined,
-      password: undefined,
-      password_verify: undefined,
+  private subscription?: Subscription;
+  private audio?: HTMLAudioElement;
+
+  state: State = {
+    form: {
       show_nsfw: false,
-      captcha_uuid: None,
-      captcha_answer: None,
-      honeypot: None,
-      answer: None,
-      email: None,
-    }),
+    },
     registerLoading: false,
-    captcha: None,
     captchaPlaying: false,
     siteRes: this.isoData.site_res,
   };
@@ -93,7 +93,6 @@ export class Signup extends Component<any, State> {
   constructor(props: any, context: any) {
     super(props, context);
 
-    this.state = this.emptyState;
     this.handleAnswerChange = this.handleAnswerChange.bind(this);
 
     this.parseMessage = this.parseMessage.bind(this);
@@ -106,7 +105,7 @@ export class Signup extends Component<any, State> {
 
   componentWillUnmount() {
     if (isBrowser()) {
-      this.subscription.unsubscribe();
+      this.subscription?.unsubscribe();
     }
   }
 
@@ -131,8 +130,6 @@ export class Signup extends Component<any, State> {
         <HtmlTags
           title={this.documentTitle}
           path={this.context.router.route.match.url}
-          description={None}
-          image={None}
         />
         <div className="row">
           <div className="col-12 col-lg-6 offset-lg-3">
@@ -172,7 +169,7 @@ export class Signup extends Component<any, State> {
               type="text"
               id="register-username"
               className="form-control"
-              value={this.state.registerForm.username}
+              value={this.state.form.username}
               onInput={linkEvent(this, this.handleRegisterUsernameChange)}
               required
               minLength={3}
@@ -196,14 +193,15 @@ export class Signup extends Component<any, State> {
                   ? i18n.t("required")
                   : i18n.t("optional")
               }
-              value={toUndefined(this.state.registerForm.email)}
+              value={this.state.form.email}
               autoComplete="email"
               onInput={linkEvent(this, this.handleRegisterEmailChange)}
               required={siteView.local_site.require_email_verification}
               minLength={3}
             />
             {!siteView.local_site.require_email_verification &&
-              !this.state.registerForm.email.map(validEmail).unwrapOr(true) && (
+              this.state.form.email &&
+              !validEmail(this.state.form.email) && (
                 <div className="mt-2 mb-0 alert alert-warning" role="alert">
                   <Icon icon="alert-triangle" classes="icon-inline mr-2" />
                   {i18n.t("no_password_reset")}
@@ -223,7 +221,7 @@ export class Signup extends Component<any, State> {
             <input
               type="password"
               id="register-password"
-              value={this.state.registerForm.password}
+              value={this.state.form.password}
               autoComplete="new-password"
               onInput={linkEvent(this, this.handleRegisterPasswordChange)}
               minLength={10}
@@ -231,7 +229,7 @@ export class Signup extends Component<any, State> {
               className="form-control"
               required
             />
-            {this.state.registerForm.password && (
+            {this.state.form.password && (
               <div className={this.passwordColorClass}>
                 {i18n.t(this.passwordStrength as I18nKeys)}
               </div>
@@ -250,7 +248,7 @@ export class Signup extends Component<any, State> {
             <input
               type="password"
               id="register-verify-password"
-              value={this.state.registerForm.password_verify}
+              value={this.state.form.password_verify}
               autoComplete="new-password"
               onInput={linkEvent(this, this.handleRegisterPasswordVerifyChange)}
               maxLength={60}
@@ -260,7 +258,8 @@ export class Signup extends Component<any, State> {
           </div>
         </div>
 
-        {siteView.local_site.require_application && (
+        {siteView.local_site.registration_mode ==
+          RegistrationMode.RequireApplication && (
           <>
             <div className="form-group row">
               <div className="offset-sm-2 col-sm-10">
@@ -268,15 +267,14 @@ export class Signup extends Component<any, State> {
                   <Icon icon="alert-triangle" classes="icon-inline mr-2" />
                   {i18n.t("fill_out_application")}
                 </div>
-                {siteView.local_site.application_question.match({
-                  some: question => (
-                    <div
-                      className="md-div"
-                      dangerouslySetInnerHTML={mdToHtml(question)}
-                    />
-                  ),
-                  none: <></>,
-                })}
+                {siteView.local_site.application_question && (
+                  <div
+                    className="md-div"
+                    dangerouslySetInnerHTML={mdToHtml(
+                      siteView.local_site.application_question
+                    )}
+                  />
+                )}
               </div>
             </div>
 
@@ -289,11 +287,6 @@ export class Signup extends Component<any, State> {
               </label>
               <div className="col-sm-10">
                 <MarkdownTextArea
-                  initialContent={None}
-                  initialLanguageId={None}
-                  placeholder={None}
-                  buttonTitle={None}
-                  maxLength={None}
                   onContentChange={this.handleAnswerChange}
                   hideNavigationWarnings
                   allLanguages={[]}
@@ -304,7 +297,7 @@ export class Signup extends Component<any, State> {
           </>
         )}
 
-        {this.state.captcha.isSome() && (
+        {this.state.captcha && (
           <div className="form-group row">
             <label className="col-sm-2" htmlFor="register-captcha">
               <span className="mr-2">{i18n.t("enter_code")}</span>
@@ -323,7 +316,7 @@ export class Signup extends Component<any, State> {
                 type="text"
                 className="form-control"
                 id="register-captcha"
-                value={toUndefined(this.state.registerForm.captcha_answer)}
+                value={this.state.form.captcha_answer}
                 onInput={linkEvent(
                   this,
                   this.handleRegisterCaptchaAnswerChange
@@ -341,7 +334,7 @@ export class Signup extends Component<any, State> {
                   className="form-check-input"
                   id="register-show-nsfw"
                   type="checkbox"
-                  checked={this.state.registerForm.show_nsfw}
+                  checked={this.state.form.show_nsfw}
                   onChange={linkEvent(this, this.handleRegisterShowNsfwChange)}
                 />
                 <label
@@ -361,7 +354,7 @@ export class Signup extends Component<any, State> {
           type="text"
           className="form-control honeypot"
           id="register-honey"
-          value={toUndefined(this.state.registerForm.honeypot)}
+          value={this.state.form.honeypot}
           onInput={linkEvent(this, this.handleHoneyPotChange)}
         />
         <div className="form-group row">
@@ -380,51 +373,46 @@ export class Signup extends Component<any, State> {
   }
 
   showCaptcha() {
-    return this.state.captcha.match({
-      some: captcha => (
-        <div className="col-sm-4">
-          {captcha.ok.match({
-            some: res => (
-              <>
-                <img
-                  className="rounded-top img-fluid"
-                  src={this.captchaPngSrc(res)}
-                  style="border-bottom-right-radius: 0; border-bottom-left-radius: 0;"
-                  alt={i18n.t("captcha")}
-                />
-                {res.wav.isSome() && (
-                  <button
-                    className="rounded-bottom btn btn-sm btn-secondary btn-block"
-                    style="border-top-right-radius: 0; border-top-left-radius: 0;"
-                    title={i18n.t("play_captcha_audio")}
-                    onClick={linkEvent(this, this.handleCaptchaPlay)}
-                    type="button"
-                    disabled={this.state.captchaPlaying}
-                  >
-                    <Icon icon="play" classes="icon-play" />
-                  </button>
-                )}
-              </>
-            ),
-            none: <></>,
-          })}
-        </div>
-      ),
-      none: <></>,
-    });
+    let captchaRes = this.state.captcha?.ok;
+    return captchaRes ? (
+      <div className="col-sm-4">
+        <>
+          <img
+            className="rounded-top img-fluid"
+            src={this.captchaPngSrc(captchaRes)}
+            style="border-bottom-right-radius: 0; border-bottom-left-radius: 0;"
+            alt={i18n.t("captcha")}
+          />
+          {captchaRes.wav && (
+            <button
+              className="rounded-bottom btn btn-sm btn-secondary btn-block"
+              style="border-top-right-radius: 0; border-top-left-radius: 0;"
+              title={i18n.t("play_captcha_audio")}
+              onClick={linkEvent(this, this.handleCaptchaPlay)}
+              type="button"
+              disabled={this.state.captchaPlaying}
+            >
+              <Icon icon="play" classes="icon-play" />
+            </button>
+          )}
+        </>
+      </div>
+    ) : (
+      <></>
+    );
   }
 
-  get passwordStrength() {
-    return passwordStrength(
-      this.state.registerForm.password,
-      passwordStrengthOptions
-    ).value;
+  get passwordStrength(): string | undefined {
+    let password = this.state.form.password;
+    return password
+      ? passwordStrength(password, passwordStrengthOptions).value
+      : undefined;
   }
 
   get passwordColorClass(): string {
     let strength = this.passwordStrength;
 
-    if (["weak", "medium"].includes(strength)) {
+    if (strength && ["weak", "medium"].includes(strength)) {
       return "text-warning";
     } else if (strength == "strong") {
       return "text-success";
@@ -436,53 +424,67 @@ export class Signup extends Component<any, State> {
   handleRegisterSubmit(i: Signup, event: any) {
     event.preventDefault();
     i.setState({ registerLoading: true });
-    WebSocketService.Instance.send(wsClient.register(i.state.registerForm));
+    let cForm = i.state.form;
+    if (cForm.username && cForm.password && cForm.password_verify) {
+      let form: Register = {
+        username: cForm.username,
+        password: cForm.password,
+        password_verify: cForm.password_verify,
+        email: cForm.email,
+        show_nsfw: cForm.show_nsfw,
+        captcha_uuid: cForm.captcha_uuid,
+        captcha_answer: cForm.captcha_answer,
+        honeypot: cForm.honeypot,
+        answer: cForm.answer,
+      };
+      WebSocketService.Instance.send(wsClient.register(form));
+    }
   }
 
   handleRegisterUsernameChange(i: Signup, event: any) {
-    i.state.registerForm.username = event.target.value;
+    i.state.form.username = event.target.value;
     i.setState(i.state);
   }
 
   handleRegisterEmailChange(i: Signup, event: any) {
-    i.state.registerForm.email = Some(event.target.value);
-    if (i.state.registerForm.email.unwrap() == "") {
-      i.state.registerForm.email = None;
+    i.state.form.email = event.target.value;
+    if (i.state.form.email == "") {
+      i.state.form.email = undefined;
     }
     i.setState(i.state);
   }
 
   handleRegisterPasswordChange(i: Signup, event: any) {
-    i.state.registerForm.password = event.target.value;
+    i.state.form.password = event.target.value;
     i.setState(i.state);
   }
 
   handleRegisterPasswordVerifyChange(i: Signup, event: any) {
-    i.state.registerForm.password_verify = event.target.value;
+    i.state.form.password_verify = event.target.value;
     i.setState(i.state);
   }
 
   handleRegisterShowNsfwChange(i: Signup, event: any) {
-    i.state.registerForm.show_nsfw = event.target.checked;
+    i.state.form.show_nsfw = event.target.checked;
     i.setState(i.state);
   }
 
   handleRegisterCaptchaAnswerChange(i: Signup, event: any) {
-    i.state.registerForm.captcha_answer = Some(event.target.value);
+    i.state.form.captcha_answer = event.target.value;
     i.setState(i.state);
   }
 
   handleAnswerChange(val: string) {
-    this.setState(s => ((s.registerForm.answer = Some(val)), s));
+    this.setState(s => ((s.form.answer = val), s));
   }
 
   handleHoneyPotChange(i: Signup, event: any) {
-    i.state.registerForm.honeypot = Some(event.target.value);
+    i.state.form.honeypot = event.target.value;
     i.setState(i.state);
   }
 
   handleRegenCaptcha(i: Signup) {
-    i.audio = null;
+    i.audio = undefined;
     i.setState({ captchaPlaying: false });
     WebSocketService.Instance.send(wsClient.getCaptcha());
   }
@@ -490,28 +492,23 @@ export class Signup extends Component<any, State> {
   handleCaptchaPlay(i: Signup) {
     // This was a bad bug, it should only build the new audio on a new file.
     // Replays would stop prematurely if this was rebuilt every time.
-    i.state.captcha.match({
-      some: captcha =>
-        captcha.ok.match({
-          some: res => {
-            if (i.audio == null) {
-              let base64 = `data:audio/wav;base64,${res.wav}`;
-              i.audio = new Audio(base64);
-            }
-
-            i.audio.play();
-
-            i.setState({ captchaPlaying: true });
-
-            i.audio.addEventListener("ended", () => {
-              i.audio.currentTime = 0;
-              i.setState({ captchaPlaying: false });
-            });
-          },
-          none: void 0,
-        }),
-      none: void 0,
-    });
+    let captchaRes = i.state.captcha?.ok;
+    if (captchaRes) {
+      if (!i.audio) {
+        let base64 = `data:audio/wav;base64,${captchaRes.wav}`;
+        i.audio = new Audio(base64);
+        i.audio.play();
+
+        i.setState({ captchaPlaying: true });
+
+        i.audio.addEventListener("ended", () => {
+          if (i.audio) {
+            i.audio.currentTime = 0;
+            i.setState({ captchaPlaying: false });
+          }
+        });
+      }
+    }
   }
 
   captchaPngSrc(captcha: CaptchaResponse) {
@@ -523,17 +520,15 @@ export class Signup extends Component<any, State> {
     console.log(msg);
     if (msg.error) {
       toast(i18n.t(msg.error), "danger");
-      this.setState(this.emptyState);
-      this.setState(s => ((s.registerForm.captcha_answer = undefined), s));
+      this.setState(s => ((s.form.captcha_answer = undefined), s));
       // Refetch another captcha
       // WebSocketService.Instance.send(wsClient.getCaptcha());
       return;
     } else {
       if (op == UserOperation.Register) {
-        let data = wsJsonToRes<LoginResponse>(msg, LoginResponse);
-        this.setState(this.emptyState);
+        let data = wsJsonToRes<LoginResponse>(msg);
         // Only log them in if a jwt was set
-        if (data.jwt.isSome()) {
+        if (data.jwt) {
           UserService.Instance.login(data);
           this.props.history.push("/communities");
           location.reload();
@@ -547,20 +542,15 @@ export class Signup extends Component<any, State> {
           this.props.history.push("/");
         }
       } else if (op == UserOperation.GetCaptcha) {
-        let data = wsJsonToRes<GetCaptchaResponse>(msg, GetCaptchaResponse);
-        data.ok.match({
-          some: res => {
-            this.setState({ captcha: Some(data) });
-            this.setState(
-              s => ((s.registerForm.captcha_uuid = Some(res.uuid)), s)
-            );
-          },
-          none: void 0,
-        });
+        let data = wsJsonToRes<GetCaptchaResponse>(msg);
+        if (data.ok) {
+          this.setState({ captcha: data });
+          this.setState(s => ((s.form.captcha_uuid = data.ok?.uuid), s));
+        }
       } else if (op == UserOperation.PasswordReset) {
         toast(i18n.t("reset_password_mail_sent"));
       } else if (op == UserOperation.GetSite) {
-        let data = wsJsonToRes<GetSiteResponse>(msg, GetSiteResponse);
+        let data = wsJsonToRes<GetSiteResponse>(msg);
         this.setState({ siteRes: data });
       }
     }
index 58532b63be8f5418afbfb370ebfc797b1e03192f..668f00dd48116fab733743a085b522f37abc135c 100644 (file)
@@ -1,4 +1,3 @@
-import { None, Option, Some } from "@sniptt/monads";
 import { Component, InfernoMouseEvent, linkEvent } from "inferno";
 import { Prompt } from "inferno-router";
 import {
@@ -6,14 +5,14 @@ import {
   EditSite,
   GetSiteResponse,
   ListingType,
-  toUndefined,
+  RegistrationMode,
 } from "lemmy-js-client";
 import { i18n } from "../../i18next";
 import { WebSocketService } from "../../services";
 import {
-  auth,
   capitalizeFirstLetter,
   fetchThemeList,
+  myAuth,
   wsClient,
 } from "../../utils";
 import { Icon, Spinner } from "../common/icon";
@@ -30,63 +29,20 @@ interface SiteFormProps {
 interface SiteFormState {
   siteForm: EditSite;
   loading: boolean;
-  themeList: Option<string[]>;
+  themeList?: string[];
 }
 
 export class SiteForm extends Component<SiteFormProps, SiteFormState> {
-  private emptyState: SiteFormState = {
-    siteForm: new EditSite({
-      enable_downvotes: None,
-      open_registration: None,
-      enable_nsfw: None,
-      name: None,
-      icon: None,
-      banner: None,
-      require_email_verification: None,
-      require_application: None,
-      application_question: None,
-      private_instance: None,
-      default_theme: None,
-      sidebar: None,
-      default_post_listing_type: None,
-      legal_information: None,
-      description: None,
-      community_creation_admin_only: None,
-      application_email_admins: None,
-      hide_modlog_mod_names: None,
-      discussion_languages: None,
-      slur_filter_regex: None,
-      actor_name_max_length: None,
-      rate_limit_message: None,
-      rate_limit_message_per_second: None,
-      rate_limit_comment: None,
-      rate_limit_comment_per_second: None,
-      rate_limit_image: None,
-      rate_limit_image_per_second: None,
-      rate_limit_post: None,
-      rate_limit_post_per_second: None,
-      rate_limit_register: None,
-      rate_limit_register_per_second: None,
-      rate_limit_search: None,
-      rate_limit_search_per_second: None,
-      federation_enabled: None,
-      federation_debug: None,
-      federation_worker_count: None,
-      captcha_enabled: None,
-      captcha_difficulty: None,
-      allowed_instances: None,
-      blocked_instances: None,
-      taglines: None,
-      auth: undefined,
-    }),
+  state: SiteFormState = {
+    siteForm: {
+      auth: "TODO",
+    },
     loading: false,
-    themeList: None,
   };
 
   constructor(props: any, context: any) {
     super(props, context);
 
-    this.state = this.emptyState;
     this.handleSiteSidebarChange = this.handleSiteSidebarChange.bind(this);
     this.handleSiteLegalInfoChange = this.handleSiteLegalInfoChange.bind(this);
     this.handleSiteApplicationQuestionChange =
@@ -109,59 +65,54 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
     let lsrl = this.props.siteRes.site_view.local_site_rate_limit;
     this.state = {
       ...this.state,
-      siteForm: new EditSite({
-        name: Some(site.name),
+      siteForm: {
+        name: site.name,
         sidebar: site.sidebar,
         description: site.description,
-        enable_downvotes: Some(ls.enable_downvotes),
-        open_registration: Some(ls.open_registration),
-        enable_nsfw: Some(ls.enable_nsfw),
-        community_creation_admin_only: Some(ls.community_creation_admin_only),
+        enable_downvotes: ls.enable_downvotes,
+        registration_mode: ls.registration_mode,
+        enable_nsfw: ls.enable_nsfw,
+        community_creation_admin_only: ls.community_creation_admin_only,
         icon: site.icon,
         banner: site.banner,
-        require_email_verification: Some(ls.require_email_verification),
-        require_application: Some(ls.require_application),
+        require_email_verification: ls.require_email_verification,
         application_question: ls.application_question,
-        private_instance: Some(ls.private_instance),
-        default_theme: Some(ls.default_theme),
-        default_post_listing_type: Some(ls.default_post_listing_type),
+        private_instance: ls.private_instance,
+        default_theme: ls.default_theme,
+        default_post_listing_type: ls.default_post_listing_type,
         legal_information: ls.legal_information,
-        application_email_admins: Some(ls.application_email_admins),
-        hide_modlog_mod_names: Some(ls.hide_modlog_mod_names),
-        discussion_languages: Some(this.props.siteRes.discussion_languages),
+        application_email_admins: ls.application_email_admins,
+        hide_modlog_mod_names: ls.hide_modlog_mod_names,
+        discussion_languages: this.props.siteRes.discussion_languages,
         slur_filter_regex: ls.slur_filter_regex,
-        actor_name_max_length: Some(ls.actor_name_max_length),
-        rate_limit_message: Some(lsrl.message),
-        rate_limit_message_per_second: Some(lsrl.message_per_second),
-        rate_limit_comment: Some(lsrl.comment),
-        rate_limit_comment_per_second: Some(lsrl.comment_per_second),
-        rate_limit_image: Some(lsrl.image),
-        rate_limit_image_per_second: Some(lsrl.image_per_second),
-        rate_limit_post: Some(lsrl.post),
-        rate_limit_post_per_second: Some(lsrl.post_per_second),
-        rate_limit_register: Some(lsrl.register),
-        rate_limit_register_per_second: Some(lsrl.register_per_second),
-        rate_limit_search: Some(lsrl.search),
-        rate_limit_search_per_second: Some(lsrl.search_per_second),
-        federation_enabled: Some(ls.federation_enabled),
-        federation_debug: Some(ls.federation_debug),
-        federation_worker_count: Some(ls.federation_worker_count),
-        captcha_enabled: Some(ls.captcha_enabled),
-        captcha_difficulty: Some(ls.captcha_difficulty),
-        allowed_instances: this.props.siteRes.federated_instances.andThen(
-          f => f.allowed
-        ),
-        blocked_instances: this.props.siteRes.federated_instances.andThen(
-          f => f.blocked
-        ),
-        taglines: this.props.siteRes.taglines.map(x => x.map(y => y.content)),
-        auth: undefined,
-      }),
+        actor_name_max_length: ls.actor_name_max_length,
+        rate_limit_message: lsrl.message,
+        rate_limit_message_per_second: lsrl.message_per_second,
+        rate_limit_comment: lsrl.comment,
+        rate_limit_comment_per_second: lsrl.comment_per_second,
+        rate_limit_image: lsrl.image,
+        rate_limit_image_per_second: lsrl.image_per_second,
+        rate_limit_post: lsrl.post,
+        rate_limit_post_per_second: lsrl.post_per_second,
+        rate_limit_register: lsrl.register,
+        rate_limit_register_per_second: lsrl.register_per_second,
+        rate_limit_search: lsrl.search,
+        rate_limit_search_per_second: lsrl.search_per_second,
+        federation_enabled: ls.federation_enabled,
+        federation_debug: ls.federation_debug,
+        federation_worker_count: ls.federation_worker_count,
+        captcha_enabled: ls.captcha_enabled,
+        captcha_difficulty: ls.captcha_difficulty,
+        allowed_instances: this.props.siteRes.federated_instances?.allowed,
+        blocked_instances: this.props.siteRes.federated_instances?.blocked,
+        taglines: this.props.siteRes.taglines?.map(x => x.content),
+        auth: "TODO",
+      },
     };
   }
 
   async componentDidMount() {
-    this.setState({ themeList: Some(await fetchThemeList()) });
+    this.setState({ themeList: await fetchThemeList() });
   }
 
   // Necessary to stop the loading
@@ -180,7 +131,7 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
     ) {
       window.onbeforeunload = () => true;
     } else {
-      window.onbeforeunload = undefined;
+      window.onbeforeunload = null;
     }
   }
 
@@ -218,7 +169,7 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
                 type="text"
                 id="create-site-name"
                 className="form-control"
-                value={toUndefined(this.state.siteForm.name)}
+                value={this.state.siteForm.name}
                 onInput={linkEvent(this, this.handleSiteNameChange)}
                 required
                 minLength={3}
@@ -254,7 +205,7 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
                 type="text"
                 className="form-control"
                 id="site-desc"
-                value={toUndefined(this.state.siteForm.description)}
+                value={this.state.siteForm.description}
                 onInput={linkEvent(this, this.handleSiteDescChange)}
                 maxLength={150}
               />
@@ -265,10 +216,6 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
             <div className="col-12">
               <MarkdownTextArea
                 initialContent={this.state.siteForm.sidebar}
-                initialLanguageId={None}
-                placeholder={None}
-                buttonTitle={None}
-                maxLength={None}
                 onContentChange={this.handleSiteSidebarChange}
                 hideNavigationWarnings
                 allLanguages={[]}
@@ -283,10 +230,6 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
             <div className="col-12">
               <MarkdownTextArea
                 initialContent={this.state.siteForm.legal_information}
-                initialLanguageId={None}
-                placeholder={None}
-                buttonTitle={None}
-                maxLength={None}
                 onContentChange={this.handleSiteLegalInfoChange}
                 hideNavigationWarnings
                 allLanguages={[]}
@@ -294,26 +237,6 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
               />
             </div>
           </div>
-          {this.state.siteForm.require_application.unwrapOr(false) && (
-            <div className="form-group row">
-              <label className="col-12 col-form-label">
-                {i18n.t("application_questionnaire")}
-              </label>
-              <div className="col-12">
-                <MarkdownTextArea
-                  initialContent={this.state.siteForm.application_question}
-                  initialLanguageId={None}
-                  placeholder={None}
-                  buttonTitle={None}
-                  maxLength={None}
-                  onContentChange={this.handleSiteApplicationQuestionChange}
-                  hideNavigationWarnings
-                  allLanguages={[]}
-                  siteLanguages={[]}
-                />
-              </div>
-            </div>
-          )}
           <div className="form-group row">
             <div className="col-12">
               <div className="form-check">
@@ -321,7 +244,7 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
                   className="form-check-input"
                   id="create-site-downvotes"
                   type="checkbox"
-                  checked={toUndefined(this.state.siteForm.enable_downvotes)}
+                  checked={this.state.siteForm.enable_downvotes}
                   onChange={linkEvent(
                     this,
                     this.handleSiteEnableDownvotesChange
@@ -343,7 +266,7 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
                   className="form-check-input"
                   id="create-site-enable-nsfw"
                   type="checkbox"
-                  checked={toUndefined(this.state.siteForm.enable_nsfw)}
+                  checked={this.state.siteForm.enable_nsfw}
                   onChange={linkEvent(this, this.handleSiteEnableNsfwChange)}
                 />
                 <label
@@ -357,26 +280,50 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
           </div>
           <div className="form-group row">
             <div className="col-12">
-              <div className="form-check">
-                <input
-                  className="form-check-input"
-                  id="create-site-open-registration"
-                  type="checkbox"
-                  checked={toUndefined(this.state.siteForm.open_registration)}
-                  onChange={linkEvent(
-                    this,
-                    this.handleSiteOpenRegistrationChange
-                  )}
-                />
-                <label
-                  className="form-check-label"
-                  htmlFor="create-site-open-registration"
-                >
+              <label
+                className="form-check-label mr-2"
+                htmlFor="create-site-registration-mode"
+              >
+                {i18n.t("registration_mode")}
+              </label>
+              <select
+                id="create-site-registration-mode"
+                value={this.state.siteForm.registration_mode}
+                onChange={linkEvent(
+                  this,
+                  this.handleSiteRegistrationModeChange
+                )}
+                className="custom-select w-auto"
+              >
+                <option value={RegistrationMode.RequireApplication}>
+                  {i18n.t("require_registration_application")}
+                </option>
+                <option value={RegistrationMode.Open}>
                   {i18n.t("open_registration")}
-                </label>
-              </div>
+                </option>
+                <option value={RegistrationMode.Closed}>
+                  {i18n.t("close_registration")}
+                </option>
+              </select>
             </div>
           </div>
+          {this.state.siteForm.registration_mode ==
+            RegistrationMode.RequireApplication && (
+            <div className="form-group row">
+              <label className="col-12 col-form-label">
+                {i18n.t("application_questionnaire")}
+              </label>
+              <div className="col-12">
+                <MarkdownTextArea
+                  initialContent={this.state.siteForm.application_question}
+                  onContentChange={this.handleSiteApplicationQuestionChange}
+                  hideNavigationWarnings
+                  allLanguages={[]}
+                  siteLanguages={[]}
+                />
+              </div>
+            </div>
+          )}
           <div className="form-group row">
             <div className="col-12">
               <div className="form-check">
@@ -384,9 +331,7 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
                   className="form-check-input"
                   id="create-site-community-creation-admin-only"
                   type="checkbox"
-                  checked={toUndefined(
-                    this.state.siteForm.community_creation_admin_only
-                  )}
+                  checked={this.state.siteForm.community_creation_admin_only}
                   onChange={linkEvent(
                     this,
                     this.handleSiteCommunityCreationAdminOnly
@@ -408,9 +353,7 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
                   className="form-check-input"
                   id="create-site-require-email-verification"
                   type="checkbox"
-                  checked={toUndefined(
-                    this.state.siteForm.require_email_verification
-                  )}
+                  checked={this.state.siteForm.require_email_verification}
                   onChange={linkEvent(
                     this,
                     this.handleSiteRequireEmailVerification
@@ -425,25 +368,6 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
               </div>
             </div>
           </div>
-          <div className="form-group row">
-            <div className="col-12">
-              <div className="form-check">
-                <input
-                  className="form-check-input"
-                  id="create-site-require-application"
-                  type="checkbox"
-                  checked={toUndefined(this.state.siteForm.require_application)}
-                  onChange={linkEvent(this, this.handleSiteRequireApplication)}
-                />
-                <label
-                  className="form-check-label"
-                  htmlFor="create-site-require-application"
-                >
-                  {i18n.t("require_registration_application")}
-                </label>
-              </div>
-            </div>
-          </div>
           <div className="form-group row">
             <div className="col-12">
               <div className="form-check">
@@ -451,9 +375,7 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
                   className="form-check-input"
                   id="create-site-application-email-admins"
                   type="checkbox"
-                  checked={toUndefined(
-                    this.state.siteForm.application_email_admins
-                  )}
+                  checked={this.state.siteForm.application_email_admins}
                   onChange={linkEvent(
                     this,
                     this.handleSiteApplicationEmailAdmins
@@ -478,12 +400,12 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
               </label>
               <select
                 id="create-site-default-theme"
-                value={toUndefined(this.state.siteForm.default_theme)}
+                value={this.state.siteForm.default_theme}
                 onChange={linkEvent(this, this.handleSiteDefaultTheme)}
                 className="custom-select w-auto"
               >
                 <option value="browser">{i18n.t("browser_default")}</option>
-                {this.state.themeList.unwrapOr([]).map(theme => (
+                {this.state.themeList?.map(theme => (
                   <option key={theme} value={theme}>
                     {theme}
                   </option>
@@ -498,9 +420,7 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
                 <ListingTypeSelect
                   type_={
                     ListingType[
-                      this.state.siteForm.default_post_listing_type.unwrapOr(
-                        "Local"
-                      )
+                      this.state.siteForm.default_post_listing_type ?? "Local"
                     ]
                   }
                   showLocal
@@ -517,7 +437,7 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
                   className="form-check-input"
                   id="create-site-private-instance"
                   type="checkbox"
-                  checked={toUndefined(this.state.siteForm.private_instance)}
+                  checked={this.state.siteForm.private_instance}
                   onChange={linkEvent(this, this.handleSitePrivateInstance)}
                 />
                 <label
@@ -536,9 +456,7 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
                   className="form-check-input"
                   id="create-site-hide-modlog-mod-names"
                   type="checkbox"
-                  checked={toUndefined(
-                    this.state.siteForm.hide_modlog_mod_names
-                  )}
+                  checked={this.state.siteForm.hide_modlog_mod_names}
                   onChange={linkEvent(this, this.handleSiteHideModlogModNames)}
                 />
                 <label
@@ -563,7 +481,7 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
                 id="create-site-slur-filter-regex"
                 placeholder="(word1|word2)"
                 className="form-control"
-                value={toUndefined(this.state.siteForm.slur_filter_regex)}
+                value={this.state.siteForm.slur_filter_regex}
                 onInput={linkEvent(this, this.handleSiteSlurFilterRegex)}
                 minLength={3}
               />
@@ -590,7 +508,7 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
                 id="create-site-actor-name"
                 className="form-control"
                 min={5}
-                value={toUndefined(this.state.siteForm.actor_name_max_length)}
+                value={this.state.siteForm.actor_name_max_length}
                 onInput={linkEvent(this, this.handleSiteActorNameMaxLength)}
               />
             </div>
@@ -602,7 +520,7 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
                   className="form-check-input"
                   id="create-site-federation-enabled"
                   type="checkbox"
-                  checked={toUndefined(this.state.siteForm.federation_enabled)}
+                  checked={this.state.siteForm.federation_enabled}
                   onChange={linkEvent(this, this.handleSiteFederationEnabled)}
                 />
                 <label
@@ -614,7 +532,7 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
               </div>
             </div>
           </div>
-          {this.state.siteForm.federation_enabled.unwrapOr(false) && (
+          {this.state.siteForm.federation_enabled && (
             <>
               <div className="form-group row">
                 <label
@@ -663,9 +581,7 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
                       className="form-check-input"
                       id="create-site-federation-debug"
                       type="checkbox"
-                      checked={toUndefined(
-                        this.state.siteForm.federation_debug
-                      )}
+                      checked={this.state.siteForm.federation_debug}
                       onChange={linkEvent(this, this.handleSiteFederationDebug)}
                     />
                     <label
@@ -690,9 +606,7 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
                     id="create-site-federation-worker-count"
                     className="form-control"
                     min={0}
-                    value={toUndefined(
-                      this.state.siteForm.federation_worker_count
-                    )}
+                    value={this.state.siteForm.federation_worker_count}
                     onInput={linkEvent(
                       this,
                       this.handleSiteFederationWorkerCount
@@ -709,7 +623,7 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
                   className="form-check-input"
                   id="create-site-captcha-enabled"
                   type="checkbox"
-                  checked={toUndefined(this.state.siteForm.captcha_enabled)}
+                  checked={this.state.siteForm.captcha_enabled}
                   onChange={linkEvent(this, this.handleSiteCaptchaEnabled)}
                 />
                 <label
@@ -721,7 +635,7 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
               </div>
             </div>
           </div>
-          {this.state.siteForm.captcha_enabled.unwrapOr(false) && (
+          {this.state.siteForm.captcha_enabled && (
             <div className="form-group row">
               <div className="col-12">
                 <label
@@ -732,7 +646,7 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
                 </label>
                 <select
                   id="create-site-captcha-difficulty"
-                  value={toUndefined(this.state.siteForm.captcha_difficulty)}
+                  value={this.state.siteForm.captcha_difficulty}
                   onChange={linkEvent(this, this.handleSiteCaptchaDifficulty)}
                   className="custom-select w-auto"
                 >
@@ -756,7 +670,7 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
                 id="create-site-rate-limit-message"
                 className="form-control"
                 min={0}
-                value={toUndefined(this.state.siteForm.rate_limit_message)}
+                value={this.state.siteForm.rate_limit_message}
                 onInput={linkEvent(this, this.handleSiteRateLimitMessage)}
               />
             </div>
@@ -774,9 +688,7 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
                 id="create-site-rate-limit-message-per-second"
                 className="form-control"
                 min={0}
-                value={toUndefined(
-                  this.state.siteForm.rate_limit_message_per_second
-                )}
+                value={this.state.siteForm.rate_limit_message_per_second}
                 onInput={linkEvent(
                   this,
                   this.handleSiteRateLimitMessagePerSecond
@@ -797,7 +709,7 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
                 id="create-site-rate-limit-post"
                 className="form-control"
                 min={0}
-                value={toUndefined(this.state.siteForm.rate_limit_post)}
+                value={this.state.siteForm.rate_limit_post}
                 onInput={linkEvent(this, this.handleSiteRateLimitPost)}
               />
             </div>
@@ -815,9 +727,7 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
                 id="create-site-rate-limit-post-per-second"
                 className="form-control"
                 min={0}
-                value={toUndefined(
-                  this.state.siteForm.rate_limit_post_per_second
-                )}
+                value={this.state.siteForm.rate_limit_post_per_second}
                 onInput={linkEvent(this, this.handleSiteRateLimitPostPerSecond)}
               />
             </div>
@@ -835,7 +745,7 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
                 id="create-site-rate-limit-register"
                 className="form-control"
                 min={0}
-                value={toUndefined(this.state.siteForm.rate_limit_register)}
+                value={this.state.siteForm.rate_limit_register}
                 onInput={linkEvent(this, this.handleSiteRateLimitRegister)}
               />
             </div>
@@ -853,9 +763,7 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
                 id="create-site-rate-limit-register-per-second"
                 className="form-control"
                 min={0}
-                value={toUndefined(
-                  this.state.siteForm.rate_limit_register_per_second
-                )}
+                value={this.state.siteForm.rate_limit_register_per_second}
                 onInput={linkEvent(
                   this,
                   this.handleSiteRateLimitRegisterPerSecond
@@ -876,7 +784,7 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
                 id="create-site-rate-limit-image"
                 className="form-control"
                 min={0}
-                value={toUndefined(this.state.siteForm.rate_limit_image)}
+                value={this.state.siteForm.rate_limit_image}
                 onInput={linkEvent(this, this.handleSiteRateLimitImage)}
               />
             </div>
@@ -894,9 +802,7 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
                 id="create-site-rate-limit-image-per-second"
                 className="form-control"
                 min={0}
-                value={toUndefined(
-                  this.state.siteForm.rate_limit_image_per_second
-                )}
+                value={this.state.siteForm.rate_limit_image_per_second}
                 onInput={linkEvent(
                   this,
                   this.handleSiteRateLimitImagePerSecond
@@ -917,7 +823,7 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
                 id="create-site-rate-limit-comment"
                 className="form-control"
                 min={0}
-                value={toUndefined(this.state.siteForm.rate_limit_comment)}
+                value={this.state.siteForm.rate_limit_comment}
                 onInput={linkEvent(this, this.handleSiteRateLimitComment)}
               />
             </div>
@@ -935,9 +841,7 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
                 id="create-site-rate-limit-comment-per-second"
                 className="form-control"
                 min={0}
-                value={toUndefined(
-                  this.state.siteForm.rate_limit_comment_per_second
-                )}
+                value={this.state.siteForm.rate_limit_comment_per_second}
                 onInput={linkEvent(
                   this,
                   this.handleSiteRateLimitCommentPerSecond
@@ -958,7 +862,7 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
                 id="create-site-rate-limit-search"
                 className="form-control"
                 min={0}
-                value={toUndefined(this.state.siteForm.rate_limit_search)}
+                value={this.state.siteForm.rate_limit_search}
                 onInput={linkEvent(this, this.handleSiteRateLimitSearch)}
               />
             </div>
@@ -976,9 +880,7 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
                 id="create-site-rate-limit-search-per-second"
                 className="form-control"
                 min={0}
-                value={toUndefined(
-                  this.state.siteForm.rate_limit_search_per_second
-                )}
+                value={this.state.siteForm.rate_limit_search_per_second}
                 onInput={linkEvent(
                   this,
                   this.handleSiteRateLimitSearchPerSecond
@@ -992,44 +894,38 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
               <table id="taglines_table" className="table table-sm table-hover">
                 <thead className="pointer"></thead>
                 <tbody>
-                  {this.state.siteForm.taglines
-                    .unwrapOr([])
-                    .map((cv, index) => (
-                      <tr key={index}>
-                        <td>
-                          <MarkdownTextArea
-                            initialContent={Some(cv)}
-                            initialLanguageId={None}
-                            placeholder={None}
-                            buttonTitle={None}
-                            maxLength={None}
-                            onContentChange={s =>
-                              this.handleTaglineChange(this, index, s)
-                            }
-                            hideNavigationWarnings
-                            allLanguages={this.props.siteRes.all_languages}
-                            siteLanguages={
-                              this.props.siteRes.discussion_languages
-                            }
+                  {this.state.siteForm.taglines?.map((cv, index) => (
+                    <tr key={index}>
+                      <td>
+                        <MarkdownTextArea
+                          initialContent={cv}
+                          onContentChange={s =>
+                            this.handleTaglineChange(this, index, s)
+                          }
+                          hideNavigationWarnings
+                          allLanguages={this.props.siteRes.all_languages}
+                          siteLanguages={
+                            this.props.siteRes.discussion_languages
+                          }
+                        />
+                      </td>
+                      <td className="text-right">
+                        <button
+                          className="btn btn-link btn-animate text-muted"
+                          onClick={e =>
+                            this.handleDeleteTaglineClick(this, index, e)
+                          }
+                          data-tippy-content={i18n.t("delete")}
+                          aria-label={i18n.t("delete")}
+                        >
+                          <Icon
+                            icon="trash"
+                            classes={`icon-inline text-danger`}
                           />
-                        </td>
-                        <td className="text-right">
-                          <button
-                            className="btn btn-link btn-animate text-muted"
-                            onClick={e =>
-                              this.handleDeleteTaglineClick(this, index, e)
-                            }
-                            data-tippy-content={i18n.t("delete")}
-                            aria-label={i18n.t("delete")}
-                          >
-                            <Icon
-                              icon="trash"
-                              classes={`icon-inline text-danger`}
-                            />
-                          </button>
-                        </td>
-                      </tr>
-                    ))}
+                        </button>
+                      </td>
+                    </tr>
+                  ))}
                 </tbody>
               </table>
               <button
@@ -1065,13 +961,14 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
   handleCreateSiteSubmit(i: SiteForm, event: any) {
     event.preventDefault();
     i.setState({ loading: true });
-    i.setState(s => ((s.siteForm.auth = auth().unwrap()), s));
+    let auth = myAuth() ?? "TODO";
+    i.setState(s => ((s.siteForm.auth = auth), s));
     if (i.props.siteRes.site_view.local_site.site_setup) {
       WebSocketService.Instance.send(wsClient.editSite(i.state.siteForm));
     } else {
       let sForm = i.state.siteForm;
-      let form = new CreateSite({
-        name: sForm.name.unwrapOr("My site"),
+      let form: CreateSite = {
+        name: sForm.name ?? "My site",
         sidebar: sForm.sidebar,
         description: sForm.description,
         icon: sForm.icon,
@@ -1079,15 +976,13 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
         community_creation_admin_only: sForm.community_creation_admin_only,
         enable_nsfw: sForm.enable_nsfw,
         enable_downvotes: sForm.enable_downvotes,
-        require_application: sForm.require_application,
         application_question: sForm.application_question,
-        open_registration: sForm.open_registration,
+        registration_mode: sForm.registration_mode,
         require_email_verification: sForm.require_email_verification,
         private_instance: sForm.private_instance,
         default_theme: sForm.default_theme,
         default_post_listing_type: sForm.default_post_listing_type,
         application_email_admins: sForm.application_email_admins,
-        auth: auth().unwrap(),
         hide_modlog_mod_names: sForm.hide_modlog_mod_names,
         legal_information: sForm.legal_information,
         slur_filter_regex: sForm.slur_filter_regex,
@@ -1113,14 +1008,15 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
         blocked_instances: sForm.blocked_instances,
         discussion_languages: sForm.discussion_languages,
         taglines: sForm.taglines,
-      });
+        auth,
+      };
       WebSocketService.Instance.send(wsClient.createSite(form));
     }
     i.setState(i.state);
   }
 
-  instancesToString(opt: Option<string[]>): string {
-    return opt.map(list => list.join(",")).unwrapOr("");
+  instancesToString(opt?: string[]): string {
+    return opt ? opt.join(",") : "";
   }
 
   handleSiteAllowedInstances(i: SiteForm, event: any) {
@@ -1134,26 +1030,24 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
   }
 
   handleSiteNameChange(i: SiteForm, event: any) {
-    i.state.siteForm.name = Some(event.target.value);
+    i.state.siteForm.name = event.target.value;
     i.setState(i.state);
   }
 
   handleSiteSidebarChange(val: string) {
-    this.setState(s => ((s.siteForm.sidebar = Some(val)), s));
+    this.setState(s => ((s.siteForm.sidebar = val), s));
   }
 
   handleSiteLegalInfoChange(val: string) {
-    this.setState(s => ((s.siteForm.legal_information = Some(val)), s));
+    this.setState(s => ((s.siteForm.legal_information = val), s));
   }
 
   handleTaglineChange(i: SiteForm, index: number, val: string) {
-    i.state.siteForm.taglines.match({
-      some: tls => {
-        tls[index] = val;
-      },
-      none: void 0,
-    });
-    i.setState(i.state);
+    let taglines = i.state.siteForm.taglines;
+    if (taglines) {
+      taglines[index] = val;
+      i.setState(i.state);
+    }
   }
 
   handleDeleteTaglineClick(
@@ -1162,12 +1056,12 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
     event: InfernoMouseEvent<HTMLButtonElement>
   ) {
     event.preventDefault();
-    if (i.state.siteForm.taglines.isSome()) {
-      let taglines = i.state.siteForm.taglines.unwrap();
+    let taglines = i.state.siteForm.taglines;
+    if (taglines) {
       taglines.splice(index, 1);
-      i.state.siteForm.taglines = None; // force rerender of table rows
+      i.state.siteForm.taglines = undefined;
       i.setState(i.state);
-      i.state.siteForm.taglines = Some(taglines);
+      i.state.siteForm.taglines = taglines;
       i.setState(i.state);
     }
   }
@@ -1177,116 +1071,103 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
     event: InfernoMouseEvent<HTMLButtonElement>
   ) {
     event.preventDefault();
-    if (i.state.siteForm.taglines.isNone()) {
-      i.state.siteForm.taglines = Some([]);
+    if (!i.state.siteForm.taglines) {
+      i.state.siteForm.taglines = [];
     }
-    i.state.siteForm.taglines.unwrap().push("");
+    i.state.siteForm.taglines.push("");
     i.setState(i.state);
   }
 
   handleSiteApplicationQuestionChange(val: string) {
-    this.setState(s => ((s.siteForm.application_question = Some(val)), s));
+    this.setState(s => ((s.siteForm.application_question = val), s));
   }
 
   handleSiteDescChange(i: SiteForm, event: any) {
-    i.state.siteForm.description = Some(event.target.value);
+    i.state.siteForm.description = event.target.value;
     i.setState(i.state);
   }
 
   handleSiteEnableNsfwChange(i: SiteForm, event: any) {
-    i.state.siteForm.enable_nsfw = Some(event.target.checked);
+    i.state.siteForm.enable_nsfw = event.target.checked;
     i.setState(i.state);
   }
 
-  handleSiteOpenRegistrationChange(i: SiteForm, event: any) {
-    i.state.siteForm.open_registration = Some(event.target.checked);
+  handleSiteRegistrationModeChange(i: SiteForm, event: any) {
+    i.state.siteForm.registration_mode = event.target.value;
     i.setState(i.state);
   }
 
   handleSiteCommunityCreationAdminOnly(i: SiteForm, event: any) {
-    i.state.siteForm.community_creation_admin_only = Some(event.target.checked);
+    i.state.siteForm.community_creation_admin_only = event.target.checked;
     i.setState(i.state);
   }
 
   handleSiteEnableDownvotesChange(i: SiteForm, event: any) {
-    i.state.siteForm.enable_downvotes = Some(event.target.checked);
-    i.setState(i.state);
-  }
-
-  handleSiteRequireApplication(i: SiteForm, event: any) {
-    i.state.siteForm.require_application = Some(event.target.checked);
+    i.state.siteForm.enable_downvotes = event.target.checked;
     i.setState(i.state);
   }
 
   handleSiteRequireEmailVerification(i: SiteForm, event: any) {
-    i.state.siteForm.require_email_verification = Some(event.target.checked);
+    i.state.siteForm.require_email_verification = event.target.checked;
     i.setState(i.state);
   }
 
   handleSiteApplicationEmailAdmins(i: SiteForm, event: any) {
-    i.state.siteForm.application_email_admins = Some(event.target.checked);
+    i.state.siteForm.application_email_admins = event.target.checked;
     i.setState(i.state);
   }
 
   handleSitePrivateInstance(i: SiteForm, event: any) {
-    i.state.siteForm.private_instance = Some(event.target.checked);
+    i.state.siteForm.private_instance = event.target.checked;
     i.setState(i.state);
   }
 
   handleSiteHideModlogModNames(i: SiteForm, event: any) {
-    i.state.siteForm.hide_modlog_mod_names = Some(event.target.checked);
+    i.state.siteForm.hide_modlog_mod_names = event.target.checked;
     i.setState(i.state);
   }
 
   handleSiteDefaultTheme(i: SiteForm, event: any) {
-    i.state.siteForm.default_theme = Some(event.target.value);
+    i.state.siteForm.default_theme = event.target.value;
     i.setState(i.state);
   }
 
   handleIconUpload(url: string) {
-    this.setState(s => ((s.siteForm.icon = Some(url)), s));
+    this.setState(s => ((s.siteForm.icon = url), s));
   }
 
   handleIconRemove() {
-    this.setState(s => ((s.siteForm.icon = Some("")), s));
+    this.setState(s => ((s.siteForm.icon = ""), s));
   }
 
   handleBannerUpload(url: string) {
-    this.setState(s => ((s.siteForm.banner = Some(url)), s));
+    this.setState(s => ((s.siteForm.banner = url), s));
   }
 
   handleBannerRemove() {
-    this.setState(s => ((s.siteForm.banner = Some("")), s));
+    this.setState(s => ((s.siteForm.banner = ""), s));
   }
 
   handleSiteSlurFilterRegex(i: SiteForm, event: any) {
-    i.setState(
-      s => ((s.siteForm.slur_filter_regex = Some(event.target.value)), s)
-    );
+    i.setState(s => ((s.siteForm.slur_filter_regex = event.target.value), s));
   }
 
   handleSiteActorNameMaxLength(i: SiteForm, event: any) {
     i.setState(
-      s => (
-        (s.siteForm.actor_name_max_length = Some(Number(event.target.value))), s
-      )
+      s => ((s.siteForm.actor_name_max_length = Number(event.target.value)), s)
     );
   }
 
   handleSiteRateLimitMessage(i: SiteForm, event: any) {
     i.setState(
-      s => (
-        (s.siteForm.rate_limit_message = Some(Number(event.target.value))), s
-      )
+      s => ((s.siteForm.rate_limit_message = Number(event.target.value)), s)
     );
   }
 
   handleSiteRateLimitMessagePerSecond(i: SiteForm, event: any) {
     i.setState(
       s => (
-        (s.siteForm.rate_limit_message_per_second = Some(
-          Number(event.target.value)
-        )),
+        (s.siteForm.rate_limit_message_per_second = Number(event.target.value)),
         s
       )
     );
@@ -1294,52 +1175,42 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
 
   handleSiteRateLimitPost(i: SiteForm, event: any) {
     i.setState(
-      s => ((s.siteForm.rate_limit_post = Some(Number(event.target.value))), s)
+      s => ((s.siteForm.rate_limit_post = Number(event.target.value)), s)
     );
   }
 
   handleSiteRateLimitPostPerSecond(i: SiteForm, event: any) {
     i.setState(
       s => (
-        (s.siteForm.rate_limit_post_per_second = Some(
-          Number(event.target.value)
-        )),
-        s
+        (s.siteForm.rate_limit_post_per_second = Number(event.target.value)), s
       )
     );
   }
 
   handleSiteRateLimitImage(i: SiteForm, event: any) {
     i.setState(
-      s => ((s.siteForm.rate_limit_image = Some(Number(event.target.value))), s)
+      s => ((s.siteForm.rate_limit_image = Number(event.target.value)), s)
     );
   }
 
   handleSiteRateLimitImagePerSecond(i: SiteForm, event: any) {
     i.setState(
       s => (
-        (s.siteForm.rate_limit_image_per_second = Some(
-          Number(event.target.value)
-        )),
-        s
+        (s.siteForm.rate_limit_image_per_second = Number(event.target.value)), s
       )
     );
   }
 
   handleSiteRateLimitComment(i: SiteForm, event: any) {
     i.setState(
-      s => (
-        (s.siteForm.rate_limit_comment = Some(Number(event.target.value))), s
-      )
+      s => ((s.siteForm.rate_limit_comment = Number(event.target.value)), s)
     );
   }
 
   handleSiteRateLimitCommentPerSecond(i: SiteForm, event: any) {
     i.setState(
       s => (
-        (s.siteForm.rate_limit_comment_per_second = Some(
-          Number(event.target.value)
-        )),
+        (s.siteForm.rate_limit_comment_per_second = Number(event.target.value)),
         s
       )
     );
@@ -1347,18 +1218,14 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
 
   handleSiteRateLimitSearch(i: SiteForm, event: any) {
     i.setState(
-      s => (
-        (s.siteForm.rate_limit_search = Some(Number(event.target.value))), s
-      )
+      s => ((s.siteForm.rate_limit_search = Number(event.target.value)), s)
     );
   }
 
   handleSiteRateLimitSearchPerSecond(i: SiteForm, event: any) {
     i.setState(
       s => (
-        (s.siteForm.rate_limit_search_per_second = Some(
-          Number(event.target.value)
-        )),
+        (s.siteForm.rate_limit_search_per_second = Number(event.target.value)),
         s
       )
     );
@@ -1366,17 +1233,15 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
 
   handleSiteRateLimitRegister(i: SiteForm, event: any) {
     i.setState(
-      s => (
-        (s.siteForm.rate_limit_register = Some(Number(event.target.value))), s
-      )
+      s => ((s.siteForm.rate_limit_register = Number(event.target.value)), s)
     );
   }
 
   handleSiteRateLimitRegisterPerSecond(i: SiteForm, event: any) {
     i.setState(
       s => (
-        (s.siteForm.rate_limit_register_per_second = Some(
-          Number(event.target.value)
+        (s.siteForm.rate_limit_register_per_second = Number(
+          event.target.value
         )),
         s
       )
@@ -1384,56 +1249,51 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
   }
 
   handleSiteFederationEnabled(i: SiteForm, event: any) {
-    i.state.siteForm.federation_enabled = Some(event.target.checked);
+    i.state.siteForm.federation_enabled = event.target.checked;
     i.setState(i.state);
   }
 
   handleSiteFederationDebug(i: SiteForm, event: any) {
-    i.state.siteForm.federation_debug = Some(event.target.checked);
+    i.state.siteForm.federation_debug = event.target.checked;
     i.setState(i.state);
   }
 
   handleSiteFederationWorkerCount(i: SiteForm, event: any) {
     i.setState(
       s => (
-        (s.siteForm.federation_worker_count = Some(Number(event.target.value))),
-        s
+        (s.siteForm.federation_worker_count = Number(event.target.value)), s
       )
     );
   }
 
   handleSiteCaptchaEnabled(i: SiteForm, event: any) {
-    i.state.siteForm.captcha_enabled = Some(event.target.checked);
+    i.state.siteForm.captcha_enabled = event.target.checked;
     i.setState(i.state);
   }
 
   handleSiteCaptchaDifficulty(i: SiteForm, event: any) {
-    i.setState(
-      s => ((s.siteForm.captcha_difficulty = Some(event.target.value)), s)
-    );
+    i.setState(s => ((s.siteForm.captcha_difficulty = event.target.value), s));
   }
 
   handleDiscussionLanguageChange(val: number[]) {
-    this.setState(s => ((s.siteForm.discussion_languages = Some(val)), s));
+    this.setState(s => ((s.siteForm.discussion_languages = val), s));
   }
 
   handleDefaultPostListingTypeChange(val: ListingType) {
     this.setState(
       s => (
-        (s.siteForm.default_post_listing_type = Some(
-          ListingType[ListingType[val]]
-        )),
+        (s.siteForm.default_post_listing_type = ListingType[ListingType[val]]),
         s
       )
     );
   }
 }
 
-function splitToList(commaList: string): Option<string[]> {
+function splitToList(commaList: string): string[] {
   if (commaList !== "") {
     let list = commaList.trim().split(",");
-    return Some(list);
+    return list;
   } else {
-    return Some([]);
+    return [];
   }
 }
index e321ae9f6b9b1218a6846d6e762fdf39302cd5d9..fbe3e2a283ee9d3e820ac66553e473c56c40d0a2 100644 (file)
@@ -1,4 +1,3 @@
-import { None, Option } from "@sniptt/monads";
 import { Component, linkEvent } from "inferno";
 import { Link } from "inferno-router";
 import { PersonViewSafe, Site, SiteAggregates } from "lemmy-js-client";
@@ -11,9 +10,9 @@ import { PersonListing } from "../person/person-listing";
 interface SiteSidebarProps {
   site: Site;
   showLocal: boolean;
-  counts: Option<SiteAggregates>;
-  admins: Option<PersonViewSafe[]>;
-  online: Option<number>;
+  counts?: SiteAggregates;
+  admins?: PersonViewSafe[];
+  online?: number;
 }
 
 interface SiteSidebarState {
@@ -21,13 +20,12 @@ interface SiteSidebarState {
 }
 
 export class SiteSidebar extends Component<SiteSidebarProps, SiteSidebarState> {
-  private emptyState: SiteSidebarState = {
+  state: SiteSidebarState = {
     collapsed: false,
   };
 
   constructor(props: any, context: any) {
     super(props, context);
-    this.state = this.emptyState;
   }
 
   render() {
@@ -38,7 +36,7 @@ export class SiteSidebar extends Component<SiteSidebarProps, SiteSidebarState> {
             <div className="mb-2">{this.siteName()}</div>
             {!this.state.collapsed && (
               <>
-                <BannerIconHeader banner={this.props.site.banner} icon={None} />
+                <BannerIconHeader banner={this.props.site.banner} />
                 {this.siteInfo()}
               </>
             )}
@@ -72,22 +70,10 @@ export class SiteSidebar extends Component<SiteSidebarProps, SiteSidebarState> {
     let site = this.props.site;
     return (
       <div>
-        {site.description.match({
-          some: description => <h6>{description}</h6>,
-          none: <></>,
-        })}
-        {site.sidebar.match({
-          some: sidebar => this.siteSidebar(sidebar),
-          none: <></>,
-        })}
-        {this.props.counts.match({
-          some: counts => this.badges(counts),
-          none: <></>,
-        })}
-        {this.props.admins.match({
-          some: admins => this.admins(admins),
-          none: <></>,
-        })}
+        {site.description && <h6>{site.description}</h6>}
+        {site.sidebar && this.siteSidebar(site.sidebar)}
+        {this.props.counts && this.badges(this.props.counts)}
+        {this.props.admins && this.admins(this.props.admins)}
       </div>
     );
   }
@@ -113,7 +99,7 @@ export class SiteSidebar extends Component<SiteSidebarProps, SiteSidebarState> {
 
   badges(siteAggregates: SiteAggregates) {
     let counts = siteAggregates;
-    let online = this.props.online.unwrapOr(1);
+    let online = this.props.online ?? 1;
     return (
       <ul className="my-2 list-inline">
         <li className="list-inline-item badge badge-secondary">
index 95e0cc86358c827b4f1dfcde9721e3a35d91e980..140d56a8024c089e11a8c6c3366dbb23fd26c291 100644 (file)
@@ -1,4 +1,3 @@
-import { None, Option, Some } from "@sniptt/monads";
 import { Component, linkEvent } from "inferno";
 import { Link } from "inferno-router";
 import {
@@ -24,7 +23,6 @@ import {
   ModRemovePostView,
   ModTransferCommunityView,
   PersonSafe,
-  toUndefined,
   UserOperation,
   wsJsonToRes,
   wsUserOp,
@@ -37,12 +35,12 @@ import { WebSocketService } from "../services";
 import {
   amAdmin,
   amMod,
-  auth,
   choicesConfig,
   debounce,
   fetchLimit,
   fetchUsers,
   isBrowser,
+  myAuth,
   setIsoData,
   toast,
   wsClient,
@@ -57,7 +55,7 @@ import { PersonListing } from "./person/person-listing";
 type ModlogType = {
   id: number;
   type_: ModlogActionType;
-  moderator: Option<PersonSafe>;
+  moderator?: PersonSafe;
   view:
     | ModRemovePostView
     | ModLockPostView
@@ -81,43 +79,32 @@ if (isBrowser()) {
 }
 
 interface ModlogState {
-  res: Option<GetModlogResponse>;
-  communityId: Option<number>;
-  communityMods: Option<CommunityModeratorView[]>;
-  communityName: Option<string>;
+  res?: GetModlogResponse;
+  communityId?: number;
+  communityMods?: CommunityModeratorView[];
+  communityName?: string;
   page: number;
   siteRes: GetSiteResponse;
   loading: boolean;
   filter_action: ModlogActionType;
-  filter_user: Option<number>;
-  filter_mod: Option<number>;
+  filter_user?: number;
+  filter_mod?: number;
 }
 
 export class Modlog extends Component<any, ModlogState> {
-  private isoData = setIsoData(
-    this.context,
-    GetModlogResponse,
-    GetCommunityResponse
-  );
-  private subscription: Subscription;
+  private isoData = setIsoData(this.context);
+  private subscription?: Subscription;
   private userChoices: any;
   private modChoices: any;
-  private emptyState: ModlogState = {
-    res: None,
-    communityId: None,
-    communityMods: None,
-    communityName: None,
+  state: ModlogState = {
     page: 1,
     loading: true,
     siteRes: this.isoData.site_res,
     filter_action: ModlogActionType.All,
-    filter_user: None,
-    filter_mod: None,
   };
 
   constructor(props: any, context: any) {
     super(props, context);
-    this.state = this.emptyState;
     this.handlePageChange = this.handlePageChange.bind(this);
 
     this.parseMessage = this.parseMessage.bind(this);
@@ -126,27 +113,25 @@ export class Modlog extends Component<any, ModlogState> {
     this.state = {
       ...this.state,
       communityId: this.props.match.params.community_id
-        ? Some(Number(this.props.match.params.community_id))
-        : None,
+        ? Number(this.props.match.params.community_id)
+        : undefined,
     };
 
     // Only fetch the data if coming from another route
     if (this.isoData.path == this.context.router.route.match.url) {
       this.state = {
         ...this.state,
-        res: Some(this.isoData.routeData[0] as GetModlogResponse),
+        res: this.isoData.routeData[0] as GetModlogResponse,
       };
 
-      if (this.isoData.routeData[1]) {
-        // Getting the moderators
-        let communityRes = Some(
-          this.isoData.routeData[1] as GetCommunityResponse
-        );
-        this.state = {
-          ...this.state,
-          communityMods: communityRes.map(c => c.moderators),
-        };
-      }
+      let communityRes: GetCommunityResponse | undefined =
+        this.isoData.routeData[1];
+
+      // Getting the moderators
+      this.state = {
+        ...this.state,
+        communityMods: communityRes?.moderators,
+      };
 
       this.state = { ...this.state, loading: false };
     } else {
@@ -161,7 +146,7 @@ export class Modlog extends Component<any, ModlogState> {
 
   componentWillUnmount() {
     if (isBrowser()) {
-      this.subscription.unsubscribe();
+      this.subscription?.unsubscribe();
     }
   }
 
@@ -310,22 +295,20 @@ export class Modlog extends Component<any, ModlogState> {
     switch (i.type_) {
       case ModlogActionType.ModRemovePost: {
         let mrpv = i.view as ModRemovePostView;
+        let reason = mrpv.mod_remove_post.reason;
         return (
           <>
             <span>
-              {mrpv.mod_remove_post.removed.unwrapOr(false)
-                ? "Removed "
-                : "Restored "}
+              {mrpv.mod_remove_post.removed ? "Removed " : "Restored "}
             </span>
             <span>
               Post <Link to={`/post/${mrpv.post.id}`}>{mrpv.post.name}</Link>
             </span>
-            <span>
-              {mrpv.mod_remove_post.reason.match({
-                some: reason => <div>reason: {reason}</div>,
-                none: <></>,
-              })}
-            </span>
+            {reason && (
+              <span>
+                <div>reason: {reason}</div>
+              </span>
+            )}
           </>
         );
       }
@@ -333,11 +316,7 @@ export class Modlog extends Component<any, ModlogState> {
         let mlpv = i.view as ModLockPostView;
         return (
           <>
-            <span>
-              {mlpv.mod_lock_post.locked.unwrapOr(false)
-                ? "Locked "
-                : "Unlocked "}
-            </span>
+            <span>{mlpv.mod_lock_post.locked ? "Locked " : "Unlocked "}</span>
             <span>
               Post <Link to={`/post/${mlpv.post.id}`}>{mlpv.post.name}</Link>
             </span>
@@ -364,12 +343,11 @@ export class Modlog extends Component<any, ModlogState> {
       }
       case ModlogActionType.ModRemoveComment: {
         let mrc = i.view as ModRemoveCommentView;
+        let reason = mrc.mod_remove_comment.reason;
         return (
           <>
             <span>
-              {mrc.mod_remove_comment.removed.unwrapOr(false)
-                ? "Removed "
-                : "Restored "}
+              {mrc.mod_remove_comment.removed ? "Removed " : "Restored "}
             </span>
             <span>
               Comment{" "}
@@ -381,52 +359,47 @@ export class Modlog extends Component<any, ModlogState> {
               {" "}
               by <PersonListing person={mrc.commenter} />
             </span>
-            <span>
-              {mrc.mod_remove_comment.reason.match({
-                some: reason => <div>reason: {reason}</div>,
-                none: <></>,
-              })}
-            </span>
+            {reason && (
+              <span>
+                <div>reason: {reason}</div>
+              </span>
+            )}
           </>
         );
       }
       case ModlogActionType.ModRemoveCommunity: {
         let mrco = i.view as ModRemoveCommunityView;
+        let reason = mrco.mod_remove_community.reason;
+        let expires = mrco.mod_remove_community.expires;
         return (
           <>
             <span>
-              {mrco.mod_remove_community.removed.unwrapOr(false)
-                ? "Removed "
-                : "Restored "}
+              {mrco.mod_remove_community.removed ? "Removed " : "Restored "}
             </span>
             <span>
               Community <CommunityLink community={mrco.community} />
             </span>
-            <span>
-              {mrco.mod_remove_community.reason.match({
-                some: reason => <div>reason: {reason}</div>,
-                none: <></>,
-              })}
-            </span>
-            <span>
-              {mrco.mod_remove_community.expires.match({
-                some: expires => (
-                  <div>expires: {moment.utc(expires).fromNow()}</div>
-                ),
-                none: <></>,
-              })}
-            </span>
+            {reason && (
+              <span>
+                <div>reason: {reason}</div>
+              </span>
+            )}
+            {expires && (
+              <span>
+                <div>expires: {moment.utc(expires).fromNow()}</div>
+              </span>
+            )}
           </>
         );
       }
       case ModlogActionType.ModBanFromCommunity: {
         let mbfc = i.view as ModBanFromCommunityView;
+        let reason = mbfc.mod_ban_from_community.reason;
+        let expires = mbfc.mod_ban_from_community.expires;
         return (
           <>
             <span>
-              {mbfc.mod_ban_from_community.banned.unwrapOr(false)
-                ? "Banned "
-                : "Unbanned "}{" "}
+              {mbfc.mod_ban_from_community.banned ? "Banned " : "Unbanned "}{" "}
             </span>
             <span>
               <PersonListing person={mbfc.banned_person} />
@@ -435,20 +408,16 @@ export class Modlog extends Component<any, ModlogState> {
             <span>
               <CommunityLink community={mbfc.community} />
             </span>
-            <span>
-              {mbfc.mod_ban_from_community.reason.match({
-                some: reason => <div>reason: {reason}</div>,
-                none: <></>,
-              })}
-            </span>
-            <span>
-              {mbfc.mod_ban_from_community.expires.match({
-                some: expires => (
-                  <div>expires: {moment.utc(expires).fromNow()}</div>
-                ),
-                none: <></>,
-              })}
-            </span>
+            {reason && (
+              <span>
+                <div>reason: {reason}</div>
+              </span>
+            )}
+            {expires && (
+              <span>
+                <div>expires: {moment.utc(expires).fromNow()}</div>
+              </span>
+            )}
           </>
         );
       }
@@ -457,9 +426,7 @@ export class Modlog extends Component<any, ModlogState> {
         return (
           <>
             <span>
-              {mac.mod_add_community.removed.unwrapOr(false)
-                ? "Removed "
-                : "Appointed "}{" "}
+              {mac.mod_add_community.removed ? "Removed " : "Appointed "}{" "}
             </span>
             <span>
               <PersonListing person={mac.modded_person} />
@@ -476,9 +443,7 @@ export class Modlog extends Component<any, ModlogState> {
         return (
           <>
             <span>
-              {mtc.mod_transfer_community.removed.unwrapOr(false)
-                ? "Removed "
-                : "Transferred "}{" "}
+              {mtc.mod_transfer_community.removed ? "Removed " : "Transferred "}{" "}
             </span>
             <span>
               <CommunityLink community={mtc.community} />
@@ -492,28 +457,24 @@ export class Modlog extends Component<any, ModlogState> {
       }
       case ModlogActionType.ModBan: {
         let mb = i.view as ModBanView;
+        let reason = mb.mod_ban.reason;
+        let expires = mb.mod_ban.expires;
         return (
           <>
-            <span>
-              {mb.mod_ban.banned.unwrapOr(false) ? "Banned " : "Unbanned "}{" "}
-            </span>
+            <span>{mb.mod_ban.banned ? "Banned " : "Unbanned "} </span>
             <span>
               <PersonListing person={mb.banned_person} />
             </span>
-            <span>
-              {mb.mod_ban.reason.match({
-                some: reason => <div>reason: {reason}</div>,
-                none: <></>,
-              })}
-            </span>
-            <span>
-              {mb.mod_ban.expires.match({
-                some: expires => (
-                  <div>expires: {moment.utc(expires).fromNow()}</div>
-                ),
-                none: <></>,
-              })}
-            </span>
+            {reason && (
+              <span>
+                <div>reason: {reason}</div>
+              </span>
+            )}
+            {expires && (
+              <span>
+                <div>expires: {moment.utc(expires).fromNow()}</div>
+              </span>
+            )}
           </>
         );
       }
@@ -521,9 +482,7 @@ export class Modlog extends Component<any, ModlogState> {
         let ma = i.view as ModAddView;
         return (
           <>
-            <span>
-              {ma.mod_add.removed.unwrapOr(false) ? "Removed " : "Appointed "}{" "}
-            </span>
+            <span>{ma.mod_add.removed ? "Removed " : "Appointed "} </span>
             <span>
               <PersonListing person={ma.modded_person} />
             </span>
@@ -533,61 +492,61 @@ export class Modlog extends Component<any, ModlogState> {
       }
       case ModlogActionType.AdminPurgePerson: {
         let ap = i.view as AdminPurgePersonView;
+        let reason = ap.admin_purge_person.reason;
         return (
           <>
             <span>Purged a Person</span>
-            <span>
-              {ap.admin_purge_person.reason.match({
-                some: reason => <div>reason: {reason}</div>,
-                none: <></>,
-              })}
-            </span>
+            {reason && (
+              <span>
+                <div>reason: {reason}</div>
+              </span>
+            )}
           </>
         );
       }
       case ModlogActionType.AdminPurgeCommunity: {
         let ap = i.view as AdminPurgeCommunityView;
+        let reason = ap.admin_purge_community.reason;
         return (
           <>
             <span>Purged a Community</span>
-            <span>
-              {ap.admin_purge_community.reason.match({
-                some: reason => <div>reason: {reason}</div>,
-                none: <></>,
-              })}
-            </span>
+            {reason && (
+              <span>
+                <div>reason: {reason}</div>
+              </span>
+            )}
           </>
         );
       }
       case ModlogActionType.AdminPurgePost: {
         let ap = i.view as AdminPurgePostView;
+        let reason = ap.admin_purge_post.reason;
         return (
           <>
             <span>Purged a Post from from </span>
             <CommunityLink community={ap.community} />
-            <span>
-              {ap.admin_purge_post.reason.match({
-                some: reason => <div>reason: {reason}</div>,
-                none: <></>,
-              })}
-            </span>
+            {reason && (
+              <span>
+                <div>reason: {reason}</div>
+              </span>
+            )}
           </>
         );
       }
       case ModlogActionType.AdminPurgeComment: {
         let ap = i.view as AdminPurgeCommentView;
+        let reason = ap.admin_purge_comment.reason;
         return (
           <>
             <span>
               Purged a Comment from{" "}
               <Link to={`/post/${ap.post.id}`}>{ap.post.name}</Link>
             </span>
-            <span>
-              {ap.admin_purge_comment.reason.match({
-                some: reason => <div>reason: {reason}</div>,
-                none: <></>,
-              })}
-            </span>
+            {reason && (
+              <span>
+                <div>reason: {reason}</div>
+              </span>
+            )}
           </>
         );
       }
@@ -597,18 +556,19 @@ export class Modlog extends Component<any, ModlogState> {
   }
 
   combined() {
-    let combined = this.state.res.map(this.buildCombined).unwrapOr([]);
+    let res = this.state.res;
+    let combined = res ? this.buildCombined(res) : [];
 
     return (
       <tbody>
         {combined.map(i => (
           <tr key={i.id}>
             <td>
-              <MomentTime published={i.when_} updated={None} />
+              <MomentTime published={i.when_} />
             </td>
             <td>
-              {this.amAdminOrMod ? (
-                <PersonListing person={i.moderator.unwrap()} />
+              {this.amAdminOrMod && i.moderator ? (
+                <PersonListing person={i.moderator} />
               ) : (
                 <div>{this.modOrAdminText(i.moderator)}</div>
               )}
@@ -624,14 +584,12 @@ export class Modlog extends Component<any, ModlogState> {
     return amAdmin() || amMod(this.state.communityMods);
   }
 
-  modOrAdminText(person: Option<PersonSafe>): string {
-    return person.match({
-      some: res =>
-        this.isoData.site_res.admins.map(a => a.person.id).includes(res.id)
-          ? i18n.t("admin")
-          : i18n.t("mod"),
-      none: i18n.t("mod"),
-    });
+  modOrAdminText(person?: PersonSafe): string {
+    return person
+      ? this.isoData.site_res.admins.map(a => a.person.id).includes(person.id)
+        ? i18n.t("admin")
+        : i18n.t("mod")
+      : i18n.t("mod");
   }
 
   get documentTitle(): string {
@@ -639,13 +597,12 @@ export class Modlog extends Component<any, ModlogState> {
   }
 
   render() {
+    let communityName = this.state.communityName;
     return (
       <div className="container-lg">
         <HtmlTags
           title={this.documentTitle}
           path={this.context.router.route.match.url}
-          description={None}
-          image={None}
         />
         {this.state.loading ? (
           <h5>
@@ -654,14 +611,11 @@ export class Modlog extends Component<any, ModlogState> {
         ) : (
           <div>
             <h5>
-              {this.state.communityName.match({
-                some: name => (
-                  <Link className="text-body" to={`/c/${name}`}>
-                    /c/{name}{" "}
-                  </Link>
-                ),
-                none: <></>,
-              })}
+              {communityName && (
+                <Link className="text-body" to={`/c/${communityName}`}>
+                  /c/{communityName}{" "}
+                </Link>
+              )}
               <span>{i18n.t("modlog")}</span>
             </h5>
             <div className="form-row">
@@ -714,7 +668,7 @@ export class Modlog extends Component<any, ModlogState> {
                   <select
                     id="filter-mod"
                     className="form-control"
-                    value={toUndefined(this.state.filter_mod)}
+                    value={this.state.filter_mod}
                   >
                     <option>{i18n.t("filter_by_mod")}</option>
                   </select>
@@ -724,7 +678,7 @@ export class Modlog extends Component<any, ModlogState> {
                 <select
                   id="filter-user"
                   className="form-control"
-                  value={toUndefined(this.state.filter_user)}
+                  value={this.state.filter_user}
                 >
                   <option>{i18n.t("filter_by_user")}</option>
                 </select>
@@ -763,28 +717,26 @@ export class Modlog extends Component<any, ModlogState> {
   }
 
   refetch() {
-    let modlogForm = new GetModlog({
+    let auth = myAuth(false);
+    let modlogForm: GetModlog = {
       community_id: this.state.communityId,
-      page: Some(this.state.page),
-      limit: Some(fetchLimit),
-      auth: auth(false).ok(),
+      page: this.state.page,
+      limit: fetchLimit,
       type_: this.state.filter_action,
       other_person_id: this.state.filter_user,
       mod_person_id: this.state.filter_mod,
-    });
+      auth,
+    };
     WebSocketService.Instance.send(wsClient.getModlog(modlogForm));
 
-    this.state.communityId.match({
-      some: id => {
-        let communityForm = new GetCommunity({
-          id: Some(id),
-          name: None,
-          auth: auth(false).ok(),
-        });
-        WebSocketService.Instance.send(wsClient.getCommunity(communityForm));
-      },
-      none: void 0,
-    });
+    let communityId = this.state.communityId;
+    if (communityId) {
+      let communityForm: GetCommunity = {
+        id: communityId,
+        auth,
+      };
+      WebSocketService.Instance.send(wsClient.getCommunity(communityForm));
+    }
   }
 
   setupUserFilter() {
@@ -795,7 +747,7 @@ export class Modlog extends Component<any, ModlogState> {
         this.userChoices.passedElement.element.addEventListener(
           "choice",
           (e: any) => {
-            this.setState({ filter_user: Some(Number(e.detail.choice.value)) });
+            this.setState({ filter_user: Number(e.detail.choice.value) });
             this.refetch();
           },
           false
@@ -834,7 +786,7 @@ export class Modlog extends Component<any, ModlogState> {
         this.modChoices.passedElement.element.addEventListener(
           "choice",
           (e: any) => {
-            this.setState({ filter_mod: Some(Number(e.detail.choice.value)) });
+            this.setState({ filter_mod: Number(e.detail.choice.value) });
             this.refetch();
           },
           false
@@ -867,27 +819,25 @@ export class Modlog extends Component<any, ModlogState> {
 
   static fetchInitialData(req: InitialFetchRequest): Promise<any>[] {
     let pathSplit = req.path.split("/");
-    let communityId = Some(pathSplit[3]).map(Number);
+    let communityId = pathSplit[3] ? Number(pathSplit[3]) : undefined;
+    let auth = req.auth;
     let promises: Promise<any>[] = [];
 
-    let modlogForm = new GetModlog({
-      page: Some(1),
-      limit: Some(fetchLimit),
+    let modlogForm: GetModlog = {
+      page: 1,
+      limit: fetchLimit,
       community_id: communityId,
-      mod_person_id: None,
-      auth: req.auth,
       type_: ModlogActionType.All,
-      other_person_id: None,
-    });
+      auth,
+    };
 
     promises.push(req.client.getModlog(modlogForm));
 
-    if (communityId.isSome()) {
-      let communityForm = new GetCommunity({
+    if (communityId) {
+      let communityForm: GetCommunity = {
         id: communityId,
-        name: None,
         auth: req.auth,
-      });
+      };
       promises.push(req.client.getCommunity(communityForm));
     } else {
       promises.push(Promise.resolve());
@@ -902,16 +852,16 @@ export class Modlog extends Component<any, ModlogState> {
       toast(i18n.t(msg.error), "danger");
       return;
     } else if (op == UserOperation.GetModlog) {
-      let data = wsJsonToRes<GetModlogResponse>(msg, GetModlogResponse);
+      let data = wsJsonToRes<GetModlogResponse>(msg);
       window.scrollTo(0, 0);
-      this.setState({ res: Some(data), loading: false });
+      this.setState({ res: data, loading: false });
       this.setupUserFilter();
       this.setupModFilter();
     } else if (op == UserOperation.GetCommunity) {
-      let data = wsJsonToRes<GetCommunityResponse>(msg, GetCommunityResponse);
+      let data = wsJsonToRes<GetCommunityResponse>(msg);
       this.setState({
-        communityMods: Some(data.moderators),
-        communityName: Some(data.community_view.community.name),
+        communityMods: data.moderators,
+        communityName: data.community_view.community.name,
       });
     }
   }
index 00d8af80ec53db8085ccb84d850a86fbbd0239f7..2254a40e20799d28b4e07fb7324961e1940e04f4 100644 (file)
@@ -1,4 +1,3 @@
-import { None, Some } from "@sniptt/monads";
 import { Component, linkEvent } from "inferno";
 import {
   BlockPersonResponse,
@@ -30,13 +29,13 @@ import { i18n } from "../../i18next";
 import { CommentViewType, InitialFetchRequest } from "../../interfaces";
 import { UserService, WebSocketService } from "../../services";
 import {
-  auth,
   commentsToFlatNodes,
   createCommentLikeRes,
   editCommentRes,
   enableDownvotes,
   fetchLimit,
   isBrowser,
+  myAuth,
   relTags,
   saveCommentRes,
   setIsoData,
@@ -91,14 +90,9 @@ interface InboxState {
 }
 
 export class Inbox extends Component<any, InboxState> {
-  private isoData = setIsoData(
-    this.context,
-    GetRepliesResponse,
-    GetPersonMentionsResponse,
-    PrivateMessagesResponse
-  );
-  private subscription: Subscription;
-  private emptyState: InboxState = {
+  private isoData = setIsoData(this.context);
+  private subscription?: Subscription;
+  state: InboxState = {
     unreadOrAll: UnreadOrAll.Unread,
     messageType: MessageType.All,
     replies: [],
@@ -114,11 +108,10 @@ export class Inbox extends Component<any, InboxState> {
   constructor(props: any, context: any) {
     super(props, context);
 
-    this.state = this.emptyState;
     this.handleSortChange = this.handleSortChange.bind(this);
     this.handlePageChange = this.handlePageChange.bind(this);
 
-    if (UserService.Instance.myUserInfo.isNone() && isBrowser()) {
+    if (!UserService.Instance.myUserInfo && isBrowser()) {
       toast(i18n.t("not_logged_in"), "danger");
       this.context.router.history.push(`/login`);
     }
@@ -148,24 +141,22 @@ export class Inbox extends Component<any, InboxState> {
 
   componentWillUnmount() {
     if (isBrowser()) {
-      this.subscription.unsubscribe();
+      this.subscription?.unsubscribe();
     }
   }
 
   get documentTitle(): string {
-    return UserService.Instance.myUserInfo.match({
-      some: mui =>
-        `@${mui.local_user_view.person.name} ${i18n.t("inbox")} - ${
+    let mui = UserService.Instance.myUserInfo;
+    return mui
+      ? `@${mui.local_user_view.person.name} ${i18n.t("inbox")} - ${
           this.state.siteRes.site_view.site.name
-        }`,
-      none: "",
-    });
+        }`
+      : "";
   }
 
   render() {
-    let inboxRss = auth()
-      .ok()
-      .map(a => `/feeds/inbox/${a}.xml`);
+    let auth = myAuth();
+    let inboxRss = auth ? `/feeds/inbox/${auth}.xml` : undefined;
     return (
       <div className="container-lg">
         {this.state.loading ? (
@@ -178,26 +169,21 @@ export class Inbox extends Component<any, InboxState> {
               <HtmlTags
                 title={this.documentTitle}
                 path={this.context.router.route.match.url}
-                description={None}
-                image={None}
               />
               <h5 className="mb-2">
                 {i18n.t("inbox")}
-                {inboxRss.match({
-                  some: rss => (
-                    <small>
-                      <a href={rss} title="RSS" rel={relTags}>
-                        <Icon icon="rss" classes="ml-2 text-muted small" />
-                      </a>
-                      <link
-                        rel="alternate"
-                        type="application/atom+xml"
-                        href={rss}
-                      />
-                    </small>
-                  ),
-                  none: <></>,
-                })}
+                {inboxRss && (
+                  <small>
+                    <a href={inboxRss} title="RSS" rel={relTags}>
+                      <Icon icon="rss" classes="ml-2 text-muted small" />
+                    </a>
+                    <link
+                      rel="alternate"
+                      type="application/atom+xml"
+                      href={inboxRss}
+                    />
+                  </small>
+                )}
               </h5>
               {this.state.replies.length +
                 this.state.mentions.length +
@@ -387,9 +373,6 @@ export class Inbox extends Component<any, InboxState> {
               { comment_view: i.view as CommentView, children: [], depth: 0 },
             ]}
             viewType={CommentViewType.Flat}
-            moderators={None}
-            admins={None}
-            maxCommentsShown={None}
             noIndent
             markable
             showCommunity
@@ -411,9 +394,6 @@ export class Inbox extends Component<any, InboxState> {
               },
             ]}
             viewType={CommentViewType.Flat}
-            moderators={None}
-            admins={None}
-            maxCommentsShown={None}
             noIndent
             markable
             showCommunity
@@ -445,9 +425,6 @@ export class Inbox extends Component<any, InboxState> {
         <CommentNodes
           nodes={commentsToFlatNodes(this.state.replies)}
           viewType={CommentViewType.Flat}
-          moderators={None}
-          admins={None}
-          maxCommentsShown={None}
           noIndent
           markable
           showCommunity
@@ -468,9 +445,6 @@ export class Inbox extends Component<any, InboxState> {
             key={umv.person_mention.id}
             nodes={[{ comment_view: umv, children: [], depth: 0 }]}
             viewType={CommentViewType.Flat}
-            moderators={None}
-            admins={None}
-            maxCommentsShown={None}
             noIndent
             markable
             showCommunity
@@ -515,73 +489,79 @@ export class Inbox extends Component<any, InboxState> {
   static fetchInitialData(req: InitialFetchRequest): Promise<any>[] {
     let promises: Promise<any>[] = [];
 
-    let sort = Some(CommentSortType.New);
-
-    // It can be /u/me, or /username/1
-    let repliesForm = new GetReplies({
-      sort,
-      unread_only: Some(true),
-      page: Some(1),
-      limit: Some(fetchLimit),
-      auth: req.auth.unwrap(),
-    });
-    promises.push(req.client.getReplies(repliesForm));
-
-    let personMentionsForm = new GetPersonMentions({
-      sort,
-      unread_only: Some(true),
-      page: Some(1),
-      limit: Some(fetchLimit),
-      auth: req.auth.unwrap(),
-    });
-    promises.push(req.client.getPersonMentions(personMentionsForm));
-
-    let privateMessagesForm = new GetPrivateMessages({
-      unread_only: Some(true),
-      page: Some(1),
-      limit: Some(fetchLimit),
-      auth: req.auth.unwrap(),
-    });
-    promises.push(req.client.getPrivateMessages(privateMessagesForm));
+    let sort = CommentSortType.New;
+    let auth = req.auth;
+
+    if (auth) {
+      // It can be /u/me, or /username/1
+      let repliesForm: GetReplies = {
+        sort,
+        unread_only: true,
+        page: 1,
+        limit: fetchLimit,
+        auth,
+      };
+      promises.push(req.client.getReplies(repliesForm));
+
+      let personMentionsForm: GetPersonMentions = {
+        sort,
+        unread_only: true,
+        page: 1,
+        limit: fetchLimit,
+        auth,
+      };
+      promises.push(req.client.getPersonMentions(personMentionsForm));
+
+      let privateMessagesForm: GetPrivateMessages = {
+        unread_only: true,
+        page: 1,
+        limit: fetchLimit,
+        auth,
+      };
+      promises.push(req.client.getPrivateMessages(privateMessagesForm));
+    }
 
     return promises;
   }
 
   refetch() {
-    let sort = Some(this.state.sort);
-    let unread_only = Some(this.state.unreadOrAll == UnreadOrAll.Unread);
-    let page = Some(this.state.page);
-    let limit = Some(fetchLimit);
-
-    let repliesForm = new GetReplies({
-      sort,
-      unread_only,
-      page,
-      limit,
-      auth: auth().unwrap(),
-    });
-    WebSocketService.Instance.send(wsClient.getReplies(repliesForm));
-
-    let personMentionsForm = new GetPersonMentions({
-      sort,
-      unread_only,
-      page,
-      limit,
-      auth: auth().unwrap(),
-    });
-    WebSocketService.Instance.send(
-      wsClient.getPersonMentions(personMentionsForm)
-    );
+    let sort = this.state.sort;
+    let unread_only = this.state.unreadOrAll == UnreadOrAll.Unread;
+    let page = this.state.page;
+    let limit = fetchLimit;
+    let auth = myAuth();
+
+    if (auth) {
+      let repliesForm: GetReplies = {
+        sort,
+        unread_only,
+        page,
+        limit,
+        auth,
+      };
+      WebSocketService.Instance.send(wsClient.getReplies(repliesForm));
+
+      let personMentionsForm: GetPersonMentions = {
+        sort,
+        unread_only,
+        page,
+        limit,
+        auth,
+      };
+      WebSocketService.Instance.send(
+        wsClient.getPersonMentions(personMentionsForm)
+      );
 
-    let privateMessagesForm = new GetPrivateMessages({
-      unread_only,
-      page,
-      limit,
-      auth: auth().unwrap(),
-    });
-    WebSocketService.Instance.send(
-      wsClient.getPrivateMessages(privateMessagesForm)
-    );
+      let privateMessagesForm: GetPrivateMessages = {
+        unread_only,
+        page,
+        limit,
+        auth,
+      };
+      WebSocketService.Instance.send(
+        wsClient.getPrivateMessages(privateMessagesForm)
+      );
+    }
   }
 
   handleSortChange(val: CommentSortType) {
@@ -590,16 +570,19 @@ export class Inbox extends Component<any, InboxState> {
   }
 
   markAllAsRead(i: Inbox) {
-    WebSocketService.Instance.send(
-      wsClient.markAllAsRead({
-        auth: auth().unwrap(),
-      })
-    );
-    i.setState({ replies: [], mentions: [], messages: [] });
-    i.setState({ combined: i.buildCombined() });
-    UserService.Instance.unreadInboxCountSub.next(0);
-    window.scrollTo(0, 0);
-    i.setState(i.state);
+    let auth = myAuth();
+    if (auth) {
+      WebSocketService.Instance.send(
+        wsClient.markAllAsRead({
+          auth,
+        })
+      );
+      i.setState({ replies: [], mentions: [], messages: [] });
+      i.setState({ combined: i.buildCombined() });
+      UserService.Instance.unreadInboxCountSub.next(0);
+      window.scrollTo(0, 0);
+      i.setState(i.state);
+    }
   }
 
   sendUnreadCount(read: boolean) {
@@ -620,73 +603,62 @@ export class Inbox extends Component<any, InboxState> {
     } else if (msg.reconnect) {
       this.refetch();
     } else if (op == UserOperation.GetReplies) {
-      let data = wsJsonToRes<GetRepliesResponse>(msg, GetRepliesResponse);
+      let data = wsJsonToRes<GetRepliesResponse>(msg);
       this.setState({ replies: data.replies });
       this.setState({ combined: this.buildCombined(), loading: false });
       window.scrollTo(0, 0);
       setupTippy();
     } else if (op == UserOperation.GetPersonMentions) {
-      let data = wsJsonToRes<GetPersonMentionsResponse>(
-        msg,
-        GetPersonMentionsResponse
-      );
+      let data = wsJsonToRes<GetPersonMentionsResponse>(msg);
       this.setState({ mentions: data.mentions });
       this.setState({ combined: this.buildCombined() });
       window.scrollTo(0, 0);
       setupTippy();
     } else if (op == UserOperation.GetPrivateMessages) {
-      let data = wsJsonToRes<PrivateMessagesResponse>(
-        msg,
-        PrivateMessagesResponse
-      );
+      let data = wsJsonToRes<PrivateMessagesResponse>(msg);
       this.setState({ messages: data.private_messages });
       this.setState({ combined: this.buildCombined() });
       window.scrollTo(0, 0);
       setupTippy();
     } else if (op == UserOperation.EditPrivateMessage) {
-      let data = wsJsonToRes<PrivateMessageResponse>(
-        msg,
-        PrivateMessageResponse
-      );
-      let found: PrivateMessageView = this.state.messages.find(
+      let data = wsJsonToRes<PrivateMessageResponse>(msg);
+      let found = this.state.messages.find(
         m =>
           m.private_message.id === data.private_message_view.private_message.id
       );
       if (found) {
         let combinedView = this.state.combined.find(
           i => i.id == data.private_message_view.private_message.id
-        ).view as PrivateMessageView;
-        found.private_message.content = combinedView.private_message.content =
-          data.private_message_view.private_message.content;
-        found.private_message.updated = combinedView.private_message.updated =
-          data.private_message_view.private_message.updated;
+        )?.view as PrivateMessageView | undefined;
+        if (combinedView) {
+          found.private_message.content = combinedView.private_message.content =
+            data.private_message_view.private_message.content;
+          found.private_message.updated = combinedView.private_message.updated =
+            data.private_message_view.private_message.updated;
+        }
       }
       this.setState(this.state);
     } else if (op == UserOperation.DeletePrivateMessage) {
-      let data = wsJsonToRes<PrivateMessageResponse>(
-        msg,
-        PrivateMessageResponse
-      );
-      let found: PrivateMessageView = this.state.messages.find(
+      let data = wsJsonToRes<PrivateMessageResponse>(msg);
+      let found = this.state.messages.find(
         m =>
           m.private_message.id === data.private_message_view.private_message.id
       );
       if (found) {
         let combinedView = this.state.combined.find(
           i => i.id == data.private_message_view.private_message.id
-        ).view as PrivateMessageView;
-        found.private_message.deleted = combinedView.private_message.deleted =
-          data.private_message_view.private_message.deleted;
-        found.private_message.updated = combinedView.private_message.updated =
-          data.private_message_view.private_message.updated;
+        )?.view as PrivateMessageView | undefined;
+        if (combinedView) {
+          found.private_message.deleted = combinedView.private_message.deleted =
+            data.private_message_view.private_message.deleted;
+          found.private_message.updated = combinedView.private_message.updated =
+            data.private_message_view.private_message.updated;
+        }
       }
       this.setState(this.state);
     } else if (op == UserOperation.MarkPrivateMessageAsRead) {
-      let data = wsJsonToRes<PrivateMessageResponse>(
-        msg,
-        PrivateMessageResponse
-      );
-      let found: PrivateMessageView = this.state.messages.find(
+      let data = wsJsonToRes<PrivateMessageResponse>(msg);
+      let found = this.state.messages.find(
         m =>
           m.private_message.id === data.private_message_view.private_message.id
       );
@@ -696,30 +668,32 @@ export class Inbox extends Component<any, InboxState> {
           i =>
             i.id == data.private_message_view.private_message.id &&
             i.type_ == ReplyEnum.Message
-        ).view as PrivateMessageView;
-        found.private_message.updated = combinedView.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.private_message_view.private_message.read
-        ) {
-          this.setState({
-            messages: this.state.messages.filter(
-              r =>
-                r.private_message.id !==
-                data.private_message_view.private_message.id
-            ),
-          });
-          this.setState({
-            combined: this.state.combined.filter(
-              r => r.id !== data.private_message_view.private_message.id
-            ),
-          });
-        } else {
-          found.private_message.read = combinedView.private_message.read =
-            data.private_message_view.private_message.read;
+        )?.view as PrivateMessageView | undefined;
+        if (combinedView) {
+          found.private_message.updated = combinedView.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.private_message_view.private_message.read
+          ) {
+            this.setState({
+              messages: this.state.messages.filter(
+                r =>
+                  r.private_message.id !==
+                  data.private_message_view.private_message.id
+              ),
+            });
+            this.setState({
+              combined: this.state.combined.filter(
+                r => r.id !== data.private_message_view.private_message.id
+              ),
+            });
+          } else {
+            found.private_message.read = combinedView.private_message.read =
+              data.private_message_view.private_message.read;
+          }
         }
       }
       this.sendUnreadCount(data.private_message_view.private_message.read);
@@ -731,11 +705,11 @@ export class Inbox extends Component<any, InboxState> {
       op == UserOperation.DeleteComment ||
       op == UserOperation.RemoveComment
     ) {
-      let data = wsJsonToRes<CommentResponse>(msg, CommentResponse);
+      let data = wsJsonToRes<CommentResponse>(msg);
       editCommentRes(data.comment_view, this.state.replies);
       this.setState(this.state);
     } else if (op == UserOperation.MarkCommentReplyAsRead) {
-      let data = wsJsonToRes<CommentReplyResponse>(msg, CommentReplyResponse);
+      let data = wsJsonToRes<CommentReplyResponse>(msg);
 
       let found = this.state.replies.find(
         c => c.comment_reply.id == data.comment_reply_view.comment_reply.id
@@ -746,47 +720,50 @@ export class Inbox extends Component<any, InboxState> {
           i =>
             i.id == data.comment_reply_view.comment_reply.id &&
             i.type_ == ReplyEnum.Reply
-        ).view as CommentReplyView;
-        found.comment.content = combinedView.comment.content =
-          data.comment_reply_view.comment.content;
-        found.comment.updated = combinedView.comment.updated =
-          data.comment_reply_view.comment.updated;
-        found.comment.removed = combinedView.comment.removed =
-          data.comment_reply_view.comment.removed;
-        found.comment.deleted = combinedView.comment.deleted =
-          data.comment_reply_view.comment.deleted;
-        found.counts.upvotes = combinedView.counts.upvotes =
-          data.comment_reply_view.counts.upvotes;
-        found.counts.downvotes = combinedView.counts.downvotes =
-          data.comment_reply_view.counts.downvotes;
-        found.counts.score = combinedView.counts.score =
-          data.comment_reply_view.counts.score;
-
-        // If youre in the unread view, just remove it from the list
-        if (
-          this.state.unreadOrAll == UnreadOrAll.Unread &&
-          data.comment_reply_view.comment_reply.read
-        ) {
-          this.setState({
-            replies: this.state.replies.filter(
-              r =>
-                r.comment_reply.id !== data.comment_reply_view.comment_reply.id
-            ),
-          });
-          this.setState({
-            combined: this.state.combined.filter(
-              r => r.id !== data.comment_reply_view.comment_reply.id
-            ),
-          });
-        } else {
-          found.comment_reply.read = combinedView.comment_reply.read =
-            data.comment_reply_view.comment_reply.read;
+        )?.view as CommentReplyView | undefined;
+        if (combinedView) {
+          found.comment.content = combinedView.comment.content =
+            data.comment_reply_view.comment.content;
+          found.comment.updated = combinedView.comment.updated =
+            data.comment_reply_view.comment.updated;
+          found.comment.removed = combinedView.comment.removed =
+            data.comment_reply_view.comment.removed;
+          found.comment.deleted = combinedView.comment.deleted =
+            data.comment_reply_view.comment.deleted;
+          found.counts.upvotes = combinedView.counts.upvotes =
+            data.comment_reply_view.counts.upvotes;
+          found.counts.downvotes = combinedView.counts.downvotes =
+            data.comment_reply_view.counts.downvotes;
+          found.counts.score = combinedView.counts.score =
+            data.comment_reply_view.counts.score;
+
+          // If youre in the unread view, just remove it from the list
+          if (
+            this.state.unreadOrAll == UnreadOrAll.Unread &&
+            data.comment_reply_view.comment_reply.read
+          ) {
+            this.setState({
+              replies: this.state.replies.filter(
+                r =>
+                  r.comment_reply.id !==
+                  data.comment_reply_view.comment_reply.id
+              ),
+            });
+            this.setState({
+              combined: this.state.combined.filter(
+                r => r.id !== data.comment_reply_view.comment_reply.id
+              ),
+            });
+          } else {
+            found.comment_reply.read = combinedView.comment_reply.read =
+              data.comment_reply_view.comment_reply.read;
+          }
         }
       }
       this.sendUnreadCount(data.comment_reply_view.comment_reply.read);
       this.setState(this.state);
     } else if (op == UserOperation.MarkPersonMentionAsRead) {
-      let data = wsJsonToRes<PersonMentionResponse>(msg, PersonMentionResponse);
+      let data = wsJsonToRes<PersonMentionResponse>(msg);
 
       // TODO this might not be correct, it might need to use the comment id
       let found = this.state.mentions.find(
@@ -798,94 +775,85 @@ export class Inbox extends Component<any, InboxState> {
           i =>
             i.id == data.person_mention_view.person_mention.id &&
             i.type_ == ReplyEnum.Mention
-        ).view as PersonMentionView;
-        found.comment.content = combinedView.comment.content =
-          data.person_mention_view.comment.content;
-        found.comment.updated = combinedView.comment.updated =
-          data.person_mention_view.comment.updated;
-        found.comment.removed = combinedView.comment.removed =
-          data.person_mention_view.comment.removed;
-        found.comment.deleted = combinedView.comment.deleted =
-          data.person_mention_view.comment.deleted;
-        found.counts.upvotes = combinedView.counts.upvotes =
-          data.person_mention_view.counts.upvotes;
-        found.counts.downvotes = combinedView.counts.downvotes =
-          data.person_mention_view.counts.downvotes;
-        found.counts.score = combinedView.counts.score =
-          data.person_mention_view.counts.score;
-
-        // If youre in the unread view, just remove it from the list
-        if (
-          this.state.unreadOrAll == UnreadOrAll.Unread &&
-          data.person_mention_view.person_mention.read
-        ) {
-          this.setState({
-            mentions: this.state.mentions.filter(
-              r =>
-                r.person_mention.id !==
-                data.person_mention_view.person_mention.id
-            ),
-          });
-          this.setState({
-            combined: this.state.combined.filter(
-              r => r.id !== data.person_mention_view.person_mention.id
-            ),
-          });
-        } else {
-          // TODO test to make sure these mentions are getting marked as read
-          found.person_mention.read = combinedView.person_mention.read =
-            data.person_mention_view.person_mention.read;
+        )?.view as PersonMentionView | undefined;
+        if (combinedView) {
+          found.comment.content = combinedView.comment.content =
+            data.person_mention_view.comment.content;
+          found.comment.updated = combinedView.comment.updated =
+            data.person_mention_view.comment.updated;
+          found.comment.removed = combinedView.comment.removed =
+            data.person_mention_view.comment.removed;
+          found.comment.deleted = combinedView.comment.deleted =
+            data.person_mention_view.comment.deleted;
+          found.counts.upvotes = combinedView.counts.upvotes =
+            data.person_mention_view.counts.upvotes;
+          found.counts.downvotes = combinedView.counts.downvotes =
+            data.person_mention_view.counts.downvotes;
+          found.counts.score = combinedView.counts.score =
+            data.person_mention_view.counts.score;
+
+          // If youre in the unread view, just remove it from the list
+          if (
+            this.state.unreadOrAll == UnreadOrAll.Unread &&
+            data.person_mention_view.person_mention.read
+          ) {
+            this.setState({
+              mentions: this.state.mentions.filter(
+                r =>
+                  r.person_mention.id !==
+                  data.person_mention_view.person_mention.id
+              ),
+            });
+            this.setState({
+              combined: this.state.combined.filter(
+                r => r.id !== data.person_mention_view.person_mention.id
+              ),
+            });
+          } else {
+            // TODO test to make sure these mentions are getting marked as read
+            found.person_mention.read = combinedView.person_mention.read =
+              data.person_mention_view.person_mention.read;
+          }
         }
       }
       this.sendUnreadCount(data.person_mention_view.person_mention.read);
       this.setState(this.state);
     } else if (op == UserOperation.CreatePrivateMessage) {
-      let data = wsJsonToRes<PrivateMessageResponse>(
-        msg,
-        PrivateMessageResponse
-      );
-      UserService.Instance.myUserInfo.match({
-        some: mui => {
-          if (
-            data.private_message_view.recipient.id ==
-            mui.local_user_view.person.id
-          ) {
-            this.state.messages.unshift(data.private_message_view);
-            this.state.combined.unshift(
-              this.messageToReplyType(data.private_message_view)
-            );
-            this.setState(this.state);
-          }
-        },
-        none: void 0,
-      });
+      let data = wsJsonToRes<PrivateMessageResponse>(msg);
+      let mui = UserService.Instance.myUserInfo;
+      if (
+        data.private_message_view.recipient.id == mui?.local_user_view.person.id
+      ) {
+        this.state.messages.unshift(data.private_message_view);
+        this.state.combined.unshift(
+          this.messageToReplyType(data.private_message_view)
+        );
+        this.setState(this.state);
+      }
     } else if (op == UserOperation.SaveComment) {
-      let data = wsJsonToRes<CommentResponse>(msg, CommentResponse);
+      let data = wsJsonToRes<CommentResponse>(msg);
       saveCommentRes(data.comment_view, this.state.replies);
       this.setState(this.state);
       setupTippy();
     } else if (op == UserOperation.CreateCommentLike) {
-      let data = wsJsonToRes<CommentResponse>(msg, CommentResponse);
+      let data = wsJsonToRes<CommentResponse>(msg);
       createCommentLikeRes(data.comment_view, this.state.replies);
       this.setState(this.state);
     } else if (op == UserOperation.BlockPerson) {
-      let data = wsJsonToRes<BlockPersonResponse>(msg, BlockPersonResponse);
+      let data = wsJsonToRes<BlockPersonResponse>(msg);
       updatePersonBlock(data);
     } else if (op == UserOperation.CreatePostReport) {
-      let data = wsJsonToRes<PostReportResponse>(msg, PostReportResponse);
+      let data = wsJsonToRes<PostReportResponse>(msg);
       if (data) {
         toast(i18n.t("report_created"));
       }
     } else if (op == UserOperation.CreateCommentReport) {
-      let data = wsJsonToRes<CommentReportResponse>(msg, CommentReportResponse);
+      let data = wsJsonToRes<CommentReportResponse>(msg);
       if (data) {
         toast(i18n.t("report_created"));
       }
     } else if (op == UserOperation.CreatePrivateMessageReport) {
-      let data = wsJsonToRes<PrivateMessageReportResponse>(
-        msg,
-        PrivateMessageReportResponse
-      );
+      let data = wsJsonToRes<PrivateMessageReportResponse>(msg);
       if (data) {
         toast(i18n.t("report_created"));
       }
index d3d81828834c856518d046f12b50ba130fa3d871..32ebad69a974f21034a4e80ac23efd23d891656f 100644 (file)
@@ -1,9 +1,8 @@
-import { None } from "@sniptt/monads";
 import { Component, linkEvent } from "inferno";
 import {
   GetSiteResponse,
   LoginResponse,
-  PasswordChange as PasswordChangeForm,
+  PasswordChange as PWordChange,
   UserOperation,
   wsJsonToRes,
   wsUserOp,
@@ -23,21 +22,23 @@ import { HtmlTags } from "../common/html-tags";
 import { Spinner } from "../common/icon";
 
 interface State {
-  passwordChangeForm: PasswordChangeForm;
+  form: {
+    token: string;
+    password?: string;
+    password_verify?: string;
+  };
   loading: boolean;
   siteRes: GetSiteResponse;
 }
 
 export class PasswordChange extends Component<any, State> {
   private isoData = setIsoData(this.context);
-  private subscription: Subscription;
+  private subscription?: Subscription;
 
-  emptyState: State = {
-    passwordChangeForm: new PasswordChangeForm({
+  state: State = {
+    form: {
       token: this.props.match.params.token,
-      password: undefined,
-      password_verify: undefined,
-    }),
+    },
     loading: false,
     siteRes: this.isoData.site_res,
   };
@@ -45,15 +46,13 @@ export class PasswordChange extends Component<any, State> {
   constructor(props: any, context: any) {
     super(props, context);
 
-    this.state = this.emptyState;
-
     this.parseMessage = this.parseMessage.bind(this);
     this.subscription = wsSubscribe(this.parseMessage);
   }
 
   componentWillUnmount() {
     if (isBrowser()) {
-      this.subscription.unsubscribe();
+      this.subscription?.unsubscribe();
     }
   }
 
@@ -69,8 +68,6 @@ export class PasswordChange extends Component<any, State> {
         <HtmlTags
           title={this.documentTitle}
           path={this.context.router.route.match.url}
-          description={None}
-          image={None}
         />
         <div className="row">
           <div className="col-12 col-lg-6 offset-lg-3 mb-4">
@@ -93,7 +90,7 @@ export class PasswordChange extends Component<any, State> {
             <input
               id="new-password"
               type="password"
-              value={this.state.passwordChangeForm.password}
+              value={this.state.form.password}
               onInput={linkEvent(this, this.handlePasswordChange)}
               className="form-control"
               required
@@ -109,7 +106,7 @@ export class PasswordChange extends Component<any, State> {
             <input
               id="verify-password"
               type="password"
-              value={this.state.passwordChangeForm.password_verify}
+              value={this.state.form.password_verify}
               onInput={linkEvent(this, this.handleVerifyPasswordChange)}
               className="form-control"
               required
@@ -133,12 +130,12 @@ export class PasswordChange extends Component<any, State> {
   }
 
   handlePasswordChange(i: PasswordChange, event: any) {
-    i.state.passwordChangeForm.password = event.target.value;
+    i.state.form.password = event.target.value;
     i.setState(i.state);
   }
 
   handleVerifyPasswordChange(i: PasswordChange, event: any) {
-    i.state.passwordChangeForm.password_verify = event.target.value;
+    i.state.form.password_verify = event.target.value;
     i.setState(i.state);
   }
 
@@ -146,9 +143,18 @@ export class PasswordChange extends Component<any, State> {
     event.preventDefault();
     i.setState({ loading: true });
 
-    WebSocketService.Instance.send(
-      wsClient.passwordChange(i.state.passwordChangeForm)
-    );
+    let password = i.state.form.password;
+    let password_verify = i.state.form.password_verify;
+
+    if (password && password_verify) {
+      let form: PWordChange = {
+        token: i.state.form.token,
+        password,
+        password_verify,
+      };
+
+      WebSocketService.Instance.send(wsClient.passwordChange(form));
+    }
   }
 
   parseMessage(msg: any) {
@@ -159,8 +165,7 @@ export class PasswordChange extends Component<any, State> {
       this.setState({ loading: false });
       return;
     } else if (op == UserOperation.PasswordChange) {
-      let data = wsJsonToRes<LoginResponse>(msg, LoginResponse);
-      this.setState(this.emptyState);
+      let data = wsJsonToRes<LoginResponse>(msg);
       UserService.Instance.login(data);
       this.props.history.push("/");
       location.reload();
index f50b6d362a4d542137d9b9adec44ce73cad1c5d2..450f6c53ca38f87964da07e9209efe18955f52e1 100644 (file)
@@ -1,4 +1,3 @@
-import { None, Some } from "@sniptt/monads/build";
 import { Component } from "inferno";
 import {
   CommentView,
@@ -94,9 +93,7 @@ export class PersonDetails extends Component<PersonDetailsProps, any> {
             key={i.id}
             nodes={[{ comment_view: c, children: [], depth: 0 }]}
             viewType={CommentViewType.Flat}
-            admins={Some(this.props.admins)}
-            moderators={None}
-            maxCommentsShown={None}
+            admins={this.props.admins}
             noBorder
             noIndent
             showCommunity
@@ -113,9 +110,7 @@ export class PersonDetails extends Component<PersonDetailsProps, any> {
           <PostListing
             key={i.id}
             post_view={p}
-            admins={Some(this.props.admins)}
-            duplicates={None}
-            moderators={None}
+            admins={this.props.admins}
             showCommunity
             enableDownvotes={this.props.enableDownvotes}
             enableNsfw={this.props.enableNsfw}
@@ -171,9 +166,7 @@ export class PersonDetails extends Component<PersonDetailsProps, any> {
         <CommentNodes
           nodes={commentsToFlatNodes(this.props.personRes.comments)}
           viewType={CommentViewType.Flat}
-          admins={Some(this.props.admins)}
-          moderators={None}
-          maxCommentsShown={None}
+          admins={this.props.admins}
           noIndent
           showCommunity
           showContext
@@ -192,10 +185,8 @@ export class PersonDetails extends Component<PersonDetailsProps, any> {
           <>
             <PostListing
               post_view={post}
-              admins={Some(this.props.admins)}
+              admins={this.props.admins}
               showCommunity
-              duplicates={None}
-              moderators={None}
               enableDownvotes={this.props.enableDownvotes}
               enableNsfw={this.props.enableNsfw}
               allLanguages={this.props.allLanguages}
index 88b88820252d141767be8aa3cd5353c76858ee72..3050450b05163cc75c83c39222c58828e6bbf581 100644 (file)
@@ -37,9 +37,9 @@ export class PersonListing extends Component<PersonListingProps, any> {
 
     let displayName = this.props.useApubName
       ? apubName
-      : person.display_name.unwrapOr(apubName);
+      : person.display_name ?? apubName;
 
-    if (this.props.showApubName && !local && person.display_name.isSome()) {
+    if (this.props.showApubName && !local && person.display_name) {
       displayName = `${displayName} (${apubName})`;
     }
 
@@ -70,14 +70,12 @@ export class PersonListing extends Component<PersonListingProps, any> {
   }
 
   avatarAndName(displayName: string) {
+    let avatar = this.props.person.avatar;
     return (
       <>
-        {this.props.person.avatar.match({
-          some: avatar =>
-            !this.props.hideAvatar &&
-            showAvatars() && <PictrsImage src={avatar} icon />,
-          none: <></>,
-        })}
+        {avatar && !this.props.hideAvatar && showAvatars() && (
+          <PictrsImage src={avatar} icon />
+        )}
         <span>{displayName}</span>
       </>
     );
index 47399843cab521e119195d8f15d02d9e1a2a8ad0..d12a981fc72b29c7b517521d8662a3f587810b4b 100644 (file)
@@ -1,4 +1,3 @@
-import { None, Option, Some } from "@sniptt/monads";
 import { Component, linkEvent } from "inferno";
 import { Link } from "inferno-router";
 import {
@@ -14,7 +13,6 @@ import {
   PostResponse,
   PurgeItemResponse,
   SortType,
-  toUndefined,
   UserOperation,
   wsJsonToRes,
   wsUserOp,
@@ -25,7 +23,6 @@ import { i18n } from "../../i18next";
 import { InitialFetchRequest, PersonDetailsView } from "../../interfaces";
 import { UserService, WebSocketService } from "../../services";
 import {
-  auth,
   canMod,
   capitalizeFirstLetter,
   createCommentLikeRes,
@@ -40,6 +37,7 @@ import {
   isAdmin,
   isBanned,
   mdToHtml,
+  myAuth,
   numToSI,
   relTags,
   restoreScrollPosition,
@@ -63,15 +61,15 @@ import { PersonDetails } from "./person-details";
 import { PersonListing } from "./person-listing";
 
 interface ProfileState {
-  personRes: Option<GetPersonDetailsResponse>;
+  personRes?: GetPersonDetailsResponse;
   userName: string;
   view: PersonDetailsView;
   sort: SortType;
   page: number;
   loading: boolean;
   personBlocked: boolean;
-  banReason: Option<string>;
-  banExpireDays: Option<number>;
+  banReason?: string;
+  banExpireDays?: number;
   showBanDialog: boolean;
   removeData: boolean;
   siteRes: GetSiteResponse;
@@ -81,7 +79,7 @@ interface ProfileProps {
   view: PersonDetailsView;
   sort: SortType;
   page: number;
-  person_id: number | null;
+  person_id?: number;
   username: string;
 }
 
@@ -92,10 +90,9 @@ interface UrlParams {
 }
 
 export class Profile extends Component<any, ProfileState> {
-  private isoData = setIsoData(this.context, GetPersonDetailsResponse);
-  private subscription: Subscription;
-  private emptyState: ProfileState = {
-    personRes: None,
+  private isoData = setIsoData(this.context);
+  private subscription?: Subscription;
+  state: ProfileState = {
     userName: getUsernameFromProps(this.props),
     loading: true,
     view: Profile.getViewFromProps(this.props.match.view),
@@ -104,15 +101,12 @@ export class Profile extends Component<any, ProfileState> {
     personBlocked: false,
     siteRes: this.isoData.site_res,
     showBanDialog: false,
-    banReason: null,
-    banExpireDays: null,
     removeData: false,
   };
 
   constructor(props: any, context: any) {
     super(props, context);
 
-    this.state = this.emptyState;
     this.handleSortChange = this.handleSortChange.bind(this);
     this.handlePageChange = this.handlePageChange.bind(this);
 
@@ -123,7 +117,7 @@ export class Profile extends Component<any, ProfileState> {
     if (this.isoData.path == this.context.router.route.match.url) {
       this.state = {
         ...this.state,
-        personRes: Some(this.isoData.routeData[0] as GetPersonDetailsResponse),
+        personRes: this.isoData.routeData[0] as GetPersonDetailsResponse,
         loading: false,
       };
     } else {
@@ -132,45 +126,34 @@ export class Profile extends Component<any, ProfileState> {
   }
 
   fetchUserData() {
-    let form = new GetPersonDetails({
-      username: Some(this.state.userName),
-      person_id: None,
-      community_id: None,
-      sort: Some(this.state.sort),
-      saved_only: Some(this.state.view === PersonDetailsView.Saved),
-      page: Some(this.state.page),
-      limit: Some(fetchLimit),
-      auth: auth(false).ok(),
-    });
+    let form: GetPersonDetails = {
+      username: this.state.userName,
+      sort: this.state.sort,
+      saved_only: this.state.view === PersonDetailsView.Saved,
+      page: this.state.page,
+      limit: fetchLimit,
+      auth: myAuth(false),
+    };
     WebSocketService.Instance.send(wsClient.getPersonDetails(form));
   }
 
   get amCurrentUser() {
-    return UserService.Instance.myUserInfo.match({
-      some: mui =>
-        this.state.personRes.match({
-          some: res =>
-            mui.local_user_view.person.id == res.person_view.person.id,
-          none: false,
-        }),
-      none: false,
-    });
+    return (
+      UserService.Instance.myUserInfo?.local_user_view.person.id ==
+      this.state.personRes?.person_view.person.id
+    );
   }
 
   setPersonBlock() {
-    UserService.Instance.myUserInfo.match({
-      some: mui =>
-        this.state.personRes.match({
-          some: res =>
-            this.setState({
-              personBlocked: mui.person_blocks
-                .map(a => a.target.id)
-                .includes(res.person_view.person.id),
-            }),
-          none: void 0,
-        }),
-      none: void 0,
-    });
+    let mui = UserService.Instance.myUserInfo;
+    let res = this.state.personRes;
+    if (mui && res) {
+      this.setState({
+        personBlocked: mui.person_blocks
+          .map(a => a.target.id)
+          .includes(res.person_view.person.id),
+      });
+    }
   }
 
   static getViewFromProps(view: string): PersonDetailsView {
@@ -190,19 +173,17 @@ export class Profile extends Component<any, ProfileState> {
 
     let username = pathSplit[2];
     let view = this.getViewFromProps(pathSplit[4]);
-    let sort = Some(this.getSortTypeFromProps(pathSplit[6]));
-    let page = Some(this.getPageFromProps(Number(pathSplit[8])));
+    let sort = this.getSortTypeFromProps(pathSplit[6]);
+    let page = this.getPageFromProps(Number(pathSplit[8]));
 
-    let form = new GetPersonDetails({
-      username: Some(username),
-      person_id: None,
-      community_id: None,
+    let form: GetPersonDetails = {
+      username: username,
       sort,
-      saved_only: Some(view === PersonDetailsView.Saved),
+      saved_only: view === PersonDetailsView.Saved,
       page,
-      limit: Some(fetchLimit),
+      limit: fetchLimit,
       auth: req.auth,
-    });
+    };
     return [req.client.getPersonDetails(form)];
   }
 
@@ -212,7 +193,7 @@ export class Profile extends Component<any, ProfileState> {
   }
 
   componentWillUnmount() {
-    this.subscription.unsubscribe();
+    this.subscription?.unsubscribe();
     saveScrollPosition(this.context);
   }
 
@@ -221,7 +202,7 @@ export class Profile extends Component<any, ProfileState> {
       view: this.getViewFromProps(props.match.params.view),
       sort: this.getSortTypeFromProps(props.match.params.sort),
       page: this.getPageFromProps(props.match.params.page),
-      person_id: Number(props.match.params.id) || null,
+      person_id: Number(props.match.params.id),
       username: props.match.params.username,
     };
   }
@@ -238,14 +219,14 @@ export class Profile extends Component<any, ProfileState> {
   }
 
   get documentTitle(): string {
-    return this.state.personRes.match({
-      some: res =>
-        `@${res.person_view.person.name} - ${this.state.siteRes.site_view.site.name}`,
-      none: "",
-    });
+    let res = this.state.personRes;
+    return res
+      ? `@${res.person_view.person.name} - ${this.state.siteRes.site_view.site.name}`
+      : "";
   }
 
   render() {
+    let res = this.state.personRes;
     return (
       <div className="container-lg">
         {this.state.loading ? (
@@ -253,46 +234,43 @@ export class Profile extends Component<any, ProfileState> {
             <Spinner large />
           </h5>
         ) : (
-          this.state.personRes.match({
-            some: res => (
-              <div className="row">
-                <div className="col-12 col-md-8">
-                  <>
-                    <HtmlTags
-                      title={this.documentTitle}
-                      path={this.context.router.route.match.url}
-                      description={res.person_view.person.bio}
-                      image={res.person_view.person.avatar}
-                    />
-                    {this.userInfo()}
-                    <hr />
-                  </>
-                  {!this.state.loading && this.selects()}
-                  <PersonDetails
-                    personRes={res}
-                    admins={this.state.siteRes.admins}
-                    sort={this.state.sort}
-                    page={this.state.page}
-                    limit={fetchLimit}
-                    enableDownvotes={enableDownvotes(this.state.siteRes)}
-                    enableNsfw={enableNsfw(this.state.siteRes)}
-                    view={this.state.view}
-                    onPageChange={this.handlePageChange}
-                    allLanguages={this.state.siteRes.all_languages}
-                    siteLanguages={this.state.siteRes.discussion_languages}
+          res && (
+            <div className="row">
+              <div className="col-12 col-md-8">
+                <>
+                  <HtmlTags
+                    title={this.documentTitle}
+                    path={this.context.router.route.match.url}
+                    description={res.person_view.person.bio}
+                    image={res.person_view.person.avatar}
                   />
-                </div>
-
-                {!this.state.loading && (
-                  <div className="col-12 col-md-4">
-                    {this.moderates()}
-                    {this.amCurrentUser && this.follows()}
-                  </div>
-                )}
+                  {this.userInfo()}
+                  <hr />
+                </>
+                {!this.state.loading && this.selects()}
+                <PersonDetails
+                  personRes={res}
+                  admins={this.state.siteRes.admins}
+                  sort={this.state.sort}
+                  page={this.state.page}
+                  limit={fetchLimit}
+                  enableDownvotes={enableDownvotes(this.state.siteRes)}
+                  enableNsfw={enableNsfw(this.state.siteRes)}
+                  view={this.state.view}
+                  onPageChange={this.handlePageChange}
+                  allLanguages={this.state.siteRes.all_languages}
+                  siteLanguages={this.state.siteRes.discussion_languages}
+                />
               </div>
-            ),
-            none: <></>,
-          })
+
+              {!this.state.loading && (
+                <div className="col-12 col-md-4">
+                  {this.moderates()}
+                  {this.amCurrentUser && this.follows()}
+                </div>
+              )}
+            </div>
+          )
         )}
       </div>
     );
@@ -377,349 +355,316 @@ export class Profile extends Component<any, ProfileState> {
     );
   }
   handleBlockPerson(personId: number) {
-    if (personId != 0) {
-      let blockUserForm = new BlockPerson({
-        person_id: personId,
-        block: true,
-        auth: auth().unwrap(),
-      });
-      WebSocketService.Instance.send(wsClient.blockPerson(blockUserForm));
+    let auth = myAuth();
+    if (auth) {
+      if (personId != 0) {
+        let blockUserForm: BlockPerson = {
+          person_id: personId,
+          block: true,
+          auth,
+        };
+        WebSocketService.Instance.send(wsClient.blockPerson(blockUserForm));
+      }
     }
   }
   handleUnblockPerson(recipientId: number) {
-    let blockUserForm = new BlockPerson({
-      person_id: recipientId,
-      block: false,
-      auth: auth().unwrap(),
-    });
-    WebSocketService.Instance.send(wsClient.blockPerson(blockUserForm));
+    let auth = myAuth();
+    if (auth) {
+      let blockUserForm: BlockPerson = {
+        person_id: recipientId,
+        block: false,
+        auth,
+      };
+      WebSocketService.Instance.send(wsClient.blockPerson(blockUserForm));
+    }
   }
 
   userInfo() {
-    return this.state.personRes
-      .map(r => r.person_view)
-      .match({
-        some: pv => (
-          <div>
-            <BannerIconHeader
-              banner={pv.person.banner}
-              icon={pv.person.avatar}
-            />
-            <div className="mb-3">
-              <div className="">
-                <div className="mb-0 d-flex flex-wrap">
-                  <div>
-                    {pv.person.display_name.match({
-                      some: displayName => (
-                        <h5 className="mb-0">{displayName}</h5>
-                      ),
-                      none: <></>,
-                    })}
-                    <ul className="list-inline mb-2">
-                      <li className="list-inline-item">
-                        <PersonListing
-                          person={pv.person}
-                          realLink
-                          useApubName
-                          muted
-                          hideAvatar
-                        />
+    let pv = this.state.personRes?.person_view;
+    return (
+      pv && (
+        <div>
+          <BannerIconHeader banner={pv.person.banner} icon={pv.person.avatar} />
+          <div className="mb-3">
+            <div className="">
+              <div className="mb-0 d-flex flex-wrap">
+                <div>
+                  {pv.person.display_name && (
+                    <h5 className="mb-0">{pv.person.display_name}</h5>
+                  )}
+                  <ul className="list-inline mb-2">
+                    <li className="list-inline-item">
+                      <PersonListing
+                        person={pv.person}
+                        realLink
+                        useApubName
+                        muted
+                        hideAvatar
+                      />
+                    </li>
+                    {isBanned(pv.person) && (
+                      <li className="list-inline-item badge badge-danger">
+                        {i18n.t("banned")}
                       </li>
-                      {isBanned(pv.person) && (
-                        <li className="list-inline-item badge badge-danger">
-                          {i18n.t("banned")}
-                        </li>
-                      )}
-                      {pv.person.deleted && (
-                        <li className="list-inline-item badge badge-danger">
-                          {i18n.t("deleted")}
-                        </li>
-                      )}
-                      {pv.person.admin && (
-                        <li className="list-inline-item badge badge-light">
-                          {i18n.t("admin")}
-                        </li>
-                      )}
-                      {pv.person.bot_account && (
-                        <li className="list-inline-item badge badge-light">
-                          {i18n.t("bot_account").toLowerCase()}
-                        </li>
-                      )}
-                    </ul>
-                  </div>
-                  {this.banDialog()}
-                  <div className="flex-grow-1 unselectable pointer mx-2"></div>
-                  {!this.amCurrentUser &&
-                    UserService.Instance.myUserInfo.isSome() && (
-                      <>
-                        <a
-                          className={`d-flex align-self-start btn btn-secondary mr-2 ${
-                            !pv.person.matrix_user_id && "invisible"
-                          }`}
-                          rel={relTags}
-                          href={`https://matrix.to/#/${pv.person.matrix_user_id}`}
-                        >
-                          {i18n.t("send_secure_message")}
-                        </a>
-                        <Link
-                          className={
-                            "d-flex align-self-start btn btn-secondary mr-2"
-                          }
-                          to={`/create_private_message/recipient/${pv.person.id}`}
-                        >
-                          {i18n.t("send_message")}
-                        </Link>
-                        {this.state.personBlocked ? (
-                          <button
-                            className={
-                              "d-flex align-self-start btn btn-secondary mr-2"
-                            }
-                            onClick={linkEvent(
-                              pv.person.id,
-                              this.handleUnblockPerson
-                            )}
-                          >
-                            {i18n.t("unblock_user")}
-                          </button>
-                        ) : (
-                          <button
-                            className={
-                              "d-flex align-self-start btn btn-secondary mr-2"
-                            }
-                            onClick={linkEvent(
-                              pv.person.id,
-                              this.handleBlockPerson
-                            )}
-                          >
-                            {i18n.t("block_user")}
-                          </button>
-                        )}
-                      </>
                     )}
-
-                  {canMod(
-                    None,
-                    Some(this.state.siteRes.admins),
-                    pv.person.id
-                  ) &&
-                    !isAdmin(Some(this.state.siteRes.admins), pv.person.id) &&
-                    !this.state.showBanDialog &&
-                    (!isBanned(pv.person) ? (
+                    {pv.person.deleted && (
+                      <li className="list-inline-item badge badge-danger">
+                        {i18n.t("deleted")}
+                      </li>
+                    )}
+                    {pv.person.admin && (
+                      <li className="list-inline-item badge badge-light">
+                        {i18n.t("admin")}
+                      </li>
+                    )}
+                    {pv.person.bot_account && (
+                      <li className="list-inline-item badge badge-light">
+                        {i18n.t("bot_account").toLowerCase()}
+                      </li>
+                    )}
+                  </ul>
+                </div>
+                {this.banDialog()}
+                <div className="flex-grow-1 unselectable pointer mx-2"></div>
+                {!this.amCurrentUser && UserService.Instance.myUserInfo && (
+                  <>
+                    <a
+                      className={`d-flex align-self-start btn btn-secondary mr-2 ${
+                        !pv.person.matrix_user_id && "invisible"
+                      }`}
+                      rel={relTags}
+                      href={`https://matrix.to/#/${pv.person.matrix_user_id}`}
+                    >
+                      {i18n.t("send_secure_message")}
+                    </a>
+                    <Link
+                      className={
+                        "d-flex align-self-start btn btn-secondary mr-2"
+                      }
+                      to={`/create_private_message/recipient/${pv.person.id}`}
+                    >
+                      {i18n.t("send_message")}
+                    </Link>
+                    {this.state.personBlocked ? (
                       <button
                         className={
                           "d-flex align-self-start btn btn-secondary mr-2"
                         }
-                        onClick={linkEvent(this, this.handleModBanShow)}
-                        aria-label={i18n.t("ban")}
+                        onClick={linkEvent(
+                          pv.person.id,
+                          this.handleUnblockPerson
+                        )}
                       >
-                        {capitalizeFirstLetter(i18n.t("ban"))}
+                        {i18n.t("unblock_user")}
                       </button>
                     ) : (
                       <button
                         className={
                           "d-flex align-self-start btn btn-secondary mr-2"
                         }
-                        onClick={linkEvent(this, this.handleModBanSubmit)}
-                        aria-label={i18n.t("unban")}
+                        onClick={linkEvent(
+                          pv.person.id,
+                          this.handleBlockPerson
+                        )}
                       >
-                        {capitalizeFirstLetter(i18n.t("unban"))}
+                        {i18n.t("block_user")}
                       </button>
-                    ))}
-                </div>
-                {pv.person.bio.match({
-                  some: bio => (
-                    <div className="d-flex align-items-center mb-2">
-                      <div
-                        className="md-div"
-                        dangerouslySetInnerHTML={mdToHtml(bio)}
-                      />
-                    </div>
-                  ),
-                  none: <></>,
-                })}
-                <div>
-                  <ul className="list-inline mb-2">
-                    <li className="list-inline-item badge badge-light">
-                      {i18n.t("number_of_posts", {
-                        count: pv.counts.post_count,
-                        formattedCount: numToSI(pv.counts.post_count),
-                      })}
-                    </li>
-                    <li className="list-inline-item badge badge-light">
-                      {i18n.t("number_of_comments", {
-                        count: pv.counts.comment_count,
-                        formattedCount: numToSI(pv.counts.comment_count),
-                      })}
-                    </li>
-                  </ul>
-                </div>
-                <div className="text-muted">
-                  {i18n.t("joined")}{" "}
-                  <MomentTime
-                    published={pv.person.published}
-                    updated={None}
-                    showAgo
-                    ignoreUpdated
+                    )}
+                  </>
+                )}
+
+                {canMod(pv.person.id, undefined, this.state.siteRes.admins) &&
+                  !isAdmin(pv.person.id, this.state.siteRes.admins) &&
+                  !this.state.showBanDialog &&
+                  (!isBanned(pv.person) ? (
+                    <button
+                      className={
+                        "d-flex align-self-start btn btn-secondary mr-2"
+                      }
+                      onClick={linkEvent(this, this.handleModBanShow)}
+                      aria-label={i18n.t("ban")}
+                    >
+                      {capitalizeFirstLetter(i18n.t("ban"))}
+                    </button>
+                  ) : (
+                    <button
+                      className={
+                        "d-flex align-self-start btn btn-secondary mr-2"
+                      }
+                      onClick={linkEvent(this, this.handleModBanSubmit)}
+                      aria-label={i18n.t("unban")}
+                    >
+                      {capitalizeFirstLetter(i18n.t("unban"))}
+                    </button>
+                  ))}
+              </div>
+              {pv.person.bio && (
+                <div className="d-flex align-items-center mb-2">
+                  <div
+                    className="md-div"
+                    dangerouslySetInnerHTML={mdToHtml(pv.person.bio)}
                   />
                 </div>
-                <div className="d-flex align-items-center text-muted mb-2">
-                  <Icon icon="cake" />
-                  <span className="ml-2">
-                    {i18n.t("cake_day_title")}{" "}
-                    {moment
-                      .utc(pv.person.published)
-                      .local()
-                      .format("MMM DD, YYYY")}
-                  </span>
-                </div>
+              )}
+              <div>
+                <ul className="list-inline mb-2">
+                  <li className="list-inline-item badge badge-light">
+                    {i18n.t("number_of_posts", {
+                      count: pv.counts.post_count,
+                      formattedCount: numToSI(pv.counts.post_count),
+                    })}
+                  </li>
+                  <li className="list-inline-item badge badge-light">
+                    {i18n.t("number_of_comments", {
+                      count: pv.counts.comment_count,
+                      formattedCount: numToSI(pv.counts.comment_count),
+                    })}
+                  </li>
+                </ul>
+              </div>
+              <div className="text-muted">
+                {i18n.t("joined")}{" "}
+                <MomentTime
+                  published={pv.person.published}
+                  showAgo
+                  ignoreUpdated
+                />
+              </div>
+              <div className="d-flex align-items-center text-muted mb-2">
+                <Icon icon="cake" />
+                <span className="ml-2">
+                  {i18n.t("cake_day_title")}{" "}
+                  {moment
+                    .utc(pv.person.published)
+                    .local()
+                    .format("MMM DD, YYYY")}
+                </span>
               </div>
             </div>
           </div>
-        ),
-        none: <></>,
-      });
+        </div>
+      )
+    );
   }
 
   banDialog() {
-    return this.state.personRes
-      .map(r => r.person_view)
-      .match({
-        some: pv => (
-          <>
-            {this.state.showBanDialog && (
-              <form onSubmit={linkEvent(this, this.handleModBanSubmit)}>
-                <div className="form-group row col-12">
-                  <label
-                    className="col-form-label"
-                    htmlFor="profile-ban-reason"
-                  >
-                    {i18n.t("reason")}
-                  </label>
-                  <input
-                    type="text"
-                    id="profile-ban-reason"
-                    className="form-control mr-2"
-                    placeholder={i18n.t("reason")}
-                    value={toUndefined(this.state.banReason)}
-                    onInput={linkEvent(this, this.handleModBanReasonChange)}
-                  />
-                  <label className="col-form-label" htmlFor={`mod-ban-expires`}>
-                    {i18n.t("expires")}
-                  </label>
-                  <input
-                    type="number"
-                    id={`mod-ban-expires`}
-                    className="form-control mr-2"
-                    placeholder={i18n.t("number_of_days")}
-                    value={toUndefined(this.state.banExpireDays)}
-                    onInput={linkEvent(this, this.handleModBanExpireDaysChange)}
-                  />
-                  <div className="form-group">
-                    <div className="form-check">
-                      <input
-                        className="form-check-input"
-                        id="mod-ban-remove-data"
-                        type="checkbox"
-                        checked={this.state.removeData}
-                        onChange={linkEvent(
-                          this,
-                          this.handleModRemoveDataChange
-                        )}
-                      />
-                      <label
-                        className="form-check-label"
-                        htmlFor="mod-ban-remove-data"
-                        title={i18n.t("remove_content_more")}
-                      >
-                        {i18n.t("remove_content")}
-                      </label>
-                    </div>
+    let pv = this.state.personRes?.person_view;
+    return (
+      pv && (
+        <>
+          {this.state.showBanDialog && (
+            <form onSubmit={linkEvent(this, this.handleModBanSubmit)}>
+              <div className="form-group row col-12">
+                <label className="col-form-label" htmlFor="profile-ban-reason">
+                  {i18n.t("reason")}
+                </label>
+                <input
+                  type="text"
+                  id="profile-ban-reason"
+                  className="form-control mr-2"
+                  placeholder={i18n.t("reason")}
+                  value={this.state.banReason}
+                  onInput={linkEvent(this, this.handleModBanReasonChange)}
+                />
+                <label className="col-form-label" htmlFor={`mod-ban-expires`}>
+                  {i18n.t("expires")}
+                </label>
+                <input
+                  type="number"
+                  id={`mod-ban-expires`}
+                  className="form-control mr-2"
+                  placeholder={i18n.t("number_of_days")}
+                  value={this.state.banExpireDays}
+                  onInput={linkEvent(this, this.handleModBanExpireDaysChange)}
+                />
+                <div className="form-group">
+                  <div className="form-check">
+                    <input
+                      className="form-check-input"
+                      id="mod-ban-remove-data"
+                      type="checkbox"
+                      checked={this.state.removeData}
+                      onChange={linkEvent(this, this.handleModRemoveDataChange)}
+                    />
+                    <label
+                      className="form-check-label"
+                      htmlFor="mod-ban-remove-data"
+                      title={i18n.t("remove_content_more")}
+                    >
+                      {i18n.t("remove_content")}
+                    </label>
                   </div>
                 </div>
-                {/* TODO hold off on expires until later */}
-                {/* <div class="form-group row"> */}
-                {/*   <label class="col-form-label">Expires</label> */}
-                {/*   <input type="date" class="form-control mr-2" placeholder={i18n.t('expires')} value={this.state.banExpires} onInput={linkEvent(this, this.handleModBanExpiresChange)} /> */}
-                {/* </div> */}
-                <div className="form-group row">
-                  <button
-                    type="reset"
-                    className="btn btn-secondary mr-2"
-                    aria-label={i18n.t("cancel")}
-                    onClick={linkEvent(this, this.handleModBanSubmitCancel)}
-                  >
-                    {i18n.t("cancel")}
-                  </button>
-                  <button
-                    type="submit"
-                    className="btn btn-secondary"
-                    aria-label={i18n.t("ban")}
-                  >
-                    {i18n.t("ban")} {pv.person.name}
-                  </button>
-                </div>
-              </form>
-            )}
-          </>
-        ),
-        none: <></>,
-      });
+              </div>
+              {/* TODO hold off on expires until later */}
+              {/* <div class="form-group row"> */}
+              {/*   <label class="col-form-label">Expires</label> */}
+              {/*   <input type="date" class="form-control mr-2" placeholder={i18n.t('expires')} value={this.state.banExpires} onInput={linkEvent(this, this.handleModBanExpiresChange)} /> */}
+              {/* </div> */}
+              <div className="form-group row">
+                <button
+                  type="reset"
+                  className="btn btn-secondary mr-2"
+                  aria-label={i18n.t("cancel")}
+                  onClick={linkEvent(this, this.handleModBanSubmitCancel)}
+                >
+                  {i18n.t("cancel")}
+                </button>
+                <button
+                  type="submit"
+                  className="btn btn-secondary"
+                  aria-label={i18n.t("ban")}
+                >
+                  {i18n.t("ban")} {pv.person.name}
+                </button>
+              </div>
+            </form>
+          )}
+        </>
+      )
+    );
   }
 
   moderates() {
-    return this.state.personRes
-      .map(r => r.moderates)
-      .match({
-        some: moderates => {
-          if (moderates.length > 0) {
-            return (
-              <div className="card border-secondary mb-3">
-                <div className="card-body">
-                  <h5>{i18n.t("moderates")}</h5>
-                  <ul className="list-unstyled mb-0">
-                    {moderates.map(cmv => (
-                      <li key={cmv.community.id}>
-                        <CommunityLink community={cmv.community} />
-                      </li>
-                    ))}
-                  </ul>
-                </div>
-              </div>
-            );
-          } else {
-            return <></>;
-          }
-        },
-        none: void 0,
-      });
+    let moderates = this.state.personRes?.moderates;
+    return (
+      moderates &&
+      moderates.length > 0 && (
+        <div className="card border-secondary mb-3">
+          <div className="card-body">
+            <h5>{i18n.t("moderates")}</h5>
+            <ul className="list-unstyled mb-0">
+              {moderates.map(cmv => (
+                <li key={cmv.community.id}>
+                  <CommunityLink community={cmv.community} />
+                </li>
+              ))}
+            </ul>
+          </div>
+        </div>
+      )
+    );
   }
 
   follows() {
-    return UserService.Instance.myUserInfo
-      .map(m => m.follows)
-      .match({
-        some: follows => {
-          if (follows.length > 0) {
-            return (
-              <div className="card border-secondary mb-3">
-                <div className="card-body">
-                  <h5>{i18n.t("subscribed")}</h5>
-                  <ul className="list-unstyled mb-0">
-                    {follows.map(cfv => (
-                      <li key={cfv.community.id}>
-                        <CommunityLink community={cfv.community} />
-                      </li>
-                    ))}
-                  </ul>
-                </div>
-              </div>
-            );
-          } else {
-            return <></>;
-          }
-        },
-        none: void 0,
-      });
+    let follows = UserService.Instance.myUserInfo?.follows;
+    return (
+      follows &&
+      follows.length > 0 && (
+        <div className="card border-secondary mb-3">
+          <div className="card-body">
+            <h5>{i18n.t("subscribed")}</h5>
+            <ul className="list-unstyled mb-0">
+              {follows.map(cfv => (
+                <li key={cfv.community.id}>
+                  <CommunityLink community={cfv.community} />
+                </li>
+              ))}
+            </ul>
+          </div>
+        </div>
+      )
+    );
   }
 
   updateUrl(paramUpdates: UrlParams) {
@@ -774,30 +719,26 @@ export class Profile extends Component<any, ProfileState> {
 
   handleModBanSubmit(i: Profile, event?: any) {
     if (event) event.preventDefault();
+    let person = i.state.personRes?.person_view.person;
+    let auth = myAuth();
+    if (person && auth) {
+      // If its an unban, restore all their data
+      let ban = !person.banned;
+      if (ban == false) {
+        i.setState({ removeData: false });
+      }
+      let form: BanPerson = {
+        person_id: person.id,
+        ban,
+        remove_data: i.state.removeData,
+        reason: i.state.banReason,
+        expires: futureDaysToUnixTime(i.state.banExpireDays),
+        auth,
+      };
+      WebSocketService.Instance.send(wsClient.banPerson(form));
 
-    i.state.personRes
-      .map(r => r.person_view.person)
-      .match({
-        some: person => {
-          // If its an unban, restore all their data
-          let ban = !person.banned;
-          if (ban == false) {
-            i.setState({ removeData: false });
-          }
-          let form = new BanPerson({
-            person_id: person.id,
-            ban,
-            remove_data: Some(i.state.removeData),
-            reason: i.state.banReason,
-            expires: i.state.banExpireDays.map(futureDaysToUnixTime),
-            auth: auth().unwrap(),
-          });
-          WebSocketService.Instance.send(wsClient.banPerson(form));
-
-          i.setState({ showBanDialog: false });
-        },
-        none: void 0,
-      });
+      i.setState({ showBanDialog: false });
+    }
   }
 
   parseMessage(msg: any) {
@@ -815,50 +756,34 @@ export class Profile extends Component<any, ProfileState> {
       // Since the PersonDetails 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
-      let data = wsJsonToRes<GetPersonDetailsResponse>(
-        msg,
-        GetPersonDetailsResponse
-      );
-      this.setState({ personRes: Some(data), loading: false });
+      let data = wsJsonToRes<GetPersonDetailsResponse>(msg);
+      this.setState({ personRes: data, loading: false });
       this.setPersonBlock();
       restoreScrollPosition(this.context);
     } else if (op == UserOperation.AddAdmin) {
-      let data = wsJsonToRes<AddAdminResponse>(msg, AddAdminResponse);
+      let data = wsJsonToRes<AddAdminResponse>(msg);
       this.setState(s => ((s.siteRes.admins = data.admins), s));
     } else if (op == UserOperation.CreateCommentLike) {
-      let data = wsJsonToRes<CommentResponse>(msg, CommentResponse);
-      createCommentLikeRes(
-        data.comment_view,
-        this.state.personRes.map(r => r.comments).unwrapOr([])
-      );
+      let data = wsJsonToRes<CommentResponse>(msg);
+      createCommentLikeRes(data.comment_view, this.state.personRes?.comments);
       this.setState(this.state);
     } else if (
       op == UserOperation.EditComment ||
       op == UserOperation.DeleteComment ||
       op == UserOperation.RemoveComment
     ) {
-      let data = wsJsonToRes<CommentResponse>(msg, CommentResponse);
-      editCommentRes(
-        data.comment_view,
-        this.state.personRes.map(r => r.comments).unwrapOr([])
-      );
+      let data = wsJsonToRes<CommentResponse>(msg);
+      editCommentRes(data.comment_view, this.state.personRes?.comments);
       this.setState(this.state);
     } else if (op == UserOperation.CreateComment) {
-      let data = wsJsonToRes<CommentResponse>(msg, CommentResponse);
-      UserService.Instance.myUserInfo.match({
-        some: mui => {
-          if (data.comment_view.creator.id == mui.local_user_view.person.id) {
-            toast(i18n.t("reply_sent"));
-          }
-        },
-        none: void 0,
-      });
+      let data = wsJsonToRes<CommentResponse>(msg);
+      let mui = UserService.Instance.myUserInfo;
+      if (data.comment_view.creator.id == mui?.local_user_view.person.id) {
+        toast(i18n.t("reply_sent"));
+      }
     } else if (op == UserOperation.SaveComment) {
-      let data = wsJsonToRes<CommentResponse>(msg, CommentResponse);
-      saveCommentRes(
-        data.comment_view,
-        this.state.personRes.map(r => r.comments).unwrapOr([])
-      );
+      let data = wsJsonToRes<CommentResponse>(msg);
+      saveCommentRes(data.comment_view, this.state.personRes?.comments);
       this.setState(this.state);
     } else if (
       op == UserOperation.EditPost ||
@@ -868,40 +793,30 @@ export class Profile extends Component<any, ProfileState> {
       op == UserOperation.FeaturePost ||
       op == UserOperation.SavePost
     ) {
-      let data = wsJsonToRes<PostResponse>(msg, PostResponse);
-      editPostFindRes(
-        data.post_view,
-        this.state.personRes.map(r => r.posts).unwrapOr([])
-      );
+      let data = wsJsonToRes<PostResponse>(msg);
+      editPostFindRes(data.post_view, this.state.personRes?.posts);
       this.setState(this.state);
     } else if (op == UserOperation.CreatePostLike) {
-      let data = wsJsonToRes<PostResponse>(msg, PostResponse);
-      createPostLikeFindRes(
-        data.post_view,
-        this.state.personRes.map(r => r.posts).unwrapOr([])
-      );
+      let data = wsJsonToRes<PostResponse>(msg);
+      createPostLikeFindRes(data.post_view, this.state.personRes?.posts);
       this.setState(this.state);
     } else if (op == UserOperation.BanPerson) {
-      let data = wsJsonToRes<BanPersonResponse>(msg, BanPersonResponse);
-      this.state.personRes.match({
-        some: res => {
-          res.comments
-            .filter(c => c.creator.id == data.person_view.person.id)
-            .forEach(c => (c.creator.banned = data.banned));
-          res.posts
-            .filter(c => c.creator.id == data.person_view.person.id)
-            .forEach(c => (c.creator.banned = data.banned));
-          let pv = res.person_view;
-
-          if (pv.person.id == data.person_view.person.id) {
-            pv.person.banned = data.banned;
-          }
-          this.setState(this.state);
-        },
-        none: void 0,
-      });
+      let data = wsJsonToRes<BanPersonResponse>(msg);
+      let res = this.state.personRes;
+      res?.comments
+        .filter(c => c.creator.id == data.person_view.person.id)
+        .forEach(c => (c.creator.banned = data.banned));
+      res?.posts
+        .filter(c => c.creator.id == data.person_view.person.id)
+        .forEach(c => (c.creator.banned = data.banned));
+      let pv = res?.person_view;
+
+      if (pv?.person.id == data.person_view.person.id) {
+        pv.person.banned = data.banned;
+      }
+      this.setState(this.state);
     } else if (op == UserOperation.BlockPerson) {
-      let data = wsJsonToRes<BlockPersonResponse>(msg, BlockPersonResponse);
+      let data = wsJsonToRes<BlockPersonResponse>(msg);
       updatePersonBlock(data);
       this.setPersonBlock();
       this.setState(this.state);
@@ -911,7 +826,7 @@ export class Profile extends Component<any, ProfileState> {
       op == UserOperation.PurgeComment ||
       op == UserOperation.PurgeCommunity
     ) {
-      let data = wsJsonToRes<PurgeItemResponse>(msg, PurgeItemResponse);
+      let data = wsJsonToRes<PurgeItemResponse>(msg);
       if (data.success) {
         toast(i18n.t("purge_success"));
         this.context.router.history.push(`/`);
index e5235082fb70993143223a49025d74e18220a884..181674121ba1cd81b73ff1820e31e544cb889784 100644 (file)
@@ -1,4 +1,3 @@
-import { None, Option, Some } from "@sniptt/monads";
 import { Component, linkEvent } from "inferno";
 import {
   GetSiteResponse,
@@ -14,9 +13,9 @@ import { i18n } from "../../i18next";
 import { InitialFetchRequest } from "../../interfaces";
 import { UserService, WebSocketService } from "../../services";
 import {
-  auth,
   fetchLimit,
   isBrowser,
+  myAuth,
   setIsoData,
   setupTippy,
   toast,
@@ -35,7 +34,7 @@ enum UnreadOrAll {
 }
 
 interface RegistrationApplicationsState {
-  listRegistrationApplicationsResponse: Option<ListRegistrationApplicationsResponse>;
+  listRegistrationApplicationsResponse?: ListRegistrationApplicationsResponse;
   siteRes: GetSiteResponse;
   unreadOrAll: UnreadOrAll;
   page: number;
@@ -46,13 +45,9 @@ export class RegistrationApplications extends Component<
   any,
   RegistrationApplicationsState
 > {
-  private isoData = setIsoData(
-    this.context,
-    ListRegistrationApplicationsResponse
-  );
-  private subscription: Subscription;
-  private emptyState: RegistrationApplicationsState = {
-    listRegistrationApplicationsResponse: None,
+  private isoData = setIsoData(this.context);
+  private subscription?: Subscription;
+  state: RegistrationApplicationsState = {
     siteRes: this.isoData.site_res,
     unreadOrAll: UnreadOrAll.Unread,
     page: 1,
@@ -62,10 +57,9 @@ export class RegistrationApplications extends Component<
   constructor(props: any, context: any) {
     super(props, context);
 
-    this.state = this.emptyState;
     this.handlePageChange = this.handlePageChange.bind(this);
 
-    if (UserService.Instance.myUserInfo.isNone() && isBrowser()) {
+    if (!UserService.Instance.myUserInfo && isBrowser()) {
       toast(i18n.t("not_logged_in"), "danger");
       this.context.router.history.push(`/login`);
     }
@@ -77,9 +71,8 @@ export class RegistrationApplications extends Component<
     if (this.isoData.path == this.context.router.route.match.url) {
       this.state = {
         ...this.state,
-        listRegistrationApplicationsResponse: Some(
-          this.isoData.routeData[0] as ListRegistrationApplicationsResponse
-        ),
+        listRegistrationApplicationsResponse: this.isoData
+          .routeData[0] as ListRegistrationApplicationsResponse,
         loading: false,
       };
     } else {
@@ -93,18 +86,17 @@ export class RegistrationApplications extends Component<
 
   componentWillUnmount() {
     if (isBrowser()) {
-      this.subscription.unsubscribe();
+      this.subscription?.unsubscribe();
     }
   }
 
   get documentTitle(): string {
-    return UserService.Instance.myUserInfo.match({
-      some: mui =>
-        `@${mui.local_user_view.person.name} ${i18n.t(
+    let mui = UserService.Instance.myUserInfo;
+    return mui
+      ? `@${mui.local_user_view.person.name} ${i18n.t(
           "registration_applications"
-        )} - ${this.state.siteRes.site_view.site.name}`,
-      none: "",
-    });
+        )} - ${this.state.siteRes.site_view.site.name}`
+      : "";
   }
 
   render() {
@@ -120,8 +112,6 @@ export class RegistrationApplications extends Component<
               <HtmlTags
                 title={this.documentTitle}
                 path={this.context.router.route.match.url}
-                description={None}
-                image={None}
               />
               <h5 className="mb-2">{i18n.t("registration_applications")}</h5>
               {this.selects()}
@@ -179,8 +169,9 @@ export class RegistrationApplications extends Component<
   }
 
   applicationList() {
-    return this.state.listRegistrationApplicationsResponse.match({
-      some: res => (
+    let res = this.state.listRegistrationApplicationsResponse;
+    return (
+      res && (
         <div>
           {res.registration_applications.map(ra => (
             <>
@@ -192,9 +183,8 @@ export class RegistrationApplications extends Component<
             </>
           ))}
         </div>
-      ),
-      none: <></>,
-    });
+      )
+    );
   }
 
   handleUnreadOrAllChange(i: RegistrationApplications, event: any) {
@@ -210,26 +200,34 @@ export class RegistrationApplications extends Component<
   static fetchInitialData(req: InitialFetchRequest): Promise<any>[] {
     let promises: Promise<any>[] = [];
 
-    let form = new ListRegistrationApplications({
-      unread_only: Some(true),
-      page: Some(1),
-      limit: Some(fetchLimit),
-      auth: req.auth.unwrap(),
-    });
-    promises.push(req.client.listRegistrationApplications(form));
+    let auth = req.auth;
+    if (auth) {
+      let form: ListRegistrationApplications = {
+        unread_only: true,
+        page: 1,
+        limit: fetchLimit,
+        auth,
+      };
+      promises.push(req.client.listRegistrationApplications(form));
+    }
 
     return promises;
   }
 
   refetch() {
     let unread_only = this.state.unreadOrAll == UnreadOrAll.Unread;
-    let form = new ListRegistrationApplications({
-      unread_only: Some(unread_only),
-      page: Some(this.state.page),
-      limit: Some(fetchLimit),
-      auth: auth().unwrap(),
-    });
-    WebSocketService.Instance.send(wsClient.listRegistrationApplications(form));
+    let auth = myAuth();
+    if (auth) {
+      let form: ListRegistrationApplications = {
+        unread_only: unread_only,
+        page: this.state.page,
+        limit: fetchLimit,
+        auth,
+      };
+      WebSocketService.Instance.send(
+        wsClient.listRegistrationApplications(form)
+      );
+    }
   }
 
   parseMessage(msg: any) {
@@ -241,25 +239,18 @@ export class RegistrationApplications extends Component<
     } else if (msg.reconnect) {
       this.refetch();
     } else if (op == UserOperation.ListRegistrationApplications) {
-      let data = wsJsonToRes<ListRegistrationApplicationsResponse>(
-        msg,
-        ListRegistrationApplicationsResponse
-      );
+      let data = wsJsonToRes<ListRegistrationApplicationsResponse>(msg);
       this.setState({
-        listRegistrationApplicationsResponse: Some(data),
+        listRegistrationApplicationsResponse: data,
         loading: false,
       });
       window.scrollTo(0, 0);
     } else if (op == UserOperation.ApproveRegistrationApplication) {
-      let data = wsJsonToRes<RegistrationApplicationResponse>(
-        msg,
-        RegistrationApplicationResponse
-      );
+      let data = wsJsonToRes<RegistrationApplicationResponse>(msg);
       updateRegistrationApplicationRes(
         data.registration_application,
         this.state.listRegistrationApplicationsResponse
-          .map(r => r.registration_applications)
-          .unwrapOr([])
+          ?.registration_applications
       );
       let uacs = UserService.Instance.unreadApplicationCountSub;
       // Minor bug, where if the application switches from deny to approve, the count will still go down
index f28188e71f487395cdeb52d0c09856384a43db87..0af56b53bf13809dad421ebb798b109a09f5338a 100644 (file)
@@ -1,4 +1,3 @@
-import { None, Option, Some } from "@sniptt/monads";
 import { Component, linkEvent } from "inferno";
 import {
   CommentReportResponse,
@@ -24,9 +23,9 @@ import { InitialFetchRequest } from "../../interfaces";
 import { UserService, WebSocketService } from "../../services";
 import {
   amAdmin,
-  auth,
   fetchLimit,
   isBrowser,
+  myAuth,
   setIsoData,
   setupTippy,
   toast,
@@ -69,9 +68,9 @@ type ItemType = {
 };
 
 interface ReportsState {
-  listCommentReportsResponse: Option<ListCommentReportsResponse>;
-  listPostReportsResponse: Option<ListPostReportsResponse>;
-  listPrivateMessageReportsResponse: Option<ListPrivateMessageReportsResponse>;
+  listCommentReportsResponse?: ListCommentReportsResponse;
+  listPostReportsResponse?: ListPostReportsResponse;
+  listPrivateMessageReportsResponse?: ListPrivateMessageReportsResponse;
   unreadOrAll: UnreadOrAll;
   messageType: MessageType;
   combined: ItemType[];
@@ -81,17 +80,9 @@ interface ReportsState {
 }
 
 export class Reports extends Component<any, ReportsState> {
-  private isoData = setIsoData(
-    this.context,
-    ListCommentReportsResponse,
-    ListPostReportsResponse,
-    ListPrivateMessageReportsResponse
-  );
-  private subscription: Subscription;
-  private emptyState: ReportsState = {
-    listCommentReportsResponse: None,
-    listPostReportsResponse: None,
-    listPrivateMessageReportsResponse: None,
+  private isoData = setIsoData(this.context);
+  private subscription?: Subscription;
+  state: ReportsState = {
     unreadOrAll: UnreadOrAll.Unread,
     messageType: MessageType.All,
     combined: [],
@@ -103,10 +94,9 @@ export class Reports extends Component<any, ReportsState> {
   constructor(props: any, context: any) {
     super(props, context);
 
-    this.state = this.emptyState;
     this.handlePageChange = this.handlePageChange.bind(this);
 
-    if (UserService.Instance.myUserInfo.isNone() && isBrowser()) {
+    if (!UserService.Instance.myUserInfo && isBrowser()) {
       toast(i18n.t("not_logged_in"), "danger");
       this.context.router.history.push(`/login`);
     }
@@ -118,19 +108,16 @@ export class Reports extends Component<any, ReportsState> {
     if (this.isoData.path == this.context.router.route.match.url) {
       this.state = {
         ...this.state,
-        listCommentReportsResponse: Some(
-          this.isoData.routeData[0] as ListCommentReportsResponse
-        ),
-        listPostReportsResponse: Some(
-          this.isoData.routeData[1] as ListPostReportsResponse
-        ),
+        listCommentReportsResponse: this.isoData
+          .routeData[0] as ListCommentReportsResponse,
+        listPostReportsResponse: this.isoData
+          .routeData[1] as ListPostReportsResponse,
       };
       if (amAdmin()) {
         this.state = {
           ...this.state,
-          listPrivateMessageReportsResponse: Some(
-            this.isoData.routeData[2] as ListPrivateMessageReportsResponse
-          ),
+          listPrivateMessageReportsResponse: this.isoData
+            .routeData[2] as ListPrivateMessageReportsResponse,
         };
       }
       this.state = {
@@ -145,18 +132,17 @@ export class Reports extends Component<any, ReportsState> {
 
   componentWillUnmount() {
     if (isBrowser()) {
-      this.subscription.unsubscribe();
+      this.subscription?.unsubscribe();
     }
   }
 
   get documentTitle(): string {
-    return UserService.Instance.myUserInfo.match({
-      some: mui =>
-        `@${mui.local_user_view.person.name} ${i18n.t("reports")} - ${
+    let mui = UserService.Instance.myUserInfo;
+    return mui
+      ? `@${mui.local_user_view.person.name} ${i18n.t("reports")} - ${
           this.state.siteRes.site_view.site.name
-        }`,
-      none: "",
-    });
+        }`
+      : "";
   }
 
   render() {
@@ -172,8 +158,6 @@ export class Reports extends Component<any, ReportsState> {
               <HtmlTags
                 title={this.documentTitle}
                 path={this.context.router.route.match.url}
-                description={None}
-                image={None}
               />
               <h5 className="mb-2">{i18n.t("reports")}</h5>
               {this.selects()}
@@ -331,19 +315,22 @@ export class Reports extends Component<any, ReportsState> {
   }
 
   buildCombined(): ItemType[] {
-    let comments: ItemType[] = this.state.listCommentReportsResponse
-      .map(r => r.comment_reports)
-      .unwrapOr([])
-      .map(r => this.commentReportToItemType(r));
-    let posts: ItemType[] = this.state.listPostReportsResponse
-      .map(r => r.post_reports)
-      .unwrapOr([])
-      .map(r => this.postReportToItemType(r));
-    let privateMessages: ItemType[] =
-      this.state.listPrivateMessageReportsResponse
-        .map(r => r.private_message_reports)
-        .unwrapOr([])
-        .map(r => this.privateMessageReportToItemType(r));
+    // let comments: ItemType[] = this.state.listCommentReportsResponse
+    //   .map(r => r.comment_reports)
+    //   .unwrapOr([])
+    //   .map(r => this.commentReportToItemType(r));
+    let comments =
+      this.state.listCommentReportsResponse?.comment_reports.map(
+        this.commentReportToItemType
+      ) ?? [];
+    let posts =
+      this.state.listPostReportsResponse?.post_reports.map(
+        this.postReportToItemType
+      ) ?? [];
+    let privateMessages =
+      this.state.listPrivateMessageReportsResponse?.private_message_reports.map(
+        this.privateMessageReportToItemType
+      ) ?? [];
 
     return [...comments, ...posts, ...privateMessages].sort((a, b) =>
       b.published.localeCompare(a.published)
@@ -384,42 +371,44 @@ export class Reports extends Component<any, ReportsState> {
   }
 
   commentReports() {
-    return this.state.listCommentReportsResponse.match({
-      some: res => (
+    let reports = this.state.listCommentReportsResponse?.comment_reports;
+    return (
+      reports && (
         <div>
-          {res.comment_reports.map(cr => (
+          {reports.map(cr => (
             <>
               <hr />
               <CommentReport key={cr.comment_report.id} report={cr} />
             </>
           ))}
         </div>
-      ),
-      none: <></>,
-    });
+      )
+    );
   }
 
   postReports() {
-    return this.state.listPostReportsResponse.match({
-      some: res => (
+    let reports = this.state.listPostReportsResponse?.post_reports;
+    return (
+      reports && (
         <div>
-          {res.post_reports.map(pr => (
+          {reports.map(pr => (
             <>
               <hr />
               <PostReport key={pr.post_report.id} report={pr} />
             </>
           ))}
         </div>
-      ),
-      none: <></>,
-    });
+      )
+    );
   }
 
   privateMessageReports() {
-    return this.state.listPrivateMessageReportsResponse.match({
-      some: res => (
+    let reports =
+      this.state.listPrivateMessageReportsResponse?.private_message_reports;
+    return (
+      reports && (
         <div>
-          {res.private_message_reports.map(pmr => (
+          {reports.map(pmr => (
             <>
               <hr />
               <PrivateMessageReport
@@ -429,9 +418,8 @@ export class Reports extends Component<any, ReportsState> {
             </>
           ))}
         </div>
-      ),
-      none: <></>,
-    });
+      )
+    );
   }
 
   handlePageChange(page: number) {
@@ -452,85 +440,79 @@ export class Reports extends Component<any, ReportsState> {
   static fetchInitialData(req: InitialFetchRequest): Promise<any>[] {
     let promises: Promise<any>[] = [];
 
-    let unresolved_only = Some(true);
-    let page = Some(1);
-    let limit = Some(fetchLimit);
-    let community_id = None;
-    let auth = req.auth.unwrap();
-
-    let commentReportsForm = new ListCommentReports({
-      // TODO community_id
-      unresolved_only,
-      community_id,
-      page,
-      limit,
-      auth,
-    });
-    promises.push(req.client.listCommentReports(commentReportsForm));
-
-    let postReportsForm = new ListPostReports({
-      // TODO community_id
-      unresolved_only,
-      community_id,
-      page,
-      limit,
-      auth,
-    });
-    promises.push(req.client.listPostReports(postReportsForm));
-
-    if (amAdmin()) {
-      let privateMessageReportsForm = new ListPrivateMessageReports({
+    let unresolved_only = true;
+    let page = 1;
+    let limit = fetchLimit;
+    let auth = req.auth;
+
+    if (auth) {
+      let commentReportsForm: ListCommentReports = {
         unresolved_only,
         page,
         limit,
         auth,
-      });
-      promises.push(
-        req.client.listPrivateMessageReports(privateMessageReportsForm)
-      );
+      };
+      promises.push(req.client.listCommentReports(commentReportsForm));
+
+      let postReportsForm: ListPostReports = {
+        unresolved_only,
+        page,
+        limit,
+        auth,
+      };
+      promises.push(req.client.listPostReports(postReportsForm));
+
+      if (amAdmin()) {
+        let privateMessageReportsForm: ListPrivateMessageReports = {
+          unresolved_only,
+          page,
+          limit,
+          auth,
+        };
+        promises.push(
+          req.client.listPrivateMessageReports(privateMessageReportsForm)
+        );
+      }
     }
 
     return promises;
   }
 
   refetch() {
-    let unresolved_only = Some(this.state.unreadOrAll == UnreadOrAll.Unread);
-    let community_id = None;
-    let page = Some(this.state.page);
-    let limit = Some(fetchLimit);
-
-    let commentReportsForm = new ListCommentReports({
-      unresolved_only,
-      // TODO community_id
-      community_id,
-      page,
-      limit,
-      auth: auth().unwrap(),
-    });
-    WebSocketService.Instance.send(
-      wsClient.listCommentReports(commentReportsForm)
-    );
-
-    let postReportsForm = new ListPostReports({
-      unresolved_only,
-      // TODO community_id
-      community_id,
-      page,
-      limit,
-      auth: auth().unwrap(),
-    });
-    WebSocketService.Instance.send(wsClient.listPostReports(postReportsForm));
-
-    if (amAdmin()) {
-      let privateMessageReportsForm = new ListPrivateMessageReports({
+    let unresolved_only = this.state.unreadOrAll == UnreadOrAll.Unread;
+    let page = this.state.page;
+    let limit = fetchLimit;
+    let auth = myAuth();
+    if (auth) {
+      let commentReportsForm: ListCommentReports = {
         unresolved_only,
         page,
         limit,
-        auth: auth().unwrap(),
-      });
+        auth,
+      };
       WebSocketService.Instance.send(
-        wsClient.listPrivateMessageReports(privateMessageReportsForm)
+        wsClient.listCommentReports(commentReportsForm)
       );
+
+      let postReportsForm: ListPostReports = {
+        unresolved_only,
+        page,
+        limit,
+        auth,
+      };
+      WebSocketService.Instance.send(wsClient.listPostReports(postReportsForm));
+
+      if (amAdmin()) {
+        let privateMessageReportsForm: ListPrivateMessageReports = {
+          unresolved_only,
+          page,
+          limit,
+          auth,
+        };
+        WebSocketService.Instance.send(
+          wsClient.listPrivateMessageReports(privateMessageReportsForm)
+        );
+      }
     }
   }
 
@@ -543,40 +525,31 @@ export class Reports extends Component<any, ReportsState> {
     } else if (msg.reconnect) {
       this.refetch();
     } else if (op == UserOperation.ListCommentReports) {
-      let data = wsJsonToRes<ListCommentReportsResponse>(
-        msg,
-        ListCommentReportsResponse
-      );
-      this.setState({ listCommentReportsResponse: Some(data) });
+      let data = wsJsonToRes<ListCommentReportsResponse>(msg);
+      this.setState({ listCommentReportsResponse: data });
       this.setState({ combined: this.buildCombined(), loading: false });
       // this.sendUnreadCount();
       window.scrollTo(0, 0);
       setupTippy();
     } else if (op == UserOperation.ListPostReports) {
-      let data = wsJsonToRes<ListPostReportsResponse>(
-        msg,
-        ListPostReportsResponse
-      );
-      this.setState({ listPostReportsResponse: Some(data) });
+      let data = wsJsonToRes<ListPostReportsResponse>(msg);
+      this.setState({ listPostReportsResponse: data });
       this.setState({ combined: this.buildCombined(), loading: false });
       // this.sendUnreadCount();
       window.scrollTo(0, 0);
       setupTippy();
     } else if (op == UserOperation.ListPrivateMessageReports) {
-      let data = wsJsonToRes<ListPrivateMessageReportsResponse>(
-        msg,
-        ListPrivateMessageReportsResponse
-      );
-      this.setState({ listPrivateMessageReportsResponse: Some(data) });
+      let data = wsJsonToRes<ListPrivateMessageReportsResponse>(msg);
+      this.setState({ listPrivateMessageReportsResponse: data });
       this.setState({ combined: this.buildCombined(), loading: false });
       // this.sendUnreadCount();
       window.scrollTo(0, 0);
       setupTippy();
     } else if (op == UserOperation.ResolvePostReport) {
-      let data = wsJsonToRes<PostReportResponse>(msg, PostReportResponse);
+      let data = wsJsonToRes<PostReportResponse>(msg);
       updatePostReportRes(
         data.post_report_view,
-        this.state.listPostReportsResponse.map(r => r.post_reports).unwrapOr([])
+        this.state.listPostReportsResponse?.post_reports
       );
       let urcs = UserService.Instance.unreadReportCountSub;
       if (data.post_report_view.post_report.resolved) {
@@ -586,12 +559,10 @@ export class Reports extends Component<any, ReportsState> {
       }
       this.setState(this.state);
     } else if (op == UserOperation.ResolveCommentReport) {
-      let data = wsJsonToRes<CommentReportResponse>(msg, CommentReportResponse);
+      let data = wsJsonToRes<CommentReportResponse>(msg);
       updateCommentReportRes(
         data.comment_report_view,
-        this.state.listCommentReportsResponse
-          .map(r => r.comment_reports)
-          .unwrapOr([])
+        this.state.listCommentReportsResponse?.comment_reports
       );
       let urcs = UserService.Instance.unreadReportCountSub;
       if (data.comment_report_view.comment_report.resolved) {
@@ -601,15 +572,10 @@ export class Reports extends Component<any, ReportsState> {
       }
       this.setState(this.state);
     } else if (op == UserOperation.ResolvePrivateMessageReport) {
-      let data = wsJsonToRes<PrivateMessageReportResponse>(
-        msg,
-        PrivateMessageReportResponse
-      );
+      let data = wsJsonToRes<PrivateMessageReportResponse>(msg);
       updatePrivateMessageReportRes(
         data.private_message_report_view,
-        this.state.listPrivateMessageReportsResponse
-          .map(r => r.private_message_reports)
-          .unwrapOr([])
+        this.state.listPrivateMessageReportsResponse?.private_message_reports
       );
       let urcs = UserService.Instance.unreadReportCountSub;
       if (data.private_message_report_view.private_message_report.resolved) {
index 0f904d5e66805340ab39446f65f0cac0be13b605..734ae7120d55e57bc66f71fe9164b205f766820f 100644 (file)
@@ -1,4 +1,3 @@
-import { None, Option, Some } from "@sniptt/monads";
 import { Component, linkEvent } from "inferno";
 import {
   BlockCommunity,
@@ -16,7 +15,6 @@ import {
   PersonViewSafe,
   SaveUserSettings,
   SortType,
-  toUndefined,
   UserOperation,
   wsJsonToRes,
   wsUserOp,
@@ -25,7 +23,6 @@ import { Subscription } from "rxjs";
 import { i18n, languages } from "../../i18next";
 import { UserService, WebSocketService } from "../../services";
 import {
-  auth,
   capitalizeFirstLetter,
   choicesConfig,
   communitySelectName,
@@ -38,6 +35,7 @@ import {
   fetchUsers,
   getLanguages,
   isBrowser,
+  myAuth,
   personSelectName,
   personToChoice,
   relTags,
@@ -67,11 +65,38 @@ if (isBrowser()) {
 }
 
 interface SettingsState {
-  saveUserSettingsForm: SaveUserSettings;
-  changePasswordForm: ChangePassword;
-  deleteAccountForm: DeleteAccount;
+  // TODO redo these forms
+  saveUserSettingsForm: {
+    show_nsfw?: boolean;
+    theme?: string;
+    default_sort_type?: number;
+    default_listing_type?: number;
+    interface_language?: string;
+    avatar?: string;
+    banner?: string;
+    display_name?: string;
+    email?: string;
+    bio?: string;
+    matrix_user_id?: string;
+    show_avatars?: boolean;
+    show_scores?: boolean;
+    send_notifications_to_email?: boolean;
+    bot_account?: boolean;
+    show_bot_accounts?: boolean;
+    show_read_posts?: boolean;
+    show_new_post_notifs?: boolean;
+    discussion_languages?: number[];
+  };
+  changePasswordForm: {
+    new_password?: string;
+    new_password_verify?: string;
+    old_password?: string;
+  };
+  deleteAccountForm: {
+    password?: string;
+  };
   personBlocks: PersonBlockView[];
-  blockPerson: Option<PersonViewSafe>;
+  blockPerson?: PersonViewSafe;
   communityBlocks: CommunityBlockView[];
   blockCommunityId: number;
   blockCommunity?: CommunityView;
@@ -88,46 +113,16 @@ export class Settings extends Component<any, SettingsState> {
   private isoData = setIsoData(this.context);
   private blockPersonChoices: any;
   private blockCommunityChoices: any;
-  private subscription: Subscription;
-  private emptyState: SettingsState = {
-    saveUserSettingsForm: new SaveUserSettings({
-      show_nsfw: None,
-      show_scores: None,
-      show_avatars: None,
-      show_read_posts: None,
-      show_bot_accounts: None,
-      show_new_post_notifs: None,
-      default_sort_type: None,
-      default_listing_type: None,
-      theme: None,
-      interface_language: None,
-      discussion_languages: None,
-      avatar: None,
-      banner: None,
-      display_name: None,
-      email: None,
-      bio: None,
-      matrix_user_id: None,
-      send_notifications_to_email: None,
-      bot_account: None,
-      auth: undefined,
-    }),
-    changePasswordForm: new ChangePassword({
-      new_password: undefined,
-      new_password_verify: undefined,
-      old_password: undefined,
-      auth: undefined,
-    }),
+  private subscription?: Subscription;
+  state: SettingsState = {
+    saveUserSettingsForm: {},
+    changePasswordForm: {},
     saveUserSettingsLoading: false,
     changePasswordLoading: false,
     deleteAccountLoading: false,
     deleteAccountShowConfirm: false,
-    deleteAccountForm: new DeleteAccount({
-      password: undefined,
-      auth: undefined,
-    }),
+    deleteAccountForm: {},
     personBlocks: [],
-    blockPerson: None,
     communityBlocks: [],
     blockCommunityId: 0,
     currentTab: "settings",
@@ -138,7 +133,6 @@ export class Settings extends Component<any, SettingsState> {
   constructor(props: any, context: any) {
     super(props, context);
 
-    this.state = this.emptyState;
     this.handleSortTypeChange = this.handleSortTypeChange.bind(this);
     this.handleListingTypeChange = this.handleListingTypeChange.bind(this);
     this.handleBioChange = this.handleBioChange.bind(this);
@@ -154,8 +148,8 @@ export class Settings extends Component<any, SettingsState> {
     this.parseMessage = this.parseMessage.bind(this);
     this.subscription = wsSubscribe(this.parseMessage);
 
-    if (UserService.Instance.myUserInfo.isSome()) {
-      let mui = UserService.Instance.myUserInfo.unwrap();
+    let mui = UserService.Instance.myUserInfo;
+    if (mui) {
       let luv = mui.local_user_view;
       this.state = {
         ...this.state,
@@ -163,26 +157,25 @@ export class Settings extends Component<any, SettingsState> {
         communityBlocks: mui.community_blocks,
         saveUserSettingsForm: {
           ...this.state.saveUserSettingsForm,
-          show_nsfw: Some(luv.local_user.show_nsfw),
-          theme: Some(luv.local_user.theme ? luv.local_user.theme : "browser"),
-          default_sort_type: Some(luv.local_user.default_sort_type),
-          default_listing_type: Some(luv.local_user.default_listing_type),
-          interface_language: Some(luv.local_user.interface_language),
-          discussion_languages: Some(mui.discussion_languages),
+          show_nsfw: luv.local_user.show_nsfw,
+          theme: luv.local_user.theme ? luv.local_user.theme : "browser",
+          default_sort_type: luv.local_user.default_sort_type,
+          default_listing_type: luv.local_user.default_listing_type,
+          interface_language: luv.local_user.interface_language,
+          discussion_languages: mui.discussion_languages,
           avatar: luv.person.avatar,
           banner: luv.person.banner,
           display_name: luv.person.display_name,
-          show_avatars: Some(luv.local_user.show_avatars),
-          bot_account: Some(luv.person.bot_account),
-          show_bot_accounts: Some(luv.local_user.show_bot_accounts),
-          show_scores: Some(luv.local_user.show_scores),
-          show_read_posts: Some(luv.local_user.show_read_posts),
-          show_new_post_notifs: Some(luv.local_user.show_new_post_notifs),
+          show_avatars: luv.local_user.show_avatars,
+          bot_account: luv.person.bot_account,
+          show_bot_accounts: luv.local_user.show_bot_accounts,
+          show_scores: luv.local_user.show_scores,
+          show_read_posts: luv.local_user.show_read_posts,
+          show_new_post_notifs: luv.local_user.show_new_post_notifs,
           email: luv.local_user.email,
           bio: luv.person.bio,
-          send_notifications_to_email: Some(
-            luv.local_user.send_notifications_to_email
-          ),
+          send_notifications_to_email:
+            luv.local_user.send_notifications_to_email,
           matrix_user_id: luv.person.matrix_user_id,
         },
       };
@@ -195,7 +188,7 @@ export class Settings extends Component<any, SettingsState> {
   }
 
   componentWillUnmount() {
-    this.subscription.unsubscribe();
+    this.subscription?.unsubscribe();
   }
 
   get documentTitle(): string {
@@ -209,7 +202,7 @@ export class Settings extends Component<any, SettingsState> {
           <HtmlTags
             title={this.documentTitle}
             path={this.context.router.route.match.url}
-            description={Some(this.documentTitle)}
+            description={this.documentTitle}
             image={this.state.saveUserSettingsForm.avatar}
           />
           <ul className="nav nav-tabs mb-2">
@@ -391,6 +384,7 @@ export class Settings extends Component<any, SettingsState> {
   }
 
   blockUserForm() {
+    let blockPerson = this.state.blockPerson;
     return (
       <div className="form-group row">
         <label
@@ -403,17 +397,14 @@ export class Settings extends Component<any, SettingsState> {
           <select
             className="form-control"
             id="block-person-filter"
-            value={this.state.blockPerson.map(p => p.person.id).unwrapOr(0)}
+            value={blockPerson?.person.id ?? 0}
           >
             <option value="0">—</option>
-            {this.state.blockPerson.match({
-              some: personView => (
-                <option value={personView.person.id}>
-                  {personSelectName(personView)}
-                </option>
-              ),
-              none: <></>,
-            })}
+            {blockPerson && (
+              <option value={blockPerson.person.id}>
+                {personSelectName(blockPerson)}
+              </option>
+            )}
           </select>
         </div>
       </div>
@@ -500,9 +491,7 @@ export class Settings extends Component<any, SettingsState> {
                 type="text"
                 className="form-control"
                 placeholder={i18n.t("optional")}
-                value={toUndefined(
-                  this.state.saveUserSettingsForm.display_name
-                )}
+                value={this.state.saveUserSettingsForm.display_name}
                 onInput={linkEvent(this, this.handleDisplayNameChange)}
                 pattern="^(?!@)(.+)$"
                 minLength={3}
@@ -516,11 +505,8 @@ export class Settings extends Component<any, SettingsState> {
             <div className="col-sm-9">
               <MarkdownTextArea
                 initialContent={this.state.saveUserSettingsForm.bio}
-                initialLanguageId={None}
                 onContentChange={this.handleBioChange}
-                maxLength={Some(300)}
-                placeholder={None}
-                buttonTitle={None}
+                maxLength={300}
                 hideNavigationWarnings
                 allLanguages={this.state.siteRes.all_languages}
                 siteLanguages={this.state.siteRes.discussion_languages}
@@ -537,7 +523,7 @@ export class Settings extends Component<any, SettingsState> {
                 id="user-email"
                 className="form-control"
                 placeholder={i18n.t("optional")}
-                value={toUndefined(this.state.saveUserSettingsForm.email)}
+                value={this.state.saveUserSettingsForm.email}
                 onInput={linkEvent(this, this.handleEmailChange)}
                 minLength={3}
               />
@@ -555,9 +541,7 @@ export class Settings extends Component<any, SettingsState> {
                 type="text"
                 className="form-control"
                 placeholder="@user:example.com"
-                value={toUndefined(
-                  this.state.saveUserSettingsForm.matrix_user_id
-                )}
+                value={this.state.saveUserSettingsForm.matrix_user_id}
                 onInput={linkEvent(this, this.handleMatrixUserIdChange)}
                 pattern="^@[A-Za-z0-9._=-]+:[A-Za-z0-9.-]+\.[A-Za-z]{2,}$"
               />
@@ -593,9 +577,7 @@ export class Settings extends Component<any, SettingsState> {
             <div className="col-sm-9">
               <select
                 id="user-language"
-                value={toUndefined(
-                  this.state.saveUserSettingsForm.interface_language
-                )}
+                value={this.state.saveUserSettingsForm.interface_language}
                 onChange={linkEvent(this, this.handleInterfaceLangChange)}
                 className="custom-select w-auto"
               >
@@ -631,7 +613,7 @@ export class Settings extends Component<any, SettingsState> {
             <div className="col-sm-9">
               <select
                 id="user-theme"
-                value={toUndefined(this.state.saveUserSettingsForm.theme)}
+                value={this.state.saveUserSettingsForm.theme}
                 onChange={linkEvent(this, this.handleThemeChange)}
                 className="custom-select w-auto"
               >
@@ -653,9 +635,7 @@ export class Settings extends Component<any, SettingsState> {
               <ListingTypeSelect
                 type_={
                   Object.values(ListingType)[
-                    this.state.saveUserSettingsForm.default_listing_type.unwrapOr(
-                      1
-                    )
+                    this.state.saveUserSettingsForm.default_listing_type ?? 1
                   ]
                 }
                 showLocal={showLocal(this.isoData)}
@@ -670,9 +650,7 @@ export class Settings extends Component<any, SettingsState> {
               <SortSelect
                 sort={
                   Object.values(SortType)[
-                    this.state.saveUserSettingsForm.default_sort_type.unwrapOr(
-                      0
-                    )
+                    this.state.saveUserSettingsForm.default_sort_type ?? 0
                   ]
                 }
                 onChange={this.handleSortTypeChange}
@@ -686,9 +664,7 @@ export class Settings extends Component<any, SettingsState> {
                   className="form-check-input"
                   id="user-show-nsfw"
                   type="checkbox"
-                  checked={toUndefined(
-                    this.state.saveUserSettingsForm.show_nsfw
-                  )}
+                  checked={this.state.saveUserSettingsForm.show_nsfw}
                   onChange={linkEvent(this, this.handleShowNsfwChange)}
                 />
                 <label className="form-check-label" htmlFor="user-show-nsfw">
@@ -703,9 +679,7 @@ export class Settings extends Component<any, SettingsState> {
                 className="form-check-input"
                 id="user-show-scores"
                 type="checkbox"
-                checked={toUndefined(
-                  this.state.saveUserSettingsForm.show_scores
-                )}
+                checked={this.state.saveUserSettingsForm.show_scores}
                 onChange={linkEvent(this, this.handleShowScoresChange)}
               />
               <label className="form-check-label" htmlFor="user-show-scores">
@@ -719,9 +693,7 @@ export class Settings extends Component<any, SettingsState> {
                 className="form-check-input"
                 id="user-show-avatars"
                 type="checkbox"
-                checked={toUndefined(
-                  this.state.saveUserSettingsForm.show_avatars
-                )}
+                checked={this.state.saveUserSettingsForm.show_avatars}
                 onChange={linkEvent(this, this.handleShowAvatarsChange)}
               />
               <label className="form-check-label" htmlFor="user-show-avatars">
@@ -735,9 +707,7 @@ export class Settings extends Component<any, SettingsState> {
                 className="form-check-input"
                 id="user-bot-account"
                 type="checkbox"
-                checked={toUndefined(
-                  this.state.saveUserSettingsForm.bot_account
-                )}
+                checked={this.state.saveUserSettingsForm.bot_account}
                 onChange={linkEvent(this, this.handleBotAccount)}
               />
               <label className="form-check-label" htmlFor="user-bot-account">
@@ -751,9 +721,7 @@ export class Settings extends Component<any, SettingsState> {
                 className="form-check-input"
                 id="user-show-bot-accounts"
                 type="checkbox"
-                checked={toUndefined(
-                  this.state.saveUserSettingsForm.show_bot_accounts
-                )}
+                checked={this.state.saveUserSettingsForm.show_bot_accounts}
                 onChange={linkEvent(this, this.handleShowBotAccounts)}
               />
               <label
@@ -770,9 +738,7 @@ export class Settings extends Component<any, SettingsState> {
                 className="form-check-input"
                 id="user-show-read-posts"
                 type="checkbox"
-                checked={toUndefined(
-                  this.state.saveUserSettingsForm.show_read_posts
-                )}
+                checked={this.state.saveUserSettingsForm.show_read_posts}
                 onChange={linkEvent(this, this.handleReadPosts)}
               />
               <label
@@ -789,9 +755,7 @@ export class Settings extends Component<any, SettingsState> {
                 className="form-check-input"
                 id="user-show-new-post-notifs"
                 type="checkbox"
-                checked={toUndefined(
-                  this.state.saveUserSettingsForm.show_new_post_notifs
-                )}
+                checked={this.state.saveUserSettingsForm.show_new_post_notifs}
                 onChange={linkEvent(this, this.handleShowNewPostNotifs)}
               />
               <label
@@ -809,9 +773,9 @@ export class Settings extends Component<any, SettingsState> {
                 id="user-send-notifications-to-email"
                 type="checkbox"
                 disabled={!this.state.saveUserSettingsForm.email}
-                checked={toUndefined(
+                checked={
                   this.state.saveUserSettingsForm.send_notifications_to_email
-                )}
+                }
                 onChange={linkEvent(
                   this,
                   this.handleSendNotificationsToEmailChange
@@ -959,32 +923,37 @@ export class Settings extends Component<any, SettingsState> {
   }
 
   handleBlockPerson(personId: number) {
-    if (personId != 0) {
-      let blockUserForm = new BlockPerson({
+    let auth = myAuth();
+    if (auth && personId != 0) {
+      let blockUserForm: BlockPerson = {
         person_id: personId,
         block: true,
-        auth: auth().unwrap(),
-      });
+        auth,
+      };
       WebSocketService.Instance.send(wsClient.blockPerson(blockUserForm));
     }
   }
 
   handleUnblockPerson(i: { ctx: Settings; recipientId: number }) {
-    let blockUserForm = new BlockPerson({
-      person_id: i.recipientId,
-      block: false,
-      auth: auth().unwrap(),
-    });
-    WebSocketService.Instance.send(wsClient.blockPerson(blockUserForm));
+    let auth = myAuth();
+    if (auth) {
+      let blockUserForm: BlockPerson = {
+        person_id: i.recipientId,
+        block: false,
+        auth,
+      };
+      WebSocketService.Instance.send(wsClient.blockPerson(blockUserForm));
+    }
   }
 
   handleBlockCommunity(community_id: number) {
-    if (community_id != 0) {
-      let blockCommunityForm = new BlockCommunity({
+    let auth = myAuth();
+    if (auth && community_id != 0) {
+      let blockCommunityForm: BlockCommunity = {
         community_id,
         block: true,
-        auth: auth().unwrap(),
-      });
+        auth,
+      };
       WebSocketService.Instance.send(
         wsClient.blockCommunity(blockCommunityForm)
       );
@@ -992,94 +961,93 @@ export class Settings extends Component<any, SettingsState> {
   }
 
   handleUnblockCommunity(i: { ctx: Settings; communityId: number }) {
-    let blockCommunityForm = new BlockCommunity({
-      community_id: i.communityId,
-      block: false,
-      auth: auth().unwrap(),
-    });
-    WebSocketService.Instance.send(wsClient.blockCommunity(blockCommunityForm));
+    let auth = myAuth();
+    if (auth) {
+      let blockCommunityForm: BlockCommunity = {
+        community_id: i.communityId,
+        block: false,
+        auth,
+      };
+      WebSocketService.Instance.send(
+        wsClient.blockCommunity(blockCommunityForm)
+      );
+    }
   }
 
   handleShowNsfwChange(i: Settings, event: any) {
-    i.state.saveUserSettingsForm.show_nsfw = Some(event.target.checked);
+    i.state.saveUserSettingsForm.show_nsfw = event.target.checked;
     i.setState(i.state);
   }
 
   handleShowAvatarsChange(i: Settings, event: any) {
-    i.state.saveUserSettingsForm.show_avatars = Some(event.target.checked);
-    UserService.Instance.myUserInfo.match({
-      some: mui =>
-        (mui.local_user_view.local_user.show_avatars = event.target.checked),
-      none: void 0,
-    });
+    i.state.saveUserSettingsForm.show_avatars = event.target.checked;
+    let mui = UserService.Instance.myUserInfo;
+    if (mui) {
+      mui.local_user_view.local_user.show_avatars = event.target.checked;
+    }
     i.setState(i.state);
   }
 
   handleBotAccount(i: Settings, event: any) {
-    i.state.saveUserSettingsForm.bot_account = Some(event.target.checked);
+    i.state.saveUserSettingsForm.bot_account = event.target.checked;
     i.setState(i.state);
   }
 
   handleShowBotAccounts(i: Settings, event: any) {
-    i.state.saveUserSettingsForm.show_bot_accounts = Some(event.target.checked);
+    i.state.saveUserSettingsForm.show_bot_accounts = event.target.checked;
     i.setState(i.state);
   }
 
   handleReadPosts(i: Settings, event: any) {
-    i.state.saveUserSettingsForm.show_read_posts = Some(event.target.checked);
+    i.state.saveUserSettingsForm.show_read_posts = event.target.checked;
     i.setState(i.state);
   }
 
   handleShowNewPostNotifs(i: Settings, event: any) {
-    i.state.saveUserSettingsForm.show_new_post_notifs = Some(
-      event.target.checked
-    );
+    i.state.saveUserSettingsForm.show_new_post_notifs = event.target.checked;
     i.setState(i.state);
   }
 
   handleShowScoresChange(i: Settings, event: any) {
-    i.state.saveUserSettingsForm.show_scores = Some(event.target.checked);
-    UserService.Instance.myUserInfo.match({
-      some: mui =>
-        (mui.local_user_view.local_user.show_scores = event.target.checked),
-      none: void 0,
-    });
+    i.state.saveUserSettingsForm.show_scores = event.target.checked;
+    let mui = UserService.Instance.myUserInfo;
+    if (mui) {
+      mui.local_user_view.local_user.show_scores = event.target.checked;
+    }
     i.setState(i.state);
   }
 
   handleSendNotificationsToEmailChange(i: Settings, event: any) {
-    i.state.saveUserSettingsForm.send_notifications_to_email = Some(
-      event.target.checked
-    );
+    i.state.saveUserSettingsForm.send_notifications_to_email =
+      event.target.checked;
     i.setState(i.state);
   }
 
   handleThemeChange(i: Settings, event: any) {
-    i.state.saveUserSettingsForm.theme = Some(event.target.value);
+    i.state.saveUserSettingsForm.theme = event.target.value;
     setTheme(event.target.value, true);
     i.setState(i.state);
   }
 
   handleInterfaceLangChange(i: Settings, event: any) {
-    i.state.saveUserSettingsForm.interface_language = Some(event.target.value);
+    i.state.saveUserSettingsForm.interface_language = event.target.value;
     i18n.changeLanguage(
-      getLanguages(i.state.saveUserSettingsForm.interface_language.unwrap())[0]
+      getLanguages(i.state.saveUserSettingsForm.interface_language).at(0)
     );
     i.setState(i.state);
   }
 
   handleDiscussionLanguageChange(val: number[]) {
     this.setState(
-      s => ((s.saveUserSettingsForm.discussion_languages = Some(val)), s)
+      s => ((s.saveUserSettingsForm.discussion_languages = val), s)
     );
   }
 
   handleSortTypeChange(val: SortType) {
     this.setState(
       s => (
-        (s.saveUserSettingsForm.default_sort_type = Some(
-          Object.keys(SortType).indexOf(val)
-        )),
+        (s.saveUserSettingsForm.default_sort_type =
+          Object.keys(SortType).indexOf(val)),
         s
       )
     );
@@ -1088,46 +1056,45 @@ export class Settings extends Component<any, SettingsState> {
   handleListingTypeChange(val: ListingType) {
     this.setState(
       s => (
-        (s.saveUserSettingsForm.default_listing_type = Some(
-          Object.keys(ListingType).indexOf(val)
-        )),
+        (s.saveUserSettingsForm.default_listing_type =
+          Object.keys(ListingType).indexOf(val)),
         s
       )
     );
   }
 
   handleEmailChange(i: Settings, event: any) {
-    i.state.saveUserSettingsForm.email = Some(event.target.value);
+    i.state.saveUserSettingsForm.email = event.target.value;
     i.setState(i.state);
   }
 
   handleBioChange(val: string) {
-    this.setState(s => ((s.saveUserSettingsForm.bio = Some(val)), s));
+    this.setState(s => ((s.saveUserSettingsForm.bio = val), s));
   }
 
   handleAvatarUpload(url: string) {
-    this.setState(s => ((s.saveUserSettingsForm.avatar = Some(url)), s));
+    this.setState(s => ((s.saveUserSettingsForm.avatar = url), s));
   }
 
   handleAvatarRemove() {
-    this.setState(s => ((s.saveUserSettingsForm.avatar = Some("")), s));
+    this.setState(s => ((s.saveUserSettingsForm.avatar = ""), s));
   }
 
   handleBannerUpload(url: string) {
-    this.setState(s => ((s.saveUserSettingsForm.banner = Some(url)), s));
+    this.setState(s => ((s.saveUserSettingsForm.banner = url), s));
   }
 
   handleBannerRemove() {
-    this.setState(s => ((s.saveUserSettingsForm.banner = Some("")), s));
+    this.setState(s => ((s.saveUserSettingsForm.banner = ""), s));
   }
 
   handleDisplayNameChange(i: Settings, event: any) {
-    i.state.saveUserSettingsForm.display_name = Some(event.target.value);
+    i.state.saveUserSettingsForm.display_name = event.target.value;
     i.setState(i.state);
   }
 
   handleMatrixUserIdChange(i: Settings, event: any) {
-    i.state.saveUserSettingsForm.matrix_user_id = Some(event.target.value);
+    i.state.saveUserSettingsForm.matrix_user_id = event.target.value;
     i.setState(i.state);
   }
 
@@ -1158,20 +1125,31 @@ export class Settings extends Component<any, SettingsState> {
   handleSaveSettingsSubmit(i: Settings, event: any) {
     event.preventDefault();
     i.setState({ saveUserSettingsLoading: true });
-    i.setState(s => ((s.saveUserSettingsForm.auth = auth().unwrap()), s));
-
-    let form = new SaveUserSettings({ ...i.state.saveUserSettingsForm });
-    WebSocketService.Instance.send(wsClient.saveUserSettings(form));
+    let auth = myAuth();
+    if (auth) {
+      let form: SaveUserSettings = { ...i.state.saveUserSettingsForm, auth };
+      WebSocketService.Instance.send(wsClient.saveUserSettings(form));
+    }
   }
 
   handleChangePasswordSubmit(i: Settings, event: any) {
     event.preventDefault();
     i.setState({ changePasswordLoading: true });
-    i.setState(s => ((s.changePasswordForm.auth = auth().unwrap()), s));
-
-    let form = new ChangePassword({ ...i.state.changePasswordForm });
+    let auth = myAuth();
+    let pForm = i.state.changePasswordForm;
+    let new_password = pForm.new_password;
+    let new_password_verify = pForm.new_password_verify;
+    let old_password = pForm.old_password;
+    if (auth && new_password && old_password && new_password_verify) {
+      let form: ChangePassword = {
+        new_password,
+        new_password_verify,
+        old_password,
+        auth,
+      };
 
-    WebSocketService.Instance.send(wsClient.changePassword(form));
+      WebSocketService.Instance.send(wsClient.changePassword(form));
+    }
   }
 
   handleDeleteAccountShowConfirmToggle(i: Settings, event: any) {
@@ -1187,11 +1165,15 @@ export class Settings extends Component<any, SettingsState> {
   handleDeleteAccount(i: Settings, event: any) {
     event.preventDefault();
     i.setState({ deleteAccountLoading: true });
-    i.setState(s => ((s.deleteAccountForm.auth = auth().unwrap()), s));
-
-    let form = new DeleteAccount({ ...i.state.deleteAccountForm });
-
-    WebSocketService.Instance.send(wsClient.deleteAccount(form));
+    let auth = myAuth();
+    let password = i.state.deleteAccountForm.password;
+    if (auth && password) {
+      let form: DeleteAccount = {
+        password,
+        auth,
+      };
+      WebSocketService.Instance.send(wsClient.deleteAccount(form));
+    }
   }
 
   handleSwitchTab(i: { ctx: Settings; tab: string }) {
@@ -1215,14 +1197,14 @@ export class Settings extends Component<any, SettingsState> {
       toast(i18n.t(msg.error), "danger");
       return;
     } else if (op == UserOperation.SaveUserSettings) {
-      let data = wsJsonToRes<LoginResponse>(msg, LoginResponse);
+      let data = wsJsonToRes<LoginResponse>(msg);
       UserService.Instance.login(data);
       location.reload();
       this.setState({ saveUserSettingsLoading: false });
       toast(i18n.t("saved"));
       window.scrollTo(0, 0);
     } else if (op == UserOperation.ChangePassword) {
-      let data = wsJsonToRes<LoginResponse>(msg, LoginResponse);
+      let data = wsJsonToRes<LoginResponse>(msg);
       UserService.Instance.login(data);
       this.setState({ changePasswordLoading: false });
       window.scrollTo(0, 0);
@@ -1235,20 +1217,19 @@ export class Settings extends Component<any, SettingsState> {
       UserService.Instance.logout();
       window.location.href = "/";
     } else if (op == UserOperation.BlockPerson) {
-      let data = wsJsonToRes<BlockPersonResponse>(msg, BlockPersonResponse);
-      updatePersonBlock(data).match({
-        some: blocks => this.setState({ personBlocks: blocks }),
-        none: void 0,
-      });
+      let data = wsJsonToRes<BlockPersonResponse>(msg);
+      updatePersonBlock(data);
+      let mui = UserService.Instance.myUserInfo;
+      if (mui) {
+        this.setState({ personBlocks: mui.person_blocks });
+      }
     } else if (op == UserOperation.BlockCommunity) {
-      let data = wsJsonToRes<BlockCommunityResponse>(
-        msg,
-        BlockCommunityResponse
-      );
-      updateCommunityBlock(data).match({
-        some: blocks => this.setState({ communityBlocks: blocks }),
-        none: void 0,
-      });
+      let data = wsJsonToRes<BlockCommunityResponse>(msg);
+      updateCommunityBlock(data);
+      let mui = UserService.Instance.myUserInfo;
+      if (mui) {
+        this.setState({ communityBlocks: mui.community_blocks });
+      }
     }
   }
 }
index 819db2923cdd886dc17b0bcb7c973561da0265e9..14231b8ce3e3c92e303eb0b585ec189293b493f7 100644 (file)
@@ -1,4 +1,3 @@
-import { None } from "@sniptt/monads/build";
 import { Component } from "inferno";
 import {
   GetSiteResponse,
@@ -27,20 +26,18 @@ interface State {
 
 export class VerifyEmail extends Component<any, State> {
   private isoData = setIsoData(this.context);
-  private subscription: Subscription;
+  private subscription?: Subscription;
 
-  emptyState: State = {
-    verifyEmailForm: new VerifyEmailForm({
+  state: State = {
+    verifyEmailForm: {
       token: this.props.match.params.token,
-    }),
+    },
     siteRes: this.isoData.site_res,
   };
 
   constructor(props: any, context: any) {
     super(props, context);
 
-    this.state = this.emptyState;
-
     this.parseMessage = this.parseMessage.bind(this);
     this.subscription = wsSubscribe(this.parseMessage);
   }
@@ -53,7 +50,7 @@ export class VerifyEmail extends Component<any, State> {
 
   componentWillUnmount() {
     if (isBrowser()) {
-      this.subscription.unsubscribe();
+      this.subscription?.unsubscribe();
     }
   }
 
@@ -69,8 +66,6 @@ export class VerifyEmail extends Component<any, State> {
         <HtmlTags
           title={this.documentTitle}
           path={this.context.router.route.match.url}
-          description={None}
-          image={None}
         />
         <div className="row">
           <div className="col-12 col-lg-6 offset-lg-3 mb-4">
@@ -90,10 +85,9 @@ export class VerifyEmail extends Component<any, State> {
       this.props.history.push("/");
       return;
     } else if (op == UserOperation.VerifyEmail) {
-      let data = wsJsonToRes<VerifyEmailResponse>(msg, VerifyEmailResponse);
+      let data = wsJsonToRes<VerifyEmailResponse>(msg);
       if (data) {
         toast(i18n.t("email_verified"));
-        this.setState(this.emptyState);
         this.props.history.push("/login");
       }
     }
index 37c855f632051f013d6abcdf74b5c9488ebde557..c1d282e92576c205ff0b1c6f24df7897c17b5418 100644 (file)
@@ -1,4 +1,3 @@
-import { Either, Left, None, Option, Right, Some } from "@sniptt/monads";
 import { Component } from "inferno";
 import {
   GetCommunity,
@@ -9,7 +8,6 @@ import {
   ListingType,
   PostView,
   SortType,
-  toOption,
   UserOperation,
   wsJsonToRes,
   wsUserOp,
@@ -19,11 +17,11 @@ import { InitialFetchRequest, PostFormParams } from "shared/interfaces";
 import { i18n } from "../../i18next";
 import { UserService, WebSocketService } from "../../services";
 import {
-  auth,
   enableDownvotes,
   enableNsfw,
   fetchLimit,
   isBrowser,
+  myAuth,
   setIsoData,
   toast,
   wsClient,
@@ -34,30 +32,28 @@ import { Spinner } from "../common/icon";
 import { PostForm } from "./post-form";
 
 interface CreatePostState {
-  listCommunitiesResponse: Option<ListCommunitiesResponse>;
+  listCommunitiesResponse?: ListCommunitiesResponse;
   siteRes: GetSiteResponse;
   loading: boolean;
 }
 
 export class CreatePost extends Component<any, CreatePostState> {
-  private isoData = setIsoData(this.context, ListCommunitiesResponse);
-  private subscription: Subscription;
-  private emptyState: CreatePostState = {
+  private isoData = setIsoData(this.context);
+  private subscription?: Subscription;
+  state: CreatePostState = {
     siteRes: this.isoData.site_res,
-    listCommunitiesResponse: None,
     loading: true,
   };
 
   constructor(props: any, context: any) {
     super(props, context);
-    this.state = this.emptyState;
 
     this.handlePostCreate = this.handlePostCreate.bind(this);
 
     this.parseMessage = this.parseMessage.bind(this);
     this.subscription = wsSubscribe(this.parseMessage);
 
-    if (UserService.Instance.myUserInfo.isNone() && isBrowser()) {
+    if (!UserService.Instance.myUserInfo && isBrowser()) {
       toast(i18n.t("not_logged_in"), "danger");
       this.context.router.history.push(`/login`);
     }
@@ -66,9 +62,8 @@ export class CreatePost extends Component<any, CreatePostState> {
     if (this.isoData.path == this.context.router.route.match.url) {
       this.state = {
         ...this.state,
-        listCommunitiesResponse: Some(
-          this.isoData.routeData[0] as ListCommunitiesResponse
-        ),
+        listCommunitiesResponse: this.isoData
+          .routeData[0] as ListCommunitiesResponse,
         loading: false,
       };
     } else {
@@ -77,44 +72,38 @@ export class CreatePost extends Component<any, CreatePostState> {
   }
 
   refetch() {
-    this.params.nameOrId.match({
-      some: opt =>
-        opt.match({
-          left: name => {
-            let form = new GetCommunity({
-              name: Some(name),
-              id: None,
-              auth: auth(false).ok(),
-            });
-            WebSocketService.Instance.send(wsClient.getCommunity(form));
-          },
-          right: id => {
-            let form = new GetCommunity({
-              id: Some(id),
-              name: None,
-              auth: auth(false).ok(),
-            });
-            WebSocketService.Instance.send(wsClient.getCommunity(form));
-          },
-        }),
-      none: () => {
-        let listCommunitiesForm = new ListCommunities({
-          type_: Some(ListingType.All),
-          sort: Some(SortType.TopAll),
-          limit: Some(fetchLimit),
-          page: None,
-          auth: auth(false).ok(),
-        });
-        WebSocketService.Instance.send(
-          wsClient.listCommunities(listCommunitiesForm)
-        );
-      },
-    });
+    let nameOrId = this.params.nameOrId;
+    let auth = myAuth(false);
+    if (nameOrId) {
+      if (typeof nameOrId === "string") {
+        let form: GetCommunity = {
+          name: nameOrId,
+          auth,
+        };
+        WebSocketService.Instance.send(wsClient.getCommunity(form));
+      } else {
+        let form: GetCommunity = {
+          id: nameOrId,
+          auth,
+        };
+        WebSocketService.Instance.send(wsClient.getCommunity(form));
+      }
+    } else {
+      let listCommunitiesForm: ListCommunities = {
+        type_: ListingType.All,
+        sort: SortType.TopAll,
+        limit: fetchLimit,
+        auth,
+      };
+      WebSocketService.Instance.send(
+        wsClient.listCommunities(listCommunitiesForm)
+      );
+    }
   }
 
   componentWillUnmount() {
     if (isBrowser()) {
-      this.subscription.unsubscribe();
+      this.subscription?.unsubscribe();
     }
   }
 
@@ -125,39 +114,34 @@ export class CreatePost extends Component<any, CreatePostState> {
   }
 
   render() {
+    let res = this.state.listCommunitiesResponse;
     return (
       <div className="container-lg">
         <HtmlTags
           title={this.documentTitle}
           path={this.context.router.route.match.url}
-          description={None}
-          image={None}
         />
         {this.state.loading ? (
           <h5>
             <Spinner large />
           </h5>
         ) : (
-          this.state.listCommunitiesResponse.match({
-            some: res => (
-              <div className="row">
-                <div className="col-12 col-lg-6 offset-lg-3 mb-4">
-                  <h5>{i18n.t("create_post")}</h5>
-                  <PostForm
-                    post_view={None}
-                    communities={Some(res.communities)}
-                    onCreate={this.handlePostCreate}
-                    params={Some(this.params)}
-                    enableDownvotes={enableDownvotes(this.state.siteRes)}
-                    enableNsfw={enableNsfw(this.state.siteRes)}
-                    allLanguages={this.state.siteRes.all_languages}
-                    siteLanguages={this.state.siteRes.discussion_languages}
-                  />
-                </div>
+          res && (
+            <div className="row">
+              <div className="col-12 col-lg-6 offset-lg-3 mb-4">
+                <h5>{i18n.t("create_post")}</h5>
+                <PostForm
+                  communities={res.communities}
+                  onCreate={this.handlePostCreate}
+                  params={this.params}
+                  enableDownvotes={enableDownvotes(this.state.siteRes)}
+                  enableNsfw={enableNsfw(this.state.siteRes)}
+                  allLanguages={this.state.siteRes.all_languages}
+                  siteLanguages={this.state.siteRes.discussion_languages}
+                />
               </div>
-            ),
-            none: <></>,
-          })
+            </div>
+          )
         )}
       </div>
     );
@@ -165,48 +149,42 @@ export class CreatePost extends Component<any, CreatePostState> {
 
   get params(): PostFormParams {
     let urlParams = new URLSearchParams(this.props.location.search);
-    let name = toOption(urlParams.get("community_name")).or(
-      this.prevCommunityName
-    );
-    let id = toOption(urlParams.get("community_id"))
-      .map(Number)
-      .or(this.prevCommunityId);
-    let nameOrId: Option<Either<string, number>>;
-    if (name.isSome()) {
-      nameOrId = Some(Left(name.unwrap()));
-    } else if (id.isSome()) {
-      nameOrId = Some(Right(id.unwrap()));
-    } else {
-      nameOrId = None;
+    let name = urlParams.get("community_name") ?? this.prevCommunityName;
+    let communityIdParam = urlParams.get("community_id");
+    let id = communityIdParam ? Number(communityIdParam) : this.prevCommunityId;
+    let nameOrId: string | number | undefined;
+    if (name) {
+      nameOrId = name;
+    } else if (id) {
+      nameOrId = id;
     }
 
     let params: PostFormParams = {
-      name: toOption(urlParams.get("title")),
+      name: urlParams.get("title") ?? undefined,
       nameOrId,
-      body: toOption(urlParams.get("body")),
-      url: toOption(urlParams.get("url")),
+      body: urlParams.get("body") ?? undefined,
+      url: urlParams.get("url") ?? undefined,
     };
 
     return params;
   }
 
-  get prevCommunityName(): Option<string> {
+  get prevCommunityName(): string | undefined {
     if (this.props.match.params.name) {
-      return toOption(this.props.match.params.name);
+      return this.props.match.params.name;
     } else if (this.props.location.state) {
       let lastLocation = this.props.location.state.prevPath;
       if (lastLocation.includes("/c/")) {
-        return toOption(lastLocation.split("/c/")[1]);
+        return lastLocation.split("/c/").at(1);
       }
     }
-    return None;
+    return undefined;
   }
 
-  get prevCommunityId(): Option<number> {
-    if (this.props.match.params.id) {
-      return toOption(this.props.match.params.id);
-    }
-    return None;
+  get prevCommunityId(): number | undefined {
+    // TODO is this actually a number? Whats the real return type
+    let id = this.props.match.params.id;
+    return id ?? undefined;
   }
 
   handlePostCreate(post_view: PostView) {
@@ -214,13 +192,12 @@ export class CreatePost extends Component<any, CreatePostState> {
   }
 
   static fetchInitialData(req: InitialFetchRequest): Promise<any>[] {
-    let listCommunitiesForm = new ListCommunities({
-      type_: Some(ListingType.All),
-      sort: Some(SortType.TopAll),
-      limit: Some(fetchLimit),
-      page: None,
+    let listCommunitiesForm: ListCommunities = {
+      type_: ListingType.All,
+      sort: SortType.TopAll,
+      limit: fetchLimit,
       auth: req.auth,
-    });
+    };
     return [req.client.listCommunities(listCommunitiesForm)];
   }
 
@@ -231,17 +208,14 @@ export class CreatePost extends Component<any, CreatePostState> {
       toast(i18n.t(msg.error), "danger");
       return;
     } else if (op == UserOperation.ListCommunities) {
-      let data = wsJsonToRes<ListCommunitiesResponse>(
-        msg,
-        ListCommunitiesResponse
-      );
-      this.setState({ listCommunitiesResponse: Some(data), loading: false });
+      let data = wsJsonToRes<ListCommunitiesResponse>(msg);
+      this.setState({ listCommunitiesResponse: data, loading: false });
     } else if (op == UserOperation.GetCommunity) {
-      let data = wsJsonToRes<GetCommunityResponse>(msg, GetCommunityResponse);
+      let data = wsJsonToRes<GetCommunityResponse>(msg);
       this.setState({
-        listCommunitiesResponse: Some({
+        listCommunitiesResponse: {
           communities: [data.community_view],
-        }),
+        },
         loading: false,
       });
     }
index a586064c4723380bd44a685df74c22640496f8ea..74a4ba63f14e8f07815b891527b4eb333a3781bc 100644 (file)
@@ -17,84 +17,66 @@ export class MetadataCard extends Component<
   MetadataCardProps,
   MetadataCardState
 > {
-  private emptyState: MetadataCardState = {
+  state: MetadataCardState = {
     expanded: false,
   };
 
   constructor(props: any, context: any) {
     super(props, context);
-    this.state = this.emptyState;
   }
 
   render() {
     let post = this.props.post;
     return (
       <>
-        {!this.state.expanded &&
-          post.embed_title.match({
-            some: embedTitle =>
-              post.url.match({
-                some: url => (
-                  <div className="card border-secondary mt-3 mb-2">
-                    <div className="row">
-                      <div className="col-12">
-                        <div className="card-body">
-                          {post.name !== embedTitle && (
-                            <>
-                              <h5 className="card-title d-inline">
-                                <a
-                                  className="text-body"
-                                  href={url}
-                                  rel={relTags}
-                                >
-                                  {embedTitle}
-                                </a>
-                              </h5>
-                              <span className="d-inline-block ml-2 mb-2 small text-muted">
-                                <a
-                                  className="text-muted font-italic"
-                                  href={url}
-                                  rel={relTags}
-                                >
-                                  {new URL(url).hostname}
-                                  <Icon icon="external-link" classes="ml-1" />
-                                </a>
-                              </span>
-                            </>
-                          )}
-                          {post.embed_description.match({
-                            some: desc => (
-                              <div
-                                className="card-text small text-muted md-div"
-                                dangerouslySetInnerHTML={{
-                                  __html: sanitizeHtml(desc),
-                                }}
-                              />
-                            ),
-                            none: <></>,
-                          })}
-                          {post.embed_video_url.isSome() && (
-                            <button
-                              className="mt-2 btn btn-secondary text-monospace"
-                              onClick={linkEvent(this, this.handleIframeExpand)}
-                            >
-                              {i18n.t("expand_here")}
-                            </button>
-                          )}
-                        </div>
-                      </div>
-                    </div>
-                  </div>
-                ),
-                none: <></>,
-              }),
-            none: <></>,
-          })}
-        {this.state.expanded &&
-          post.embed_video_url.match({
-            some: video_url => <iframe src={video_url}></iframe>,
-            none: <></>,
-          })}
+        {!this.state.expanded && post.embed_title && post.url && (
+          <div className="card border-secondary mt-3 mb-2">
+            <div className="row">
+              <div className="col-12">
+                <div className="card-body">
+                  {post.name !== post.embed_title && (
+                    <>
+                      <h5 className="card-title d-inline">
+                        <a className="text-body" href={post.url} rel={relTags}>
+                          {post.embed_title}
+                        </a>
+                      </h5>
+                      <span className="d-inline-block ml-2 mb-2 small text-muted">
+                        <a
+                          className="text-muted font-italic"
+                          href={post.url}
+                          rel={relTags}
+                        >
+                          {new URL(post.url).hostname}
+                          <Icon icon="external-link" classes="ml-1" />
+                        </a>
+                      </span>
+                    </>
+                  )}
+                  {post.embed_description && (
+                    <div
+                      className="card-text small text-muted md-div"
+                      dangerouslySetInnerHTML={{
+                        __html: sanitizeHtml(post.embed_description),
+                      }}
+                    />
+                  )}
+                  {post.embed_video_url && (
+                    <button
+                      className="mt-2 btn btn-secondary text-monospace"
+                      onClick={linkEvent(this, this.handleIframeExpand)}
+                    >
+                      {i18n.t("expand_here")}
+                    </button>
+                  )}
+                </div>
+              </div>
+            </div>
+          </div>
+        )}
+        {this.state.expanded && post.embed_video_url && (
+          <iframe src={post.embed_video_url}></iframe>
+        )}
       </>
     );
   }
index abfb5430f19bf5df14788452d6872df4814f2835..490f7dcd95ee0c0b9c97394b99a730b37bc632aa 100644 (file)
@@ -1,4 +1,3 @@
-import { None, Option, Some } from "@sniptt/monads";
 import autosize from "autosize";
 import { Component, linkEvent } from "inferno";
 import { Prompt } from "inferno-router";
@@ -14,7 +13,6 @@ import {
   SearchResponse,
   SearchType,
   SortType,
-  toUndefined,
   UserOperation,
   wsJsonToRes,
   wsUserOp,
@@ -26,7 +24,6 @@ import { PostFormParams } from "../../interfaces";
 import { UserService, WebSocketService } from "../../services";
 import {
   archiveTodayUrl,
-  auth,
   capitalizeFirstLetter,
   choicesConfig,
   communitySelectName,
@@ -37,6 +34,7 @@ import {
   ghostArchiveUrl,
   isBrowser,
   isImage,
+  myAuth,
   myFirstDiscussionLanguageId,
   pictrsDeleteToast,
   relTags,
@@ -62,11 +60,11 @@ if (isBrowser()) {
 const MAX_POST_TITLE_LENGTH = 200;
 
 interface PostFormProps {
-  post_view: Option<PostView>; // If a post is given, that means this is an edit
+  post_view?: PostView; // If a post is given, that means this is an edit
   allLanguages: Language[];
   siteLanguages: number[];
-  communities: Option<CommunityView[]>;
-  params: Option<PostFormParams>;
+  communities?: CommunityView[];
+  params?: PostFormParams;
   onCancel?(): any;
   onCreate?(post: PostView): any;
   onEdit?(post: PostView): any;
@@ -75,10 +73,18 @@ interface PostFormProps {
 }
 
 interface PostFormState {
-  postForm: CreatePost;
-  suggestedTitle: Option<string>;
-  suggestedPosts: Option<PostView[]>;
-  crossPosts: Option<PostView[]>;
+  form: {
+    name?: string;
+    url?: string;
+    body?: string;
+    nsfw?: boolean;
+    language_id?: number;
+    community_id?: number;
+    honeypot?: string;
+  };
+  suggestedTitle?: string;
+  suggestedPosts?: PostView[];
+  crossPosts?: PostView[];
   loading: boolean;
   imageLoading: boolean;
   communitySearchLoading: boolean;
@@ -86,26 +92,14 @@ interface PostFormState {
 }
 
 export class PostForm extends Component<PostFormProps, PostFormState> {
-  private subscription: Subscription;
+  private subscription?: Subscription;
   private choices: any;
-  private emptyState: PostFormState = {
-    postForm: new CreatePost({
-      community_id: undefined,
-      name: undefined,
-      nsfw: Some(false),
-      url: None,
-      body: None,
-      honeypot: None,
-      language_id: None,
-      auth: undefined,
-    }),
+  state: PostFormState = {
+    form: {},
     loading: false,
     imageLoading: false,
     communitySearchLoading: false,
     previewMode: false,
-    suggestedTitle: None,
-    suggestedPosts: None,
-    crossPosts: None,
   };
 
   constructor(props: any, context: any) {
@@ -115,37 +109,32 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
     this.handlePostBodyChange = this.handlePostBodyChange.bind(this);
     this.handleLanguageChange = this.handleLanguageChange.bind(this);
 
-    this.state = this.emptyState;
-
     this.parseMessage = this.parseMessage.bind(this);
     this.subscription = wsSubscribe(this.parseMessage);
 
     // Means its an edit
-    if (this.props.post_view.isSome()) {
-      let pv = this.props.post_view.unwrap();
-
+    let pv = this.props.post_view;
+    if (pv) {
       this.state = {
         ...this.state,
-        postForm: new CreatePost({
+        form: {
           body: pv.post.body,
           name: pv.post.name,
           community_id: pv.community.id,
           url: pv.post.url,
-          nsfw: Some(pv.post.nsfw),
-          honeypot: None,
-          language_id: Some(pv.post.language_id),
-          auth: auth().unwrap(),
-        }),
+          nsfw: pv.post.nsfw,
+          language_id: pv.post.language_id,
+        },
       };
     }
 
-    if (this.props.params.isSome()) {
-      let params = this.props.params.unwrap();
+    let params = this.props.params;
+    if (params) {
       this.state = {
         ...this.state,
-        postForm: {
-          ...this.state.postForm,
-          name: toUndefined(params.name),
+        form: {
+          ...this.state.form,
+          name: params.name,
           url: params.url,
           body: params.body,
         },
@@ -165,41 +154,39 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
   componentDidUpdate() {
     if (
       !this.state.loading &&
-      (this.state.postForm.name ||
-        this.state.postForm.url.isSome() ||
-        this.state.postForm.body.isSome())
+      (this.state.form.name || this.state.form.url || this.state.form.body)
     ) {
       window.onbeforeunload = () => true;
     } else {
-      window.onbeforeunload = undefined;
+      window.onbeforeunload = null;
     }
   }
 
   componentWillUnmount() {
-    this.subscription.unsubscribe();
+    this.subscription?.unsubscribe();
     /* this.choices && this.choices.destroy(); */
     window.onbeforeunload = null;
   }
 
   render() {
-    let selectedLangs = this.state.postForm.language_id
-      .or(
-        myFirstDiscussionLanguageId(
-          this.props.allLanguages,
-          this.props.siteLanguages,
-          UserService.Instance.myUserInfo
-        )
-      )
-      .map(Array.of);
+    let firstLang =
+      this.state.form.language_id ??
+      myFirstDiscussionLanguageId(
+        this.props.allLanguages,
+        this.props.siteLanguages,
+        UserService.Instance.myUserInfo
+      );
+    let selectedLangs = firstLang ? Array.of(firstLang) : undefined;
 
+    let url = this.state.form.url;
     return (
       <div>
         <Prompt
           when={
             !this.state.loading &&
-            (this.state.postForm.name ||
-              this.state.postForm.url.isSome() ||
-              this.state.postForm.body.isSome())
+            (this.state.form.name ||
+              this.state.form.url ||
+              this.state.form.body)
           }
           message={i18n.t("block_leaving")}
         />
@@ -213,27 +200,25 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
                 type="url"
                 id="post-url"
                 className="form-control"
-                value={toUndefined(this.state.postForm.url)}
+                value={this.state.form.url}
                 onInput={linkEvent(this, this.handlePostUrlChange)}
                 onPaste={linkEvent(this, this.handleImageUploadPaste)}
               />
-              {this.state.suggestedTitle.match({
-                some: title => (
-                  <div
-                    className="mt-1 text-muted small font-weight-bold pointer"
-                    role="button"
-                    onClick={linkEvent(this, this.copySuggestedTitle)}
-                  >
-                    {i18n.t("copy_suggested_title", { title: "" })} {title}
-                  </div>
-                ),
-                none: <></>,
-              })}
+              {this.state.suggestedTitle && (
+                <div
+                  className="mt-1 text-muted small font-weight-bold pointer"
+                  role="button"
+                  onClick={linkEvent(this, this.copySuggestedTitle)}
+                >
+                  {i18n.t("copy_suggested_title", { title: "" })}{" "}
+                  {this.state.suggestedTitle}
+                </div>
+              )}
               <form>
                 <label
                   htmlFor="file-upload"
                   className={`${
-                    UserService.Instance.myUserInfo.isSome() && "pointer"
+                    UserService.Instance.myUserInfo && "pointer"
                   } d-inline-block float-right text-muted font-weight-bold`}
                   data-tippy-content={i18n.t("upload_image")}
                 >
@@ -245,72 +230,58 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
                   accept="image/*,video/*"
                   name="file"
                   className="d-none"
-                  disabled={UserService.Instance.myUserInfo.isNone()}
+                  disabled={!UserService.Instance.myUserInfo}
                   onChange={linkEvent(this, this.handleImageUpload)}
                 />
               </form>
-              {this.state.postForm.url.match({
-                some: url =>
-                  validURL(url) && (
-                    <div>
-                      <a
-                        href={`${webArchiveUrl}/save/${encodeURIComponent(
-                          url
-                        )}`}
-                        className="mr-2 d-inline-block float-right text-muted small font-weight-bold"
-                        rel={relTags}
-                      >
-                        archive.org {i18n.t("archive_link")}
-                      </a>
-                      <a
-                        href={`${ghostArchiveUrl}/search?term=${encodeURIComponent(
-                          url
-                        )}`}
-                        className="mr-2 d-inline-block float-right text-muted small font-weight-bold"
-                        rel={relTags}
-                      >
-                        ghostarchive.org {i18n.t("archive_link")}
-                      </a>
-                      <a
-                        href={`${archiveTodayUrl}/?run=1&url=${encodeURIComponent(
-                          url
-                        )}`}
-                        className="mr-2 d-inline-block float-right text-muted small font-weight-bold"
-                        rel={relTags}
-                      >
-                        archive.today {i18n.t("archive_link")}
-                      </a>
-                    </div>
-                  ),
-                none: <></>,
-              })}
+              {url && validURL(url) && (
+                <div>
+                  <a
+                    href={`${webArchiveUrl}/save/${encodeURIComponent(url)}`}
+                    className="mr-2 d-inline-block float-right text-muted small font-weight-bold"
+                    rel={relTags}
+                  >
+                    archive.org {i18n.t("archive_link")}
+                  </a>
+                  <a
+                    href={`${ghostArchiveUrl}/search?term=${encodeURIComponent(
+                      url
+                    )}`}
+                    className="mr-2 d-inline-block float-right text-muted small font-weight-bold"
+                    rel={relTags}
+                  >
+                    ghostarchive.org {i18n.t("archive_link")}
+                  </a>
+                  <a
+                    href={`${archiveTodayUrl}/?run=1&url=${encodeURIComponent(
+                      url
+                    )}`}
+                    className="mr-2 d-inline-block float-right text-muted small font-weight-bold"
+                    rel={relTags}
+                  >
+                    archive.today {i18n.t("archive_link")}
+                  </a>
+                </div>
+              )}
               {this.state.imageLoading && <Spinner />}
-              {this.state.postForm.url.match({
-                some: url =>
-                  isImage(url) && (
-                    <img src={url} className="img-fluid" alt="" />
-                  ),
-                none: <></>,
-              })}
-              {this.state.crossPosts.match({
-                some: xPosts =>
-                  xPosts.length > 0 && (
-                    <>
-                      <div className="my-1 text-muted small font-weight-bold">
-                        {i18n.t("cross_posts")}
-                      </div>
-                      <PostListings
-                        showCommunity
-                        posts={xPosts}
-                        enableDownvotes={this.props.enableDownvotes}
-                        enableNsfw={this.props.enableNsfw}
-                        allLanguages={this.props.allLanguages}
-                        siteLanguages={this.props.siteLanguages}
-                      />
-                    </>
-                  ),
-                none: <></>,
-              })}
+              {url && isImage(url) && (
+                <img src={url} className="img-fluid" alt="" />
+              )}
+              {this.state.crossPosts && this.state.crossPosts.length > 0 && (
+                <>
+                  <div className="my-1 text-muted small font-weight-bold">
+                    {i18n.t("cross_posts")}
+                  </div>
+                  <PostListings
+                    showCommunity
+                    posts={this.state.crossPosts}
+                    enableDownvotes={this.props.enableDownvotes}
+                    enableNsfw={this.props.enableNsfw}
+                    allLanguages={this.props.allLanguages}
+                    siteLanguages={this.props.siteLanguages}
+                  />
+                </>
+              )}
             </div>
           </div>
           <div className="form-group row">
@@ -319,41 +290,38 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
             </label>
             <div className="col-sm-10">
               <textarea
-                value={this.state.postForm.name}
+                value={this.state.form.name}
                 id="post-title"
                 onInput={linkEvent(this, this.handlePostNameChange)}
                 className={`form-control ${
-                  !validTitle(this.state.postForm.name) && "is-invalid"
+                  !validTitle(this.state.form.name) && "is-invalid"
                 }`}
                 required
                 rows={1}
                 minLength={3}
                 maxLength={MAX_POST_TITLE_LENGTH}
               />
-              {!validTitle(this.state.postForm.name) && (
+              {!validTitle(this.state.form.name) && (
                 <div className="invalid-feedback">
                   {i18n.t("invalid_post_title")}
                 </div>
               )}
-              {this.state.suggestedPosts.match({
-                some: sPosts =>
-                  sPosts.length > 0 && (
-                    <>
-                      <div className="my-1 text-muted small font-weight-bold">
-                        {i18n.t("related_posts")}
-                      </div>
-                      <PostListings
-                        showCommunity
-                        posts={sPosts}
-                        enableDownvotes={this.props.enableDownvotes}
-                        enableNsfw={this.props.enableNsfw}
-                        allLanguages={this.props.allLanguages}
-                        siteLanguages={this.props.siteLanguages}
-                      />
-                    </>
-                  ),
-                none: <></>,
-              })}
+              {this.state.suggestedPosts &&
+                this.state.suggestedPosts.length > 0 && (
+                  <>
+                    <div className="my-1 text-muted small font-weight-bold">
+                      {i18n.t("related_posts")}
+                    </div>
+                    <PostListings
+                      showCommunity
+                      posts={this.state.suggestedPosts}
+                      enableDownvotes={this.props.enableDownvotes}
+                      enableNsfw={this.props.enableNsfw}
+                      allLanguages={this.props.allLanguages}
+                      siteLanguages={this.props.siteLanguages}
+                    />
+                  </>
+                )}
             </div>
           </div>
 
@@ -361,18 +329,14 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
             <label className="col-sm-2 col-form-label">{i18n.t("body")}</label>
             <div className="col-sm-10">
               <MarkdownTextArea
-                initialContent={this.state.postForm.body}
-                initialLanguageId={None}
+                initialContent={this.state.form.body}
                 onContentChange={this.handlePostBodyChange}
-                placeholder={None}
-                buttonTitle={None}
-                maxLength={None}
                 allLanguages={this.props.allLanguages}
                 siteLanguages={this.props.siteLanguages}
               />
             </div>
           </div>
-          {this.props.post_view.isNone() && (
+          {!this.props.post_view && (
             <div className="form-group row">
               <label
                 className="col-sm-2 col-form-label"
@@ -388,11 +352,11 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
                 <select
                   className="form-control"
                   id="post-community"
-                  value={this.state.postForm.community_id}
+                  value={this.state.form.community_id}
                   onInput={linkEvent(this, this.handlePostCommunityChange)}
                 >
                   <option>{i18n.t("select_a_community")}</option>
-                  {this.props.communities.unwrapOr([]).map(cv => (
+                  {this.props.communities?.map(cv => (
                     <option key={cv.community.id} value={cv.community.id}>
                       {communitySelectName(cv)}
                     </option>
@@ -412,7 +376,7 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
                     className="form-check-input position-static"
                     id="post-nsfw"
                     type="checkbox"
-                    checked={toUndefined(this.state.postForm.nsfw)}
+                    checked={this.state.form.nsfw}
                     onChange={linkEvent(this, this.handlePostNsfwChange)}
                   />
                 </div>
@@ -433,27 +397,25 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
             type="text"
             className="form-control honeypot"
             id="register-honey"
-            value={toUndefined(this.state.postForm.honeypot)}
+            value={this.state.form.honeypot}
             onInput={linkEvent(this, this.handleHoneyPotChange)}
           />
           <div className="form-group row">
             <div className="col-sm-10">
               <button
-                disabled={
-                  !this.state.postForm.community_id || this.state.loading
-                }
+                disabled={!this.state.form.community_id || this.state.loading}
                 type="submit"
                 className="btn btn-secondary mr-2"
               >
                 {this.state.loading ? (
                   <Spinner />
-                ) : this.props.post_view.isSome() ? (
+                ) : this.props.post_view ? (
                   capitalizeFirstLetter(i18n.t("save"))
                 ) : (
                   capitalizeFirstLetter(i18n.t("create"))
                 )}
               </button>
-              {this.props.post_view.isSome() && (
+              {this.props.post_view && (
                 <button
                   type="button"
                   className="btn btn-secondary"
@@ -475,139 +437,133 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
     i.setState({ loading: true });
 
     // Coerce empty url string to undefined
-    if (
-      i.state.postForm.url.isSome() &&
-      i.state.postForm.url.unwrapOr("blank") === ""
-    ) {
-      i.setState(s => ((s.postForm.url = None), s));
+    if ((i.state.form.url ?? "blank") === "") {
+      i.setState(s => ((s.form.url = undefined), s));
     }
 
-    let pForm = i.state.postForm;
-    i.props.post_view.match({
-      some: pv => {
-        let form = new EditPost({
-          name: Some(pForm.name),
+    let pForm = i.state.form;
+    let pv = i.props.post_view;
+    let auth = myAuth();
+    if (auth) {
+      if (pv) {
+        let form: EditPost = {
+          name: pForm.name,
           url: pForm.url,
           body: pForm.body,
           nsfw: pForm.nsfw,
           post_id: pv.post.id,
-          language_id: Some(pv.post.language_id),
-          auth: auth().unwrap(),
-        });
+          language_id: pv.post.language_id,
+          auth,
+        };
         WebSocketService.Instance.send(wsClient.editPost(form));
-      },
-      none: () => {
-        i.setState(s => ((s.postForm.auth = auth().unwrap()), s));
-        let form = new CreatePost({ ...i.state.postForm });
-        WebSocketService.Instance.send(wsClient.createPost(form));
-      },
-    });
+      } else {
+        if (pForm.name && pForm.community_id) {
+          let form: CreatePost = {
+            name: pForm.name,
+            community_id: pForm.community_id,
+            url: pForm.url,
+            body: pForm.body,
+            nsfw: pForm.nsfw,
+            language_id: pForm.language_id,
+            honeypot: pForm.honeypot,
+            auth,
+          };
+          WebSocketService.Instance.send(wsClient.createPost(form));
+        }
+      }
+    }
   }
 
   copySuggestedTitle(i: PostForm) {
-    i.state.suggestedTitle.match({
-      some: sTitle => {
-        i.setState(
-          s => (
-            (s.postForm.name = sTitle.substring(0, MAX_POST_TITLE_LENGTH)), s
-          )
-        );
-        i.setState({ suggestedTitle: None });
-        setTimeout(() => {
-          let textarea: any = document.getElementById("post-title");
-          autosize.update(textarea);
-        }, 10);
-      },
-      none: void 0,
-    });
+    let sTitle = i.state.suggestedTitle;
+    if (sTitle) {
+      i.setState(
+        s => ((s.form.name = sTitle?.substring(0, MAX_POST_TITLE_LENGTH)), s)
+      );
+      i.setState({ suggestedTitle: undefined });
+      setTimeout(() => {
+        let textarea: any = document.getElementById("post-title");
+        autosize.update(textarea);
+      }, 10);
+    }
   }
 
   handlePostUrlChange(i: PostForm, event: any) {
-    i.setState(s => ((s.postForm.url = Some(event.target.value)), s));
+    i.setState(s => ((s.form.url = event.target.value), s));
     i.fetchPageTitle();
   }
 
   fetchPageTitle() {
-    this.state.postForm.url.match({
-      some: url => {
-        if (validURL(url)) {
-          let form = new Search({
-            q: url,
-            community_id: None,
-            community_name: None,
-            creator_id: None,
-            type_: Some(SearchType.Url),
-            sort: Some(SortType.TopAll),
-            listing_type: Some(ListingType.All),
-            page: Some(1),
-            limit: Some(trendingFetchLimit),
-            auth: auth(false).ok(),
-          });
-
-          WebSocketService.Instance.send(wsClient.search(form));
-
-          // Fetch the page title
-          getSiteMetadata(url).then(d => {
-            this.setState({ suggestedTitle: d.metadata.title });
-          });
-        } else {
-          this.setState({ suggestedTitle: None, crossPosts: None });
-        }
-      },
-      none: void 0,
-    });
+    let url = this.state.form.url;
+    if (url && validURL(url)) {
+      let form: Search = {
+        q: url,
+        type_: SearchType.Url,
+        sort: SortType.TopAll,
+        listing_type: ListingType.All,
+        page: 1,
+        limit: trendingFetchLimit,
+        auth: myAuth(false),
+      };
+
+      WebSocketService.Instance.send(wsClient.search(form));
+
+      // Fetch the page title
+      getSiteMetadata(url).then(d => {
+        this.setState({ suggestedTitle: d.metadata.title });
+      });
+    } else {
+      this.setState({ suggestedTitle: undefined, crossPosts: undefined });
+    }
   }
 
   handlePostNameChange(i: PostForm, event: any) {
-    i.setState(s => ((s.postForm.name = event.target.value), s));
+    i.setState(s => ((s.form.name = event.target.value), s));
     i.fetchSimilarPosts();
   }
 
   fetchSimilarPosts() {
-    let form = new Search({
-      q: this.state.postForm.name,
-      type_: Some(SearchType.Posts),
-      sort: Some(SortType.TopAll),
-      listing_type: Some(ListingType.All),
-      community_id: Some(this.state.postForm.community_id),
-      community_name: None,
-      creator_id: None,
-      page: Some(1),
-      limit: Some(trendingFetchLimit),
-      auth: auth(false).ok(),
-    });
-
-    if (this.state.postForm.name !== "") {
+    let q = this.state.form.name;
+    if (q && q !== "") {
+      let form: Search = {
+        q,
+        type_: SearchType.Posts,
+        sort: SortType.TopAll,
+        listing_type: ListingType.All,
+        community_id: this.state.form.community_id,
+        page: 1,
+        limit: trendingFetchLimit,
+        auth: myAuth(false),
+      };
+
       WebSocketService.Instance.send(wsClient.search(form));
     } else {
-      this.setState({ suggestedPosts: None });
+      this.setState({ suggestedPosts: undefined });
     }
   }
 
   handlePostBodyChange(val: string) {
-    this.setState(s => ((s.postForm.body = Some(val)), s));
+    this.setState(s => ((s.form.body = val), s));
   }
 
   handlePostCommunityChange(i: PostForm, event: any) {
-    i.setState(
-      s => ((s.postForm.community_id = Number(event.target.value)), s)
-    );
+    i.setState(s => ((s.form.community_id = Number(event.target.value)), s));
   }
 
   handlePostNsfwChange(i: PostForm, event: any) {
-    i.setState(s => ((s.postForm.nsfw = Some(event.target.checked)), s));
+    i.setState(s => ((s.form.nsfw = event.target.checked), s));
   }
 
   handleLanguageChange(val: number[]) {
-    this.setState(s => ((s.postForm.language_id = Some(val[0])), s));
+    this.setState(s => ((s.form.language_id = val.at(0)), s));
   }
 
   handleHoneyPotChange(i: PostForm, event: any) {
-    i.setState(s => ((s.postForm.honeypot = Some(event.target.value)), s));
+    i.setState(s => ((s.form.honeypot = event.target.value), s));
   }
 
   handleCancel(i: PostForm) {
-    i.props.onCancel();
+    i.props.onCancel?.();
   }
 
   handlePreviewToggle(i: PostForm, event: any) {
@@ -649,7 +605,7 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
           let url = `${pictrsUri}/${hash}`;
           let deleteToken = res.files[0].delete_token;
           let deleteUrl = `${pictrsUri}/delete/${deleteToken}/${hash}`;
-          i.state.postForm.url = Some(url);
+          i.state.form.url = url;
           i.setState({ imageLoading: false });
           pictrsDeleteToast(
             `${i18n.t("click_to_delete_picture")}: ${file.name}`,
@@ -679,9 +635,7 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
           "choice",
           (e: any) => {
             this.setState(
-              s => (
-                (s.postForm.community_id = Number(e.detail.choice.value)), s
-              )
+              s => ((s.form.community_id = Number(e.detail.choice.value)), s)
             );
           },
           false
@@ -711,41 +665,31 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
       }
     }
 
-    this.props.post_view.match({
-      some: pv =>
-        this.setState(s => ((s.postForm.community_id = pv.community.id), s)),
-      none: void 0,
-    });
-    this.props.params.match({
-      some: params =>
-        params.nameOrId.match({
-          some: nameOrId =>
-            nameOrId.match({
-              left: name => {
-                let foundCommunityId = this.props.communities
-                  .unwrapOr([])
-                  .find(r => r.community.name == name).community.id;
-                this.setState(
-                  s => ((s.postForm.community_id = foundCommunityId), s)
-                );
-              },
-              right: id =>
-                this.setState(s => ((s.postForm.community_id = id), s)),
-            }),
-          none: void 0,
-        }),
-      none: void 0,
-    });
-
-    if (isBrowser() && this.state.postForm.community_id) {
-      this.choices.setChoiceByValue(
-        this.state.postForm.community_id.toString()
-      );
+    let pv = this.props.post_view;
+    this.setState(s => ((s.form.community_id = pv?.community.id), s));
+
+    let nameOrId = this.props.params?.nameOrId;
+    if (nameOrId) {
+      if (typeof nameOrId === "string") {
+        let name_ = nameOrId;
+        let foundCommunityId = this.props.communities?.find(
+          r => r.community.name == name_
+        )?.community.id;
+        this.setState(s => ((s.form.community_id = foundCommunityId), s));
+      } else {
+        let id = nameOrId;
+        this.setState(s => ((s.form.community_id = id), s));
+      }
+    }
+
+    if (isBrowser() && this.state.form.community_id) {
+      this.choices.setChoiceByValue(this.state.form.community_id.toString());
     }
     this.setState(this.state);
   }
 
   parseMessage(msg: any) {
+    let mui = UserService.Instance.myUserInfo;
     let op = wsUserOp(msg);
     console.log(msg);
     if (msg.error) {
@@ -754,33 +698,23 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
       this.setState({ loading: false });
       return;
     } else if (op == UserOperation.CreatePost) {
-      let data = wsJsonToRes<PostResponse>(msg, PostResponse);
-      UserService.Instance.myUserInfo.match({
-        some: mui => {
-          if (data.post_view.creator.id == mui.local_user_view.person.id) {
-            this.props.onCreate(data.post_view);
-          }
-        },
-        none: void 0,
-      });
+      let data = wsJsonToRes<PostResponse>(msg);
+      if (data.post_view.creator.id == mui?.local_user_view.person.id) {
+        this.props.onCreate?.(data.post_view);
+      }
     } else if (op == UserOperation.EditPost) {
-      let data = wsJsonToRes<PostResponse>(msg, PostResponse);
-      UserService.Instance.myUserInfo.match({
-        some: mui => {
-          if (data.post_view.creator.id == mui.local_user_view.person.id) {
-            this.setState({ loading: false });
-            this.props.onEdit(data.post_view);
-          }
-        },
-        none: void 0,
-      });
+      let data = wsJsonToRes<PostResponse>(msg);
+      if (data.post_view.creator.id == mui?.local_user_view.person.id) {
+        this.setState({ loading: false });
+        this.props.onEdit?.(data.post_view);
+      }
     } else if (op == UserOperation.Search) {
-      let data = wsJsonToRes<SearchResponse>(msg, SearchResponse);
+      let data = wsJsonToRes<SearchResponse>(msg);
 
       if (data.type_ == SearchType[SearchType.Posts]) {
-        this.setState({ suggestedPosts: Some(data.posts) });
+        this.setState({ suggestedPosts: data.posts });
       } else if (data.type_ == SearchType[SearchType.Url]) {
-        this.setState({ crossPosts: Some(data.posts) });
+        this.setState({ crossPosts: data.posts });
       }
     }
   }
index d5e26ceb0ea0d8f7914f8a3bd5f88eb28291e73b..f8140c3e990576bda8a15a167377d3746ff8de60 100644 (file)
@@ -1,4 +1,3 @@
-import { None, Option, Some } from "@sniptt/monads";
 import classNames from "classnames";
 import { Component, linkEvent } from "inferno";
 import { Link } from "inferno-router";
@@ -22,7 +21,6 @@ import {
   PurgePost,
   RemovePost,
   SavePost,
-  toUndefined,
   TransferCommunity,
 } from "lemmy-js-client";
 import { externalHost } from "../../env";
@@ -32,7 +30,6 @@ import { UserService, WebSocketService } from "../../services";
 import {
   amAdmin,
   amCommunityCreator,
-  auth,
   canAdmin,
   canMod,
   futureDaysToUnixTime,
@@ -45,6 +42,7 @@ import {
   mdNoImages,
   mdToHtml,
   mdToHtmlInline,
+  myAuth,
   numToSI,
   relTags,
   setupTippy,
@@ -63,15 +61,15 @@ interface PostListingState {
   showEdit: boolean;
   showRemoveDialog: boolean;
   showPurgeDialog: boolean;
-  purgeReason: Option<string>;
-  purgeType: PurgeType;
+  purgeReason?: string;
+  purgeType?: PurgeType;
   purgeLoading: boolean;
-  removeReason: Option<string>;
+  removeReason?: string;
   showBanDialog: boolean;
-  banReason: Option<string>;
-  banExpireDays: Option<number>;
-  banType: BanType;
-  removeData: boolean;
+  banReason?: string;
+  banExpireDays?: number;
+  banType?: BanType;
+  removeData?: boolean;
   showConfirmTransferSite: boolean;
   showConfirmTransferCommunity: boolean;
   imageExpanded: boolean;
@@ -80,8 +78,8 @@ interface PostListingState {
   showMoreMobile: boolean;
   showBody: boolean;
   showReportDialog: boolean;
-  reportReason: Option<string>;
-  my_vote: Option<number>;
+  reportReason?: string;
+  my_vote?: number;
   score: number;
   upvotes: number;
   downvotes: number;
@@ -89,9 +87,9 @@ interface PostListingState {
 
 interface PostListingProps {
   post_view: PostView;
-  duplicates: Option<PostView[]>;
-  moderators: Option<CommunityModeratorView[]>;
-  admins: Option<PersonViewSafe[]>;
+  duplicates?: PostView[];
+  moderators?: CommunityModeratorView[];
+  admins?: PersonViewSafe[];
   allLanguages: Language[];
   siteLanguages: number[];
   showCommunity?: boolean;
@@ -103,17 +101,13 @@ interface PostListingProps {
 }
 
 export class PostListing extends Component<PostListingProps, PostListingState> {
-  private emptyState: PostListingState = {
+  state: PostListingState = {
     showEdit: false,
     showRemoveDialog: false,
     showPurgeDialog: false,
-    purgeReason: None,
     purgeType: PurgeType.Person,
     purgeLoading: false,
-    removeReason: None,
     showBanDialog: false,
-    banReason: None,
-    banExpireDays: None,
     banType: BanType.Community,
     removeData: false,
     showConfirmTransferSite: false,
@@ -124,7 +118,6 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
     showMoreMobile: false,
     showBody: false,
     showReportDialog: false,
-    reportReason: None,
     my_vote: this.props.post_view.my_vote,
     score: this.props.post_view.counts.score,
     upvotes: this.props.post_view.counts.upvotes,
@@ -134,7 +127,6 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
   constructor(props: any, context: any) {
     super(props, context);
 
-    this.state = this.emptyState;
     this.handlePostLike = this.handlePostLike.bind(this);
     this.handlePostDisLike = this.handlePostDisLike.bind(this);
     this.handleEditPost = this.handleEditPost.bind(this);
@@ -161,17 +153,15 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
           <>
             {this.listing()}
             {this.state.imageExpanded && !this.props.hideImage && this.img}
-            {post.url.isSome() &&
-              this.showBody &&
-              post.embed_title.isSome() && <MetadataCard post={post} />}
+            {post.url && this.showBody && post.embed_title && (
+              <MetadataCard post={post} />
+            )}
             {this.showBody && this.body()}
           </>
         ) : (
           <div className="col-12">
             <PostForm
-              post_view={Some(this.props.post_view)}
-              communities={None}
-              params={None}
+              post_view={this.props.post_view}
               onEdit={this.handleEditPost}
               onCancel={this.handleEditCancel}
               enableNsfw={this.props.enableNsfw}
@@ -186,41 +176,41 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
   }
 
   body() {
-    return this.props.post_view.post.body.match({
-      some: body => (
-        <div className="col-12 card my-2 p-2">
-          {this.state.viewSource ? (
-            <pre>{body}</pre>
-          ) : (
-            <div className="md-div" dangerouslySetInnerHTML={mdToHtml(body)} />
-          )}
-        </div>
-      ),
-      none: <></>,
-    });
+    let body = this.props.post_view.post.body;
+    return body ? (
+      <div className="col-12 card my-2 p-2">
+        {this.state.viewSource ? (
+          <pre>{body}</pre>
+        ) : (
+          <div className="md-div" dangerouslySetInnerHTML={mdToHtml(body)} />
+        )}
+      </div>
+    ) : (
+      <></>
+    );
   }
 
   get img() {
-    return this.imageSrc.match({
-      some: src => (
-        <>
-          <div className="offset-sm-3 my-2 d-none d-sm-block">
-            <a href={src} className="d-inline-block">
-              <PictrsImage src={src} />
-            </a>
-          </div>
-          <div className="my-2 d-block d-sm-none">
-            <a
-              className="d-inline-block"
-              onClick={linkEvent(this, this.handleImageExpandClick)}
-            >
-              <PictrsImage src={src} />
-            </a>
-          </div>
-        </>
-      ),
-      none: <></>,
-    });
+    let src = this.imageSrc;
+    return src ? (
+      <>
+        <div className="offset-sm-3 my-2 d-none d-sm-block">
+          <a href={src} className="d-inline-block">
+            <PictrsImage src={src} />
+          </a>
+        </div>
+        <div className="my-2 d-block d-sm-none">
+          <a
+            className="d-inline-block"
+            onClick={linkEvent(this, this.handleImageExpandClick)}
+          >
+            <PictrsImage src={src} />
+          </a>
+        </div>
+      </>
+    ) : (
+      <></>
+    );
   }
 
   imgThumb(src: string) {
@@ -235,23 +225,23 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
     );
   }
 
-  get imageSrc(): Option<string> {
+  get imageSrc(): string | undefined {
     let post = this.props.post_view.post;
     let url = post.url;
     let thumbnail = post.thumbnail_url;
 
-    if (url.isSome() && isImage(url.unwrap())) {
-      if (url.unwrap().includes("pictrs")) {
+    if (url && isImage(url)) {
+      if (url.includes("pictrs")) {
         return url;
-      } else if (thumbnail.isSome()) {
+      } else if (thumbnail) {
         return thumbnail;
       } else {
         return url;
       }
-    } else if (thumbnail.isSome()) {
+    } else if (thumbnail) {
       return thumbnail;
     } else {
-      return None;
+      return undefined;
     }
   }
 
@@ -260,33 +250,33 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
     let url = post.url;
     let thumbnail = post.thumbnail_url;
 
-    if (!this.props.hideImage && url.isSome() && isImage(url.unwrap())) {
+    if (!this.props.hideImage && url && isImage(url) && this.imageSrc) {
       return (
         <a
-          href={this.imageSrc.unwrap()}
+          href={this.imageSrc}
           className="text-body d-inline-block position-relative mb-2"
           data-tippy-content={i18n.t("expand_here")}
           onClick={linkEvent(this, this.handleImageExpandClick)}
           aria-label={i18n.t("expand_here")}
         >
-          {this.imgThumb(this.imageSrc.unwrap())}
+          {this.imgThumb(this.imageSrc)}
           <Icon icon="image" classes="mini-overlay" />
         </a>
       );
-    } else if (!this.props.hideImage && url.isSome() && thumbnail.isSome()) {
+    } else if (!this.props.hideImage && url && thumbnail && this.imageSrc) {
       return (
         <a
           className="text-body d-inline-block position-relative mb-2"
-          href={url.unwrap()}
+          href={url}
           rel={relTags}
-          title={url.unwrap()}
+          title={url}
         >
-          {this.imgThumb(this.imageSrc.unwrap())}
+          {this.imgThumb(this.imageSrc)}
           <Icon icon="external-link" classes="mini-overlay" />
         </a>
       );
-    } else if (url.isSome()) {
-      if (!this.props.hideImage && isVideo(url.unwrap())) {
+    } else if (url) {
+      if (!this.props.hideImage && isVideo(url)) {
         return (
           <div className="embed-responsive embed-responsive-16by9">
             <video
@@ -296,18 +286,13 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
               controls
               className="embed-responsive-item"
             >
-              <source src={url.unwrap()} type="video/mp4" />
+              <source src={url} type="video/mp4" />
             </video>
           </div>
         );
       } else {
         return (
-          <a
-            className="text-body"
-            href={url.unwrap()}
-            title={url.unwrap()}
-            rel={relTags}
-          >
+          <a className="text-body" href={url} title={url} rel={relTags}>
             <div className="thumbnail rounded bg-light d-flex justify-content-center">
               <Icon icon="external-link" classes="d-flex align-items-center" />
             </div>
@@ -331,6 +316,8 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
 
   createdLine() {
     let post_view = this.props.post_view;
+    let url = post_view.post.url;
+    let body = post_view.post.body;
     return (
       <ul className="list-inline mb-1 text-muted small">
         <li className="list-inline-item">
@@ -362,25 +349,21 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
           )}
         </li>
         <li className="list-inline-item">•</li>
-        {post_view.post.url.match({
-          some: url =>
-            !(hostname(url) == externalHost) && (
-              <>
-                <li className="list-inline-item">
-                  <a
-                    className="text-muted font-italic"
-                    href={url}
-                    title={url}
-                    rel={relTags}
-                  >
-                    {hostname(url)}
-                  </a>
-                </li>
-                <li className="list-inline-item">•</li>
-              </>
-            ),
-          none: <></>,
-        })}
+        {url && !(hostname(url) == externalHost) && (
+          <>
+            <li className="list-inline-item">
+              <a
+                className="text-muted font-italic"
+                href={url}
+                title={url}
+                rel={relTags}
+              >
+                {hostname(url)}
+              </a>
+            </li>
+            <li className="list-inline-item">•</li>
+          </>
+        )}
         <li className="list-inline-item">
           <span>
             <MomentTime
@@ -389,24 +372,21 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
             />
           </span>
         </li>
-        {post_view.post.body.match({
-          some: body => (
-            <>
-              <li className="list-inline-item">•</li>
-              <li className="list-inline-item">
-                <button
-                  className="text-muted btn btn-sm btn-link p-0"
-                  data-tippy-content={mdNoImages.render(body)}
-                  data-tippy-allowHtml={true}
-                  onClick={linkEvent(this, this.handleShowBody)}
-                >
-                  <Icon icon="book-open" classes="icon-inline mr-1" />
-                </button>
-              </li>
-            </>
-          ),
-          none: <></>,
-        })}
+        {body && (
+          <>
+            <li className="list-inline-item">•</li>
+            <li className="list-inline-item">
+              <button
+                className="text-muted btn btn-sm btn-link p-0"
+                data-tippy-content={mdNoImages.render(body)}
+                data-tippy-allowHtml={true}
+                onClick={linkEvent(this, this.handleShowBody)}
+              >
+                <Icon icon="book-open" classes="icon-inline mr-1" />
+              </button>
+            </li>
+          </>
+        )}
       </ul>
     );
   }
@@ -416,7 +396,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
       <div className={`vote-bar col-1 pr-0 small text-center`}>
         <button
           className={`btn-animate btn btn-link p-0 ${
-            this.state.my_vote.unwrapOr(0) == 1 ? "text-info" : "text-muted"
+            this.state.my_vote == 1 ? "text-info" : "text-muted"
           }`}
           onClick={this.handlePostLike}
           data-tippy-content={i18n.t("upvote")}
@@ -437,9 +417,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
         {this.props.enableDownvotes && (
           <button
             className={`btn-animate btn btn-link p-0 ${
-              this.state.my_vote.unwrapOr(0) == -1
-                ? "text-danger"
-                : "text-muted"
+              this.state.my_vote == -1 ? "text-danger" : "text-muted"
             }`}
             onClick={this.handlePostDisLike}
             data-tippy-content={i18n.t("downvote")}
@@ -471,43 +449,46 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
 
   postTitleLine() {
     let post = this.props.post_view.post;
+    let url = post.url;
+
     return (
       <div className="post-title overflow-hidden">
         <h5>
-          {post.url.match({
-            some: url =>
-              this.props.showBody ? (
-                <a
-                  className={
-                    !post.featured_community && !post.featured_local
-                      ? "text-body"
-                      : "text-primary"
-                  }
-                  href={url}
-                  title={url}
-                  rel={relTags}
-                >
-                  <div dangerouslySetInnerHTML={mdToHtmlInline(post.name)} />
-                </a>
-              ) : (
-                this.postLink
-              ),
-            none: this.postLink,
-          })}
-          {post.url.map(isImage).or(post.thumbnail_url).unwrapOr(false) && (
-            <button
-              className="btn btn-link text-monospace text-muted small d-inline-block ml-2"
-              data-tippy-content={i18n.t("expand_here")}
-              onClick={linkEvent(this, this.handleImageExpandClick)}
-            >
-              <Icon
-                icon={
-                  !this.state.imageExpanded ? "plus-square" : "minus-square"
+          {url ? (
+            this.props.showBody ? (
+              <a
+                className={
+                  !post.featured_community && !post.featured_local
+                    ? "text-body"
+                    : "text-primary"
                 }
-                classes="icon-inline"
-              />
-            </button>
+                href={url}
+                title={url}
+                rel={relTags}
+              >
+                <div dangerouslySetInnerHTML={mdToHtmlInline(post.name)} />
+              </a>
+            ) : (
+              this.postLink
+            )
+          ) : (
+            this.postLink
           )}
+          {(url && isImage(url)) ||
+            (post.thumbnail_url && (
+              <button
+                className="btn btn-link text-monospace text-muted small d-inline-block ml-2"
+                data-tippy-content={i18n.t("expand_here")}
+                onClick={linkEvent(this, this.handleImageExpandClick)}
+              >
+                <Icon
+                  icon={
+                    !this.state.imageExpanded ? "plus-square" : "minus-square"
+                  }
+                  classes="icon-inline"
+                />
+              </button>
+            ))}
           {post.removed && (
             <small className="ml-2 text-muted font-italic">
               {i18n.t("removed")}
@@ -556,30 +537,25 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
   }
 
   duplicatesLine() {
-    return this.props.duplicates.match({
-      some: dupes =>
-        dupes.length > 0 && (
-          <ul className="list-inline mb-1 small text-muted">
-            <>
-              <li className="list-inline-item mr-2">
-                {i18n.t("cross_posted_to")}
-              </li>
-              {dupes.map(pv => (
-                <li key={pv.post.id} className="list-inline-item mr-2">
-                  <Link to={`/post/${pv.post.id}`}>
-                    {pv.community.local
-                      ? pv.community.name
-                      : `${pv.community.name}@${hostname(
-                          pv.community.actor_id
-                        )}`}
-                  </Link>
-                </li>
-              ))}
-            </>
-          </ul>
-        ),
-      none: <></>,
-    });
+    let dupes = this.props.duplicates;
+    return dupes && dupes.length > 0 ? (
+      <ul className="list-inline mb-1 small text-muted">
+        <>
+          <li className="list-inline-item mr-2">{i18n.t("cross_posted_to")}</li>
+          {dupes.map(pv => (
+            <li key={pv.post.id} className="list-inline-item mr-2">
+              <Link to={`/post/${pv.post.id}`}>
+                {pv.community.local
+                  ? pv.community.name
+                  : `${pv.community.name}@${hostname(pv.community.actor_id)}`}
+              </Link>
+            </li>
+          ))}
+        </>
+      </ul>
+    ) : (
+      <></>
+    );
   }
 
   commentsLine(mobile = false) {
@@ -597,7 +573,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
           </a>
         )}
         {mobile && !this.props.viewOnly && this.mobileVotes}
-        {UserService.Instance.myUserInfo.isSome() &&
+        {UserService.Instance.myUserInfo &&
           !this.props.viewOnly &&
           this.postActions(mobile)}
       </div>
@@ -631,9 +607,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
         )}
         {this.state.showAdvanced && (
           <>
-            {this.showBody &&
-              post_view.post.body.isSome() &&
-              this.viewSourceButton}
+            {this.showBody && post_view.post.body && this.viewSourceButton}
             {this.canModOnSelf_ && (
               <>
                 {this.lockButton}
@@ -667,26 +641,21 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
               formattedCount: numToSI(post_view.counts.comments),
             })}
           </span>
-          {this.unreadCount.match({
-            some: unreadCount => (
-              <span className="small text-warning">
-                ({unreadCount} {i18n.t("new")})
-              </span>
-            ),
-            none: <></>,
-          })}
+          {this.unreadCount && (
+            <span className="small text-warning">
+              ({this.unreadCount} {i18n.t("new")})
+            </span>
+          )}
         </Link>
       </button>
     );
   }
 
-  get unreadCount(): Option<number> {
+  get unreadCount(): number | undefined {
     let pv = this.props.post_view;
-    if (pv.unread_comments == pv.counts.comments || pv.unread_comments == 0) {
-      return None;
-    } else {
-      return Some(pv.unread_comments);
-    }
+    return pv.unread_comments == pv.counts.comments || pv.unread_comments == 0
+      ? undefined
+      : pv.unread_comments;
   }
 
   get mobileVotes() {
@@ -697,7 +666,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
         <div>
           <button
             className={`btn-animate btn py-0 px-1 ${
-              this.state.my_vote.unwrapOr(0) == 1 ? "text-info" : "text-muted"
+              this.state.my_vote == 1 ? "text-info" : "text-muted"
             }`}
             {...tippy}
             onClick={this.handlePostLike}
@@ -711,9 +680,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
           {this.props.enableDownvotes && (
             <button
               className={`ml-2 btn-animate btn py-0 px-1 ${
-                this.state.my_vote.unwrapOr(0) == -1
-                  ? "text-danger"
-                  : "text-muted"
+                this.state.my_vote == -1 ? "text-danger" : "text-muted"
               }`}
               onClick={this.handlePostDisLike}
               {...tippy}
@@ -988,7 +955,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
             </>
           )}
           {/* Community creators and admins can transfer community to another mod */}
-          {(amCommunityCreator(this.props.moderators, post_view.creator.id) ||
+          {(amCommunityCreator(post_view.creator.id, this.props.moderators) ||
             this.canAdmin_) &&
             this.creatorIsMod_ &&
             (!this.state.showConfirmTransferCommunity ? (
@@ -1091,12 +1058,10 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
 
   removeAndBanDialogs() {
     let post = this.props.post_view;
-    let purgeTypeText: string;
-    if (this.state.purgeType == PurgeType.Post) {
-      purgeTypeText = i18n.t("purge_post");
-    } else if (this.state.purgeType == PurgeType.Person) {
-      purgeTypeText = `${i18n.t("purge")} ${post.creator.name}`;
-    }
+    let purgeTypeText =
+      this.state.purgeType == PurgeType.Post
+        ? i18n.t("purge_post")
+        : `${i18n.t("purge")} ${post.creator.name}`;
     return (
       <>
         {this.state.showRemoveDialog && (
@@ -1112,7 +1077,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
               id="post-listing-remove-reason"
               className="form-control mr-2"
               placeholder={i18n.t("reason")}
-              value={toUndefined(this.state.removeReason)}
+              value={this.state.removeReason}
               onInput={linkEvent(this, this.handleModRemoveReasonChange)}
             />
             <button
@@ -1138,7 +1103,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
                 id="post-listing-ban-reason"
                 className="form-control mr-2"
                 placeholder={i18n.t("reason")}
-                value={toUndefined(this.state.banReason)}
+                value={this.state.banReason}
                 onInput={linkEvent(this, this.handleModBanReasonChange)}
               />
               <label className="col-form-label" htmlFor={`mod-ban-expires`}>
@@ -1149,7 +1114,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
                 id={`mod-ban-expires`}
                 className="form-control mr-2"
                 placeholder={i18n.t("number_of_days")}
-                value={toUndefined(this.state.banExpireDays)}
+                value={this.state.banExpireDays}
                 onInput={linkEvent(this, this.handleModBanExpireDaysChange)}
               />
               <div className="form-group">
@@ -1201,7 +1166,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
               className="form-control mr-2"
               placeholder={i18n.t("reason")}
               required
-              value={toUndefined(this.state.reportReason)}
+              value={this.state.reportReason}
               onInput={linkEvent(this, this.handleReportReasonChange)}
             />
             <button
@@ -1227,7 +1192,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
               id="purge-reason"
               className="form-control mr-2"
               placeholder={i18n.t("reason")}
-              value={toUndefined(this.state.purgeReason)}
+              value={this.state.purgeReason}
               onInput={linkEvent(this, this.handlePurgeReasonChange)}
             />
             {this.state.purgeLoading ? (
@@ -1249,8 +1214,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
 
   mobileThumbnail() {
     let post = this.props.post_view.post;
-    return post.thumbnail_url.isSome() ||
-      post.url.map(isImage).unwrapOr(false) ? (
+    return post.thumbnail_url || (post.url && isImage(post.url)) ? (
       <div className="row">
         <div className={`${this.state.imageExpanded ? "col-12" : "col-8"}`}>
           {this.postTitleLine()}
@@ -1266,13 +1230,11 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
   }
 
   showMobilePreview() {
-    let post = this.props.post_view.post;
-    return (
-      !this.showBody &&
-      post.body.match({
-        some: body => <div className="md-div mb-1 preview-lines">{body}</div>,
-        none: <></>,
-      })
+    let body = this.props.post_view.post.body;
+    return !this.showBody && body ? (
+      <div className="md-div mb-1 preview-lines">{body}</div>
+    ) : (
+      <></>
     );
   }
 
@@ -1325,20 +1287,19 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
   }
 
   private get myPost(): boolean {
-    return UserService.Instance.myUserInfo.match({
-      some: mui =>
-        this.props.post_view.creator.id == mui.local_user_view.person.id,
-      none: false,
-    });
+    return (
+      this.props.post_view.creator.id ==
+      UserService.Instance.myUserInfo?.local_user_view.person.id
+    );
   }
 
   handlePostLike(event: any) {
     event.preventDefault();
-    if (UserService.Instance.myUserInfo.isNone()) {
+    if (!UserService.Instance.myUserInfo) {
       this.context.router.history.push(`/login`);
     }
 
-    let myVote = this.state.my_vote.unwrapOr(0);
+    let myVote = this.state.my_vote;
     let newVote = myVote == 1 ? 0 : 1;
 
     if (myVote == 1) {
@@ -1359,26 +1320,29 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
       });
     }
 
-    this.setState({ my_vote: Some(newVote) });
+    this.setState({ my_vote: newVote });
 
-    let form = new CreatePostLike({
-      post_id: this.props.post_view.post.id,
-      score: newVote,
-      auth: auth().unwrap(),
-    });
+    let auth = myAuth();
+    if (auth) {
+      let form: CreatePostLike = {
+        post_id: this.props.post_view.post.id,
+        score: newVote,
+        auth,
+      };
 
-    WebSocketService.Instance.send(wsClient.likePost(form));
-    this.setState(this.state);
+      WebSocketService.Instance.send(wsClient.likePost(form));
+      this.setState(this.state);
+    }
     setupTippy();
   }
 
   handlePostDisLike(event: any) {
     event.preventDefault();
-    if (UserService.Instance.myUserInfo.isNone()) {
+    if (!UserService.Instance.myUserInfo) {
       this.context.router.history.push(`/login`);
     }
 
-    let myVote = this.state.my_vote.unwrapOr(0);
+    let myVote = this.state.my_vote;
     let newVote = myVote == -1 ? 0 : -1;
 
     if (myVote == 1) {
@@ -1399,16 +1363,19 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
       });
     }
 
-    this.setState({ my_vote: Some(newVote) });
+    this.setState({ my_vote: newVote });
 
-    let form = new CreatePostLike({
-      post_id: this.props.post_view.post.id,
-      score: newVote,
-      auth: auth().unwrap(),
-    });
+    let auth = myAuth();
+    if (auth) {
+      let form: CreatePostLike = {
+        post_id: this.props.post_view.post.id,
+        score: newVote,
+        auth,
+      };
 
-    WebSocketService.Instance.send(wsClient.likePost(form));
-    this.setState(this.state);
+      WebSocketService.Instance.send(wsClient.likePost(form));
+      this.setState(this.state);
+    }
     setupTippy();
   }
 
@@ -1430,70 +1397,87 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
   }
 
   handleReportReasonChange(i: PostListing, event: any) {
-    i.setState({ reportReason: Some(event.target.value) });
+    i.setState({ reportReason: event.target.value });
   }
 
   handleReportSubmit(i: PostListing, event: any) {
     event.preventDefault();
-    let form = new CreatePostReport({
-      post_id: i.props.post_view.post.id,
-      reason: toUndefined(i.state.reportReason),
-      auth: auth().unwrap(),
-    });
-    WebSocketService.Instance.send(wsClient.createPostReport(form));
+    let auth = myAuth();
+    let reason = i.state.reportReason;
+    if (auth && reason) {
+      let form: CreatePostReport = {
+        post_id: i.props.post_view.post.id,
+        reason,
+        auth,
+      };
+      WebSocketService.Instance.send(wsClient.createPostReport(form));
 
-    i.setState({ showReportDialog: false });
+      i.setState({ showReportDialog: false });
+    }
   }
 
   handleBlockUserClick(i: PostListing) {
-    let blockUserForm = new BlockPerson({
-      person_id: i.props.post_view.creator.id,
-      block: true,
-      auth: auth().unwrap(),
-    });
-    WebSocketService.Instance.send(wsClient.blockPerson(blockUserForm));
+    let auth = myAuth();
+    if (auth) {
+      let blockUserForm: BlockPerson = {
+        person_id: i.props.post_view.creator.id,
+        block: true,
+        auth,
+      };
+      WebSocketService.Instance.send(wsClient.blockPerson(blockUserForm));
+    }
   }
 
   handleDeleteClick(i: PostListing) {
-    let deleteForm = new DeletePost({
-      post_id: i.props.post_view.post.id,
-      deleted: !i.props.post_view.post.deleted,
-      auth: auth().unwrap(),
-    });
-    WebSocketService.Instance.send(wsClient.deletePost(deleteForm));
+    let auth = myAuth();
+    if (auth) {
+      let deleteForm: DeletePost = {
+        post_id: i.props.post_view.post.id,
+        deleted: !i.props.post_view.post.deleted,
+        auth,
+      };
+      WebSocketService.Instance.send(wsClient.deletePost(deleteForm));
+    }
   }
 
   handleSavePostClick(i: PostListing) {
-    let saved =
-      i.props.post_view.saved == undefined ? true : !i.props.post_view.saved;
-    let form = new SavePost({
-      post_id: i.props.post_view.post.id,
-      save: saved,
-      auth: auth().unwrap(),
-    });
-
-    WebSocketService.Instance.send(wsClient.savePost(form));
+    let auth = myAuth();
+    if (auth) {
+      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,
+      };
+      WebSocketService.Instance.send(wsClient.savePost(form));
+    }
   }
 
   get crossPostParams(): string {
     let post = this.props.post_view.post;
     let params = `?title=${encodeURIComponent(post.name)}`;
 
-    if (post.url.isSome()) {
-      params += `&url=${encodeURIComponent(post.url.unwrap())}`;
+    if (post.url) {
+      params += `&url=${encodeURIComponent(post.url)}`;
     }
-    if (post.body.isSome()) {
-      params += `&body=${encodeURIComponent(this.crossPostBody())}`;
+    let crossPostBody = this.crossPostBody();
+    if (crossPostBody) {
+      params += `&body=${encodeURIComponent(crossPostBody)}`;
     }
     return params;
   }
 
-  crossPostBody(): string {
+  crossPostBody(): string | undefined {
     let post = this.props.post_view.post;
-    let body = `${i18n.t("cross_posted_from")} ${post.ap_id}\n\n${post.body
-      .unwrap()
-      .replace(/^/gm, "> ")}`;
-    return body;
+    let body = post.body;
+
+    return body
+      ? `${i18n.t("cross_posted_from")} ${post.ap_id}\n\n${body.replace(
+          /^/gm,
+          "> "
+        )}`
+      : undefined;
   }
 
   get showBody(): boolean {
@@ -1508,7 +1492,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
   }
 
   handleModRemoveReasonChange(i: PostListing, event: any) {
-    i.setState({ removeReason: Some(event.target.value) });
+    i.setState({ removeReason: event.target.value });
   }
 
   handleModRemoveDataChange(i: PostListing, event: any) {
@@ -1517,38 +1501,50 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
 
   handleModRemoveSubmit(i: PostListing, event: any) {
     event.preventDefault();
-    let form = new RemovePost({
-      post_id: i.props.post_view.post.id,
-      removed: !i.props.post_view.post.removed,
-      reason: i.state.removeReason,
-      auth: auth().unwrap(),
-    });
-    WebSocketService.Instance.send(wsClient.removePost(form));
 
-    i.setState({ showRemoveDialog: false });
+    let auth = myAuth();
+    if (auth) {
+      let form: RemovePost = {
+        post_id: i.props.post_view.post.id,
+        removed: !i.props.post_view.post.removed,
+        reason: i.state.removeReason,
+        auth,
+      };
+      WebSocketService.Instance.send(wsClient.removePost(form));
+      i.setState({ showRemoveDialog: false });
+    }
   }
 
   handleModLock(i: PostListing) {
-    let form = new LockPost({
-      post_id: i.props.post_view.post.id,
-      locked: !i.props.post_view.post.locked,
-      auth: auth().unwrap(),
-    });
-    WebSocketService.Instance.send(wsClient.lockPost(form));
+    let auth = myAuth();
+    if (auth) {
+      let form: LockPost = {
+        post_id: i.props.post_view.post.id,
+        locked: !i.props.post_view.post.locked,
+        auth,
+      };
+      WebSocketService.Instance.send(wsClient.lockPost(form));
+    }
   }
 
   handleModFeaturePost(i: PostListing, is_community: boolean) {
-    let form = new FeaturePost({
-      post_id: i.props.post_view.post.id,
-      featured: is_community
-        ? !i.props.post_view.post.featured_community
-        : !i.props.post_view.post.featured_local,
-      feature_type: is_community
-        ? PostFeatureType.Community
-        : PostFeatureType.Local,
-      auth: auth().unwrap(),
-    });
-    WebSocketService.Instance.send(wsClient.featurePost(form));
+    let auth = myAuth();
+    if (auth) {
+      let featured: [PostFeatureType, boolean] = is_community
+        ? [
+            PostFeatureType.Community,
+            !i.props.post_view.post.featured_community,
+          ]
+        : [PostFeatureType.Local, !i.props.post_view.post.featured_local];
+
+      let form: FeaturePost = {
+        post_id: i.props.post_view.post.id,
+        feature_type: featured[0],
+        featured: featured[1],
+        auth,
+      };
+      WebSocketService.Instance.send(wsClient.featurePost(form));
+    }
   }
 
   handleModBanFromCommunityShow(i: PostListing) {
@@ -1584,37 +1580,40 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
   }
 
   handlePurgeReasonChange(i: PostListing, event: any) {
-    i.setState({ purgeReason: Some(event.target.value) });
+    i.setState({ purgeReason: event.target.value });
   }
 
   handlePurgeSubmit(i: PostListing, event: any) {
     event.preventDefault();
 
-    if (i.state.purgeType == PurgeType.Person) {
-      let form = new PurgePerson({
-        person_id: i.props.post_view.creator.id,
-        reason: i.state.purgeReason,
-        auth: auth().unwrap(),
-      });
-      WebSocketService.Instance.send(wsClient.purgePerson(form));
-    } else if (i.state.purgeType == PurgeType.Post) {
-      let form = new PurgePost({
-        post_id: i.props.post_view.post.id,
-        reason: i.state.purgeReason,
-        auth: auth().unwrap(),
-      });
-      WebSocketService.Instance.send(wsClient.purgePost(form));
-    }
+    let auth = myAuth();
+    if (auth) {
+      if (i.state.purgeType == PurgeType.Person) {
+        let form: PurgePerson = {
+          person_id: i.props.post_view.creator.id,
+          reason: i.state.purgeReason,
+          auth,
+        };
+        WebSocketService.Instance.send(wsClient.purgePerson(form));
+      } else if (i.state.purgeType == PurgeType.Post) {
+        let form: PurgePost = {
+          post_id: i.props.post_view.post.id,
+          reason: i.state.purgeReason,
+          auth,
+        };
+        WebSocketService.Instance.send(wsClient.purgePost(form));
+      }
 
-    i.setState({ purgeLoading: true });
+      i.setState({ purgeLoading: true });
+    }
   }
 
   handleModBanReasonChange(i: PostListing, event: any) {
-    i.setState({ banReason: Some(event.target.value) });
+    i.setState({ banReason: event.target.value });
   }
 
   handleModBanExpireDaysChange(i: PostListing, event: any) {
-    i.setState({ banExpireDays: Some(event.target.value) });
+    i.setState({ banExpireDays: event.target.value });
   }
 
   handleModBanFromCommunitySubmit(i: PostListing) {
@@ -1629,62 +1628,76 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
 
   handleModBanBothSubmit(i: PostListing, event?: any) {
     if (event) event.preventDefault();
-
-    if (i.state.banType == BanType.Community) {
-      // If its an unban, restore all their data
+    let auth = myAuth();
+    if (auth) {
       let ban = !i.props.post_view.creator_banned_from_community;
-      if (ban == false) {
-        i.setState({ removeData: false });
-      }
-      let form = new BanFromCommunity({
-        person_id: i.props.post_view.creator.id,
-        community_id: i.props.post_view.community.id,
-        ban,
-        remove_data: Some(i.state.removeData),
-        reason: i.state.banReason,
-        expires: i.state.banExpireDays.map(futureDaysToUnixTime),
-        auth: auth().unwrap(),
-      });
-      WebSocketService.Instance.send(wsClient.banFromCommunity(form));
-    } else {
-      // If its an unban, restore all their data
-      let ban = !i.props.post_view.creator.banned;
-      if (ban == false) {
-        i.setState({ removeData: false });
+      let person_id = i.props.post_view.creator.id;
+      let remove_data = i.state.removeData;
+      let reason = i.state.banReason;
+      let expires = futureDaysToUnixTime(i.state.banExpireDays);
+
+      if (i.state.banType == BanType.Community) {
+        // If its an unban, restore all their data
+        if (ban == false) {
+          i.setState({ removeData: false });
+        }
+
+        let form: BanFromCommunity = {
+          person_id,
+          community_id: i.props.post_view.community.id,
+          ban,
+          remove_data,
+          reason,
+          expires,
+          auth,
+        };
+        WebSocketService.Instance.send(wsClient.banFromCommunity(form));
+      } else {
+        // If its an unban, restore all their data
+        let ban = !i.props.post_view.creator.banned;
+        if (ban == false) {
+          i.setState({ removeData: false });
+        }
+        let form: BanPerson = {
+          person_id,
+          ban,
+          remove_data,
+          reason,
+          expires,
+          auth,
+        };
+        WebSocketService.Instance.send(wsClient.banPerson(form));
       }
-      let form = new BanPerson({
-        person_id: i.props.post_view.creator.id,
-        ban,
-        remove_data: Some(i.state.removeData),
-        reason: i.state.banReason,
-        expires: i.state.banExpireDays.map(futureDaysToUnixTime),
-        auth: auth().unwrap(),
-      });
-      WebSocketService.Instance.send(wsClient.banPerson(form));
-    }
 
-    i.setState({ showBanDialog: false });
+      i.setState({ showBanDialog: false });
+    }
   }
 
   handleAddModToCommunity(i: PostListing) {
-    let form = new AddModToCommunity({
-      person_id: i.props.post_view.creator.id,
-      community_id: i.props.post_view.community.id,
-      added: !i.creatorIsMod_,
-      auth: auth().unwrap(),
-    });
-    WebSocketService.Instance.send(wsClient.addModToCommunity(form));
-    i.setState(i.state);
+    let auth = myAuth();
+    if (auth) {
+      let form: AddModToCommunity = {
+        person_id: i.props.post_view.creator.id,
+        community_id: i.props.post_view.community.id,
+        added: !i.creatorIsMod_,
+        auth,
+      };
+      WebSocketService.Instance.send(wsClient.addModToCommunity(form));
+      i.setState(i.state);
+    }
   }
 
   handleAddAdmin(i: PostListing) {
-    let form = new AddAdmin({
-      person_id: i.props.post_view.creator.id,
-      added: !i.creatorIsAdmin_,
-      auth: auth().unwrap(),
-    });
-    WebSocketService.Instance.send(wsClient.addAdmin(form));
-    i.setState(i.state);
+    let auth = myAuth();
+    if (auth) {
+      let form: AddAdmin = {
+        person_id: i.props.post_view.creator.id,
+        added: !i.creatorIsAdmin_,
+        auth,
+      };
+      WebSocketService.Instance.send(wsClient.addAdmin(form));
+      i.setState(i.state);
+    }
   }
 
   handleShowConfirmTransferCommunity(i: PostListing) {
@@ -1696,13 +1709,16 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
   }
 
   handleTransferCommunity(i: PostListing) {
-    let form = new TransferCommunity({
-      community_id: i.props.post_view.community.id,
-      person_id: i.props.post_view.creator.id,
-      auth: auth().unwrap(),
-    });
-    WebSocketService.Instance.send(wsClient.transferCommunity(form));
-    i.setState({ showConfirmTransferCommunity: false });
+    let auth = myAuth();
+    if (auth) {
+      let form: TransferCommunity = {
+        community_id: i.props.post_view.community.id,
+        person_id: i.props.post_view.creator.id,
+        auth,
+      };
+      WebSocketService.Instance.send(wsClient.transferCommunity(form));
+      i.setState({ showConfirmTransferCommunity: false });
+    }
   }
 
   handleShowConfirmTransferSite(i: PostListing) {
@@ -1762,9 +1778,9 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
 
   get canModOnSelf_(): boolean {
     return canMod(
+      this.props.post_view.creator.id,
       this.props.moderators,
       this.props.admins,
-      this.props.post_view.creator.id,
       undefined,
       true
     );
@@ -1772,21 +1788,21 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
 
   get canMod_(): boolean {
     return canMod(
+      this.props.post_view.creator.id,
       this.props.moderators,
-      this.props.admins,
-      this.props.post_view.creator.id
+      this.props.admins
     );
   }
 
   get canAdmin_(): boolean {
-    return canAdmin(this.props.admins, this.props.post_view.creator.id);
+    return canAdmin(this.props.post_view.creator.id, this.props.admins);
   }
 
   get creatorIsMod_(): boolean {
-    return isMod(this.props.moderators, this.props.post_view.creator.id);
+    return isMod(this.props.post_view.creator.id, this.props.moderators);
   }
 
   get creatorIsAdmin_(): boolean {
-    return isAdmin(this.props.admins, this.props.post_view.creator.id);
+    return isAdmin(this.props.post_view.creator.id, this.props.admins);
   }
 }
index e5d3e3bccc3cae3d3c6fb6df03bc057773cc8546..ab7692da337204befd891f755ff4e6f95d2736cc 100644 (file)
@@ -1,4 +1,3 @@
-import { None, Some } from "@sniptt/monads";
 import { Component } from "inferno";
 import { T } from "inferno-i18next-dess";
 import { Link } from "inferno-router";
@@ -12,8 +11,8 @@ interface PostListingsProps {
   siteLanguages: number[];
   showCommunity?: boolean;
   removeDuplicates?: boolean;
-  enableDownvotes: boolean;
-  enableNsfw: boolean;
+  enableDownvotes?: boolean;
+  enableNsfw?: boolean;
 }
 
 export class PostListings extends Component<PostListingsProps, any> {
@@ -37,9 +36,7 @@ export class PostListings extends Component<PostListingsProps, any> {
             <>
               <PostListing
                 post_view={post_view}
-                duplicates={Some(this.duplicatesMap.get(post_view.post.id))}
-                moderators={None}
-                admins={None}
+                duplicates={this.duplicatesMap.get(post_view.post.id)}
                 showCommunity={this.props.showCommunity}
                 enableDownvotes={this.props.enableDownvotes}
                 enableNsfw={this.props.enableNsfw}
@@ -72,20 +69,20 @@ export class PostListings extends Component<PostListingsProps, any> {
 
     // Loop over the posts, find ones with same urls
     for (let pv of posts) {
-      !pv.post.deleted &&
+      let url = pv.post.url;
+      if (
+        !pv.post.deleted &&
         !pv.post.removed &&
         !pv.community.deleted &&
         !pv.community.removed &&
-        pv.post.url.match({
-          some: url => {
-            if (!urlMap.get(url)) {
-              urlMap.set(url, [pv]);
-            } else {
-              urlMap.get(url).push(pv);
-            }
-          },
-          none: void 0,
-        });
+        url
+      ) {
+        if (!urlMap.get(url)) {
+          urlMap.set(url, [pv]);
+        } else {
+          urlMap.get(url)?.push(pv);
+        }
+      }
     }
 
     // Sort by oldest
@@ -100,22 +97,20 @@ export class PostListings extends Component<PostListingsProps, any> {
 
     for (let i = 0; i < posts.length; i++) {
       let pv = posts[i];
-      pv.post.url.match({
-        some: url => {
-          let found = urlMap.get(url);
-          if (found) {
-            // If its the oldest, add
-            if (pv.post.id == found[0].post.id) {
-              this.duplicatesMap.set(pv.post.id, found.slice(1));
-            }
-            // Otherwise, delete it
-            else {
-              posts.splice(i--, 1);
-            }
+      let url = pv.post.url;
+      if (url) {
+        let found = urlMap.get(url);
+        if (found) {
+          // If its the oldest, add
+          if (pv.post.id == found[0].post.id) {
+            this.duplicatesMap.set(pv.post.id, found.slice(1));
+          }
+          // Otherwise, delete it
+          else {
+            posts.splice(i--, 1);
           }
-        },
-        none: void 0,
-      });
+        }
+      }
     }
 
     return posts;
index 7cb9e43619e066c82e499ccffc348375b242e893..2b1540e0b1a76ac68732183a809ad3f5c0b69178 100644 (file)
@@ -1,4 +1,3 @@
-import { None } from "@sniptt/monads";
 import { Component, linkEvent } from "inferno";
 import { T } from "inferno-i18next-dess";
 import {
@@ -9,7 +8,7 @@ import {
 } from "lemmy-js-client";
 import { i18n } from "../../i18next";
 import { WebSocketService } from "../../services";
-import { auth, wsClient } from "../../utils";
+import { myAuth, wsClient } from "../../utils";
 import { Icon } from "../common/icon";
 import { PersonListing } from "../person/person-listing";
 import { PostListing } from "./post-listing";
@@ -25,6 +24,7 @@ export class PostReport extends Component<PostReportProps, any> {
 
   render() {
     let r = this.props.report;
+    let resolver = r.resolver;
     let post = r.post;
     let tippyContent = i18n.t(
       r.post_report.resolved ? "unresolve_report" : "resolve_report"
@@ -52,9 +52,6 @@ export class PostReport extends Component<PostReportProps, any> {
       <div>
         <PostListing
           post_view={pv}
-          duplicates={None}
-          moderators={None}
-          admins={None}
           showCommunity={true}
           enableDownvotes={true}
           enableNsfw={true}
@@ -69,24 +66,21 @@ export class PostReport extends Component<PostReportProps, any> {
         <div>
           {i18n.t("reason")}: {r.post_report.reason}
         </div>
-        {r.resolver.match({
-          some: resolver => (
-            <div>
-              {r.post_report.resolved ? (
-                <T i18nKey="resolved_by">
-                  #
-                  <PersonListing person={resolver} />
-                </T>
-              ) : (
-                <T i18nKey="unresolved_by">
-                  #
-                  <PersonListing person={resolver} />
-                </T>
-              )}
-            </div>
-          ),
-          none: <></>,
-        })}
+        {resolver && (
+          <div>
+            {r.post_report.resolved ? (
+              <T i18nKey="resolved_by">
+                #
+                <PersonListing person={resolver} />
+              </T>
+            ) : (
+              <T i18nKey="unresolved_by">
+                #
+                <PersonListing person={resolver} />
+              </T>
+            )}
+          </div>
+        )}
         <button
           className="btn btn-link btn-animate text-muted py-0"
           onClick={linkEvent(this, this.handleResolveReport)}
@@ -105,11 +99,14 @@ export class PostReport extends Component<PostReportProps, any> {
   }
 
   handleResolveReport(i: PostReport) {
-    let form = new ResolvePostReport({
-      report_id: i.props.report.post_report.id,
-      resolved: !i.props.report.post_report.resolved,
-      auth: auth().unwrap(),
-    });
-    WebSocketService.Instance.send(wsClient.resolvePostReport(form));
+    let auth = myAuth();
+    if (auth) {
+      let form: ResolvePostReport = {
+        report_id: i.props.report.post_report.id,
+        resolved: !i.props.report.post_report.resolved,
+        auth,
+      };
+      WebSocketService.Instance.send(wsClient.resolvePostReport(form));
+    }
   }
 }
index d2d5f84322283eae00bb2a7e8557f0b02dd26739..637cc927de6bcffa3de451ab28334693b2d21edb 100644 (file)
@@ -1,4 +1,3 @@
-import { None, Option, Right, Some } from "@sniptt/monads";
 import autosize from "autosize";
 import { Component, createRef, linkEvent, RefObject } from "inferno";
 import {
@@ -27,7 +26,6 @@ import {
   SearchResponse,
   SearchType,
   SortType,
-  toOption,
   UserOperation,
   wsJsonToRes,
   wsUserOp,
@@ -37,7 +35,6 @@ import { i18n } from "../../i18next";
 import { CommentViewType, InitialFetchRequest } from "../../interfaces";
 import { UserService, WebSocketService } from "../../services";
 import {
-  auth,
   buildCommentsTree,
   commentsToFlatNodes,
   commentTreeMaxDepth,
@@ -54,6 +51,7 @@ import {
   insertCommentIntoTree,
   isBrowser,
   isImage,
+  myAuth,
   restoreScrollPosition,
   saveCommentRes,
   saveScrollPosition,
@@ -75,16 +73,16 @@ import { PostListing } from "./post-listing";
 const commentsShownInterval = 15;
 
 interface PostState {
-  postId: Option<number>;
-  commentId: Option<number>;
-  postRes: Option<GetPostResponse>;
-  commentsRes: Option<GetCommentsResponse>;
+  postId?: number;
+  commentId?: number;
+  postRes?: GetPostResponse;
+  commentsRes?: GetCommentsResponse;
   commentTree: CommentNodeI[];
   commentSort: CommentSortType;
   commentViewType: CommentViewType;
   scrolled?: boolean;
   loading: boolean;
-  crossPosts: Option<PostView[]>;
+  crossPosts?: PostView[];
   siteRes: GetSiteResponse;
   commentSectionRef?: RefObject<HTMLDivElement>;
   showSidebarMobile: boolean;
@@ -92,16 +90,10 @@ interface PostState {
 }
 
 export class Post extends Component<any, PostState> {
-  private subscription: Subscription;
-  private isoData = setIsoData(
-    this.context,
-    GetPostResponse,
-    GetCommentsResponse
-  );
+  private subscription?: Subscription;
+  private isoData = setIsoData(this.context);
   private commentScrollDebounced: () => void;
-  private emptyState: PostState = {
-    postRes: None,
-    commentsRes: None,
+  state: PostState = {
     postId: getIdFromProps(this.props),
     commentId: getCommentIdFromProps(this.props),
     commentTree: [],
@@ -109,9 +101,7 @@ export class Post extends Component<any, PostState> {
     commentViewType: CommentViewType.Tree,
     scrolled: false,
     loading: true,
-    crossPosts: None,
     siteRes: this.isoData.site_res,
-    commentSectionRef: null,
     showSidebarMobile: false,
     maxCommentsShown: commentsShownInterval,
   };
@@ -119,8 +109,6 @@ export class Post extends Component<any, PostState> {
   constructor(props: any, context: any) {
     super(props, context);
 
-    this.state = this.emptyState;
-
     this.parseMessage = this.parseMessage.bind(this);
     this.subscription = wsSubscribe(this.parseMessage);
 
@@ -130,16 +118,16 @@ export class Post extends Component<any, PostState> {
     if (this.isoData.path == this.context.router.route.match.url) {
       this.state = {
         ...this.state,
-        postRes: Some(this.isoData.routeData[0] as GetPostResponse),
-        commentsRes: Some(this.isoData.routeData[1] as GetCommentsResponse),
+        postRes: this.isoData.routeData[0] as GetPostResponse,
+        commentsRes: this.isoData.routeData[1] as GetCommentsResponse,
       };
 
-      if (this.state.commentsRes.isSome()) {
+      if (this.state.commentsRes) {
         this.state = {
           ...this.state,
           commentTree: buildCommentsTree(
-            this.state.commentsRes.unwrap().comments,
-            this.state.commentId.isSome()
+            this.state.commentsRes.comments,
+            !!this.state.commentId
           ),
         };
       }
@@ -147,18 +135,19 @@ export class Post extends Component<any, PostState> {
       this.state = { ...this.state, loading: false };
 
       if (isBrowser()) {
-        WebSocketService.Instance.send(
-          wsClient.communityJoin({
-            community_id:
-              this.state.postRes.unwrap().community_view.community.id,
-          })
-        );
+        if (this.state.postRes) {
+          WebSocketService.Instance.send(
+            wsClient.communityJoin({
+              community_id: this.state.postRes.community_view.community.id,
+            })
+          );
+        }
 
-        this.state.postId.match({
-          some: post_id =>
-            WebSocketService.Instance.send(wsClient.postJoin({ post_id })),
-          none: void 0,
-        });
+        if (this.state.postId) {
+          WebSocketService.Instance.send(
+            wsClient.postJoin({ post_id: this.state.postId })
+          );
+        }
 
         this.fetchCrossPosts();
 
@@ -172,87 +161,70 @@ export class Post extends Component<any, PostState> {
   }
 
   fetchPost() {
-    this.setState({ commentsRes: None });
-    let postForm = new GetPost({
+    this.setState({ commentsRes: undefined, postRes: undefined });
+    let auth = myAuth(false);
+    let postForm: GetPost = {
       id: this.state.postId,
       comment_id: this.state.commentId,
-      auth: auth(false).ok(),
-    });
+      auth,
+    };
     WebSocketService.Instance.send(wsClient.getPost(postForm));
 
-    let commentsForm = new GetComments({
+    let commentsForm: GetComments = {
       post_id: this.state.postId,
       parent_id: this.state.commentId,
-      max_depth: Some(commentTreeMaxDepth),
-      page: None,
-      limit: None,
-      sort: Some(this.state.commentSort),
-      type_: Some(ListingType.All),
-      community_name: None,
-      community_id: None,
-      saved_only: Some(false),
-      auth: auth(false).ok(),
-    });
+      max_depth: commentTreeMaxDepth,
+      sort: this.state.commentSort,
+      type_: ListingType.All,
+      saved_only: false,
+      auth,
+    };
     WebSocketService.Instance.send(wsClient.getComments(commentsForm));
   }
 
   fetchCrossPosts() {
-    this.state.postRes
-      .andThen(r => r.post_view.post.url)
-      .match({
-        some: url => {
-          let form = new Search({
-            q: url,
-            type_: Some(SearchType.Url),
-            sort: Some(SortType.TopAll),
-            listing_type: Some(ListingType.All),
-            page: Some(1),
-            limit: Some(trendingFetchLimit),
-            community_id: None,
-            community_name: None,
-            creator_id: None,
-            auth: auth(false).ok(),
-          });
-          WebSocketService.Instance.send(wsClient.search(form));
-        },
-        none: void 0,
-      });
+    let q = this.state.postRes?.post_view.post.url;
+    if (q) {
+      let form: Search = {
+        q,
+        type_: SearchType.Url,
+        sort: SortType.TopAll,
+        listing_type: ListingType.All,
+        page: 1,
+        limit: trendingFetchLimit,
+        auth: myAuth(false),
+      };
+      WebSocketService.Instance.send(wsClient.search(form));
+    }
   }
 
   static fetchInitialData(req: InitialFetchRequest): Promise<any>[] {
     let pathSplit = req.path.split("/");
     let promises: Promise<any>[] = [];
 
-    let pathType = pathSplit[1];
-    let id = Number(pathSplit[2]);
+    let pathType = pathSplit.at(1);
+    let id = pathSplit.at(2) ? Number(pathSplit.at(2)) : undefined;
+    let auth = req.auth;
 
-    let postForm = new GetPost({
-      id: None,
-      comment_id: None,
-      auth: req.auth,
-    });
+    let postForm: GetPost = {
+      auth,
+    };
 
-    let commentsForm = new GetComments({
-      post_id: None,
-      parent_id: None,
-      max_depth: Some(commentTreeMaxDepth),
-      page: None,
-      limit: None,
-      sort: Some(CommentSortType.Hot),
-      type_: Some(ListingType.All),
-      community_name: None,
-      community_id: None,
-      saved_only: Some(false),
-      auth: req.auth,
-    });
+    let commentsForm: GetComments = {
+      max_depth: commentTreeMaxDepth,
+      sort: CommentSortType.Hot,
+      type_: ListingType.All,
+      saved_only: false,
+      auth,
+    };
 
     // Set the correct id based on the path type
     if (pathType == "post") {
-      postForm.id = Some(id);
-      commentsForm.post_id = Some(id);
+      postForm.id = id;
+      commentsForm.post_id = id;
     } else {
-      postForm.comment_id = Some(id);
-      commentsForm.parent_id = Some(id);
+      postForm.comment_id = id;
+      commentsForm.parent_id = id;
     }
 
     promises.push(req.client.getPost(postForm));
@@ -262,7 +234,7 @@ export class Post extends Component<any, PostState> {
   }
 
   componentWillUnmount() {
-    this.subscription.unsubscribe();
+    this.subscription?.unsubscribe();
     document.removeEventListener("scroll", this.commentScrollDebounced);
 
     saveScrollPosition(this.context);
@@ -295,7 +267,7 @@ export class Post extends Component<any, PostState> {
   }
 
   scrollIntoCommentSection() {
-    this.state.commentSectionRef.current?.scrollIntoView();
+    this.state.commentSectionRef?.current?.scrollIntoView();
   }
 
   isBottom(el: Element): boolean {
@@ -315,31 +287,21 @@ export class Post extends Component<any, PostState> {
   };
 
   get documentTitle(): string {
-    return this.state.postRes.match({
-      some: res =>
-        `${res.post_view.post.name} - ${this.state.siteRes.site_view.site.name}`,
-      none: "",
-    });
+    let name_ = this.state.postRes?.post_view.post.name;
+    let siteName = this.state.siteRes.site_view.site.name;
+    return name_ ? `${name_} - ${siteName}` : "";
   }
 
-  get imageTag(): Option<string> {
-    return this.state.postRes.match({
-      some: res =>
-        res.post_view.post.thumbnail_url.or(
-          res.post_view.post.url.match({
-            some: url => (isImage(url) ? Some(url) : None),
-            none: None,
-          })
-        ),
-      none: None,
-    });
-  }
-
-  get descriptionTag(): Option<string> {
-    return this.state.postRes.andThen(r => r.post_view.post.body);
+  get imageTag(): string | undefined {
+    let post = this.state.postRes?.post_view.post;
+    let thumbnail = post?.thumbnail_url;
+    let url = post?.url;
+    return thumbnail || (url && isImage(url) ? url : undefined);
   }
 
   render() {
+    let res = this.state.postRes;
+    let description = res?.post_view.post.body;
     return (
       <div className="container-lg">
         {this.state.loading ? (
@@ -347,65 +309,60 @@ export class Post extends Component<any, PostState> {
             <Spinner large />
           </h5>
         ) : (
-          this.state.postRes.match({
-            some: res => (
-              <div className="row">
-                <div className="col-12 col-md-8 mb-3">
-                  <HtmlTags
-                    title={this.documentTitle}
-                    path={this.context.router.route.match.url}
-                    image={this.imageTag}
-                    description={this.descriptionTag}
-                  />
-                  <PostListing
-                    post_view={res.post_view}
-                    duplicates={this.state.crossPosts}
-                    showBody
-                    showCommunity
-                    moderators={Some(res.moderators)}
-                    admins={Some(this.state.siteRes.admins)}
-                    enableDownvotes={enableDownvotes(this.state.siteRes)}
-                    enableNsfw={enableNsfw(this.state.siteRes)}
-                    allLanguages={this.state.siteRes.all_languages}
-                    siteLanguages={this.state.siteRes.discussion_languages}
-                  />
-                  <div ref={this.state.commentSectionRef} className="mb-2" />
-                  <CommentForm
-                    node={Right(res.post_view.post.id)}
-                    disabled={res.post_view.post.locked}
-                    allLanguages={this.state.siteRes.all_languages}
-                    siteLanguages={this.state.siteRes.discussion_languages}
-                  />
-                  <div className="d-block d-md-none">
-                    <button
-                      className="btn btn-secondary d-inline-block mb-2 mr-3"
-                      onClick={linkEvent(this, this.handleShowSidebarMobile)}
-                    >
-                      {i18n.t("sidebar")}{" "}
-                      <Icon
-                        icon={
-                          this.state.showSidebarMobile
-                            ? `minus-square`
-                            : `plus-square`
-                        }
-                        classes="icon-inline"
-                      />
-                    </button>
-                    {this.state.showSidebarMobile && this.sidebar()}
-                  </div>
-                  {this.sortRadios()}
-                  {this.state.commentViewType == CommentViewType.Tree &&
-                    this.commentsTree()}
-                  {this.state.commentViewType == CommentViewType.Flat &&
-                    this.commentsFlat()}
-                </div>
-                <div className="d-none d-md-block col-md-4">
-                  {this.sidebar()}
+          res && (
+            <div className="row">
+              <div className="col-12 col-md-8 mb-3">
+                <HtmlTags
+                  title={this.documentTitle}
+                  path={this.context.router.route.match.url}
+                  image={this.imageTag}
+                  description={description}
+                />
+                <PostListing
+                  post_view={res.post_view}
+                  duplicates={this.state.crossPosts}
+                  showBody
+                  showCommunity
+                  moderators={res.moderators}
+                  admins={this.state.siteRes.admins}
+                  enableDownvotes={enableDownvotes(this.state.siteRes)}
+                  enableNsfw={enableNsfw(this.state.siteRes)}
+                  allLanguages={this.state.siteRes.all_languages}
+                  siteLanguages={this.state.siteRes.discussion_languages}
+                />
+                <div ref={this.state.commentSectionRef} className="mb-2" />
+                <CommentForm
+                  node={res.post_view.post.id}
+                  disabled={res.post_view.post.locked}
+                  allLanguages={this.state.siteRes.all_languages}
+                  siteLanguages={this.state.siteRes.discussion_languages}
+                />
+                <div className="d-block d-md-none">
+                  <button
+                    className="btn btn-secondary d-inline-block mb-2 mr-3"
+                    onClick={linkEvent(this, this.handleShowSidebarMobile)}
+                  >
+                    {i18n.t("sidebar")}{" "}
+                    <Icon
+                      icon={
+                        this.state.showSidebarMobile
+                          ? `minus-square`
+                          : `plus-square`
+                      }
+                      classes="icon-inline"
+                    />
+                  </button>
+                  {this.state.showSidebarMobile && this.sidebar()}
                 </div>
+                {this.sortRadios()}
+                {this.state.commentViewType == CommentViewType.Tree &&
+                  this.commentsTree()}
+                {this.state.commentViewType == CommentViewType.Flat &&
+                  this.commentsFlat()}
               </div>
-            ),
-            none: <></>,
-          })
+              <div className="d-none d-md-block col-md-4">{this.sidebar()}</div>
+            </div>
+          )
         )}
       </div>
     );
@@ -493,35 +450,34 @@ export class Post extends Component<any, PostState> {
 
   commentsFlat() {
     // These are already sorted by new
-    return this.state.commentsRes.match({
-      some: commentsRes =>
-        this.state.postRes.match({
-          some: postRes => (
-            <div>
-              <CommentNodes
-                nodes={commentsToFlatNodes(commentsRes.comments)}
-                viewType={this.state.commentViewType}
-                maxCommentsShown={Some(this.state.maxCommentsShown)}
-                noIndent
-                locked={postRes.post_view.post.locked}
-                moderators={Some(postRes.moderators)}
-                admins={Some(this.state.siteRes.admins)}
-                enableDownvotes={enableDownvotes(this.state.siteRes)}
-                showContext
-                allLanguages={this.state.siteRes.all_languages}
-                siteLanguages={this.state.siteRes.discussion_languages}
-              />
-            </div>
-          ),
-          none: <></>,
-        }),
-      none: <></>,
-    });
+    let commentsRes = this.state.commentsRes;
+    let postRes = this.state.postRes;
+    return (
+      commentsRes &&
+      postRes && (
+        <div>
+          <CommentNodes
+            nodes={commentsToFlatNodes(commentsRes.comments)}
+            viewType={this.state.commentViewType}
+            maxCommentsShown={this.state.maxCommentsShown}
+            noIndent
+            locked={postRes.post_view.post.locked}
+            moderators={postRes.moderators}
+            admins={this.state.siteRes.admins}
+            enableDownvotes={enableDownvotes(this.state.siteRes)}
+            showContext
+            allLanguages={this.state.siteRes.all_languages}
+            siteLanguages={this.state.siteRes.discussion_languages}
+          />
+        </div>
+      )
+    );
   }
 
   sidebar() {
-    return this.state.postRes.match({
-      some: res => (
+    let res = this.state.postRes;
+    return (
+      res && (
         <div className="mb-3">
           <Sidebar
             community_view={res.community_view}
@@ -532,12 +488,10 @@ export class Post extends Component<any, PostState> {
             showIcon
             allLanguages={this.state.siteRes.all_languages}
             siteLanguages={this.state.siteRes.discussion_languages}
-            communityLanguages={None}
           />
         </div>
-      ),
-      none: <></>,
-    });
+      )
+    );
   }
 
   handleCommentSortChange(i: Post, event: any) {
@@ -549,14 +503,14 @@ export class Post extends Component<any, PostState> {
   }
 
   handleCommentViewTypeChange(i: Post, event: any) {
-    i.setState({
-      commentViewType: Number(event.target.value),
-      commentSort: CommentSortType.New,
-      commentTree: buildCommentsTree(
-        i.state.commentsRes.map(r => r.comments).unwrapOr([]),
-        i.state.commentId.isSome()
-      ),
-    });
+    let comments = i.state.commentsRes?.comments;
+    if (comments) {
+      i.setState({
+        commentViewType: Number(event.target.value),
+        commentSort: CommentSortType.New,
+        commentTree: buildCommentsTree(comments, !!i.state.commentId),
+      });
+    }
   }
 
   handleShowSidebarMobile(i: Post) {
@@ -564,33 +518,31 @@ export class Post extends Component<any, PostState> {
   }
 
   handleViewPost(i: Post) {
-    i.state.postRes.match({
-      some: res =>
-        i.context.router.history.push(`/post/${res.post_view.post.id}`),
-      none: void 0,
-    });
+    let id = i.state.postRes?.post_view.post.id;
+    if (id) {
+      i.context.router.history.push(`/post/${id}`);
+    }
   }
 
   handleViewContext(i: Post) {
-    i.state.commentsRes.match({
-      some: res =>
-        i.context.router.history.push(
-          `/comment/${getCommentParentId(res.comments[0].comment).unwrap()}`
-        ),
-      none: void 0,
-    });
+    let parentId = getCommentParentId(
+      i.state.commentsRes?.comments?.at(0)?.comment
+    );
+    if (parentId) {
+      i.context.router.history.push(`/comment/${parentId}`);
+    }
   }
 
   commentsTree() {
-    let showContextButton = toOption(this.state.commentTree[0]).match({
-      some: comment => getDepthFromComment(comment.comment_view.comment) > 0,
-      none: false,
-    });
+    let res = this.state.postRes;
+    let firstComment = this.state.commentTree.at(0)?.comment_view.comment;
+    let depth = getDepthFromComment(firstComment);
+    let showContextButton = depth ? depth > 0 : false;
 
-    return this.state.postRes.match({
-      some: res => (
+    return (
+      res && (
         <div>
-          {this.state.commentId.isSome() && (
+          {!!this.state.commentId && (
             <>
               <button
                 className="pl-0 d-block btn btn-link text-muted"
@@ -611,18 +563,17 @@ export class Post extends Component<any, PostState> {
           <CommentNodes
             nodes={this.state.commentTree}
             viewType={this.state.commentViewType}
-            maxCommentsShown={Some(this.state.maxCommentsShown)}
+            maxCommentsShown={this.state.maxCommentsShown}
             locked={res.post_view.post.locked}
-            moderators={Some(res.moderators)}
-            admins={Some(this.state.siteRes.admins)}
+            moderators={res.moderators}
+            admins={this.state.siteRes.admins}
             enableDownvotes={enableDownvotes(this.state.siteRes)}
             allLanguages={this.state.siteRes.all_languages}
             siteLanguages={this.state.siteRes.discussion_languages}
           />
         </div>
-      ),
-      none: <></>,
-    });
+      )
+    );
   }
 
   parseMessage(msg: any) {
@@ -632,25 +583,19 @@ export class Post extends Component<any, PostState> {
       toast(i18n.t(msg.error), "danger");
       return;
     } else if (msg.reconnect) {
-      this.state.postRes.match({
-        some: res => {
-          let postId = res.post_view.post.id;
-          WebSocketService.Instance.send(
-            wsClient.postJoin({ post_id: postId })
-          );
-          WebSocketService.Instance.send(
-            wsClient.getPost({
-              id: Some(postId),
-              comment_id: None,
-              auth: auth(false).ok(),
-            })
-          );
-        },
-        none: void 0,
-      });
+      let post_id = this.state.postRes?.post_view.post.id;
+      if (post_id) {
+        WebSocketService.Instance.send(wsClient.postJoin({ post_id }));
+        WebSocketService.Instance.send(
+          wsClient.getPost({
+            id: post_id,
+            auth: myAuth(false),
+          })
+        );
+      }
     } else if (op == UserOperation.GetPost) {
-      let data = wsJsonToRes<GetPostResponse>(msg, GetPostResponse);
-      this.setState({ postRes: Some(data) });
+      let data = wsJsonToRes<GetPostResponse>(msg);
+      this.setState({ postRes: data });
 
       // join the rooms
       WebSocketService.Instance.send(
@@ -666,60 +611,55 @@ export class Post extends Component<any, PostState> {
       // TODO move this into initial fetch and refetch
       this.fetchCrossPosts();
       setupTippy();
-      if (this.state.commentId.isNone()) restoreScrollPosition(this.context);
+      if (!this.state.commentId) restoreScrollPosition(this.context);
 
       if (this.checkScrollIntoCommentsParam) {
         this.scrollIntoCommentSection();
       }
     } else if (op == UserOperation.GetComments) {
-      let data = wsJsonToRes<GetCommentsResponse>(msg, GetCommentsResponse);
-      // You might need to append here, since this could be building more comments from a tree fetch
-      this.state.commentsRes.match({
-        some: res => {
-          // Remove the first comment, since it is the parent
-          let newComments = data.comments;
-          newComments.shift();
-          res.comments.push(...newComments);
-        },
-        none: () => this.setState({ commentsRes: Some(data) }),
-      });
-      // this.state.commentsRes = Some(data);
+      let data = wsJsonToRes<GetCommentsResponse>(msg);
+      // This section sets the comments res
+      let comments = this.state.commentsRes?.comments;
+      if (comments) {
+        // You might need to append here, since this could be building more comments from a tree fetch
+        // Remove the first comment, since it is the parent
+        let newComments = data.comments;
+        newComments.shift();
+        comments.push(...newComments);
+      } else {
+        this.setState({ commentsRes: data });
+      }
+
+      let cComments = this.state.commentsRes?.comments ?? [];
       this.setState({
-        commentTree: buildCommentsTree(
-          this.state.commentsRes.map(r => r.comments).unwrapOr([]),
-          this.state.commentId.isSome()
-        ),
+        commentTree: buildCommentsTree(cComments, !!this.state.commentId),
         loading: false,
       });
-      this.setState(this.state);
     } else if (op == UserOperation.CreateComment) {
-      let data = wsJsonToRes<CommentResponse>(msg, CommentResponse);
+      let data = wsJsonToRes<CommentResponse>(msg);
 
       // Don't get comments from the post room, if the creator is blocked
-      let creatorBlocked = UserService.Instance.myUserInfo
-        .map(m => m.person_blocks)
-        .unwrapOr([])
+      let creatorBlocked = UserService.Instance.myUserInfo?.person_blocks
         .map(pb => pb.target.id)
         .includes(data.comment_view.creator.id);
 
       // Necessary since it might be a user reply, which has the recipients, to avoid double
-      if (data.recipient_ids.length == 0 && !creatorBlocked) {
-        this.state.postRes.match({
-          some: postRes =>
-            this.state.commentsRes.match({
-              some: commentsRes => {
-                commentsRes.comments.unshift(data.comment_view);
-                insertCommentIntoTree(
-                  this.state.commentTree,
-                  data.comment_view,
-                  this.state.commentId.isSome()
-                );
-                postRes.post_view.counts.comments++;
-              },
-              none: void 0,
-            }),
-          none: void 0,
-        });
+      let postRes = this.state.postRes;
+      let commentsRes = this.state.commentsRes;
+      if (
+        data.recipient_ids.length == 0 &&
+        !creatorBlocked &&
+        postRes &&
+        commentsRes
+      ) {
+        commentsRes.comments.unshift(data.comment_view);
+        insertCommentIntoTree(
+          this.state.commentTree,
+          data.comment_view,
+          !!this.state.commentId
+        );
+        postRes.post_view.counts.comments++;
+
         this.setState(this.state);
         setupTippy();
       }
@@ -728,34 +668,22 @@ export class Post extends Component<any, PostState> {
       op == UserOperation.DeleteComment ||
       op == UserOperation.RemoveComment
     ) {
-      let data = wsJsonToRes<CommentResponse>(msg, CommentResponse);
-      editCommentRes(
-        data.comment_view,
-        this.state.commentsRes.map(r => r.comments).unwrapOr([])
-      );
+      let data = wsJsonToRes<CommentResponse>(msg);
+      editCommentRes(data.comment_view, this.state.commentsRes?.comments);
       this.setState(this.state);
       setupTippy();
     } else if (op == UserOperation.SaveComment) {
-      let data = wsJsonToRes<CommentResponse>(msg, CommentResponse);
-      saveCommentRes(
-        data.comment_view,
-        this.state.commentsRes.map(r => r.comments).unwrapOr([])
-      );
+      let data = wsJsonToRes<CommentResponse>(msg);
+      saveCommentRes(data.comment_view, this.state.commentsRes?.comments);
       this.setState(this.state);
       setupTippy();
     } else if (op == UserOperation.CreateCommentLike) {
-      let data = wsJsonToRes<CommentResponse>(msg, CommentResponse);
-      createCommentLikeRes(
-        data.comment_view,
-        this.state.commentsRes.map(r => r.comments).unwrapOr([])
-      );
+      let data = wsJsonToRes<CommentResponse>(msg);
+      createCommentLikeRes(data.comment_view, this.state.commentsRes?.comments);
       this.setState(this.state);
     } else if (op == UserOperation.CreatePostLike) {
-      let data = wsJsonToRes<PostResponse>(msg, PostResponse);
-      this.state.postRes.match({
-        some: res => createPostLikeRes(data.post_view, res.post_view),
-        none: void 0,
-      });
+      let data = wsJsonToRes<PostResponse>(msg);
+      createPostLikeRes(data.post_view, this.state.postRes?.post_view);
       this.setState(this.state);
     } else if (
       op == UserOperation.EditPost ||
@@ -765,112 +693,91 @@ export class Post extends Component<any, PostState> {
       op == UserOperation.FeaturePost ||
       op == UserOperation.SavePost
     ) {
-      let data = wsJsonToRes<PostResponse>(msg, PostResponse);
-      this.state.postRes.match({
-        some: res => (res.post_view = data.post_view),
-        none: void 0,
-      });
-      this.setState(this.state);
-      setupTippy();
+      let data = wsJsonToRes<PostResponse>(msg);
+      let pv = this.state.postRes?.post_view;
+      if (pv) {
+        pv = data.post_view;
+        this.setState(this.state);
+        setupTippy();
+      }
     } else if (
       op == UserOperation.EditCommunity ||
       op == UserOperation.DeleteCommunity ||
       op == UserOperation.RemoveCommunity ||
       op == UserOperation.FollowCommunity
     ) {
-      let data = wsJsonToRes<CommunityResponse>(msg, CommunityResponse);
-      this.state.postRes.match({
-        some: res => {
-          res.community_view = data.community_view;
-          res.post_view.community = data.community_view.community;
-          this.setState(this.state);
-        },
-        none: void 0,
-      });
+      let data = wsJsonToRes<CommunityResponse>(msg);
+      let res = this.state.postRes;
+      if (res) {
+        res.community_view = data.community_view;
+        res.post_view.community = data.community_view.community;
+        this.setState(this.state);
+      }
     } else if (op == UserOperation.BanFromCommunity) {
-      let data = wsJsonToRes<BanFromCommunityResponse>(
-        msg,
-        BanFromCommunityResponse
-      );
-      this.state.postRes.match({
-        some: postRes =>
-          this.state.commentsRes.match({
-            some: commentsRes => {
-              commentsRes.comments
-                .filter(c => c.creator.id == data.person_view.person.id)
-                .forEach(c => (c.creator_banned_from_community = data.banned));
-              if (postRes.post_view.creator.id == data.person_view.person.id) {
-                postRes.post_view.creator_banned_from_community = data.banned;
-              }
-              this.setState(this.state);
-            },
-            none: void 0,
-          }),
-        none: void 0,
-      });
+      let data = wsJsonToRes<BanFromCommunityResponse>(msg);
+
+      let postRes = this.state.postRes;
+      if (postRes) {
+        if (postRes.post_view.creator.id == data.person_view.person.id) {
+          postRes.post_view.creator_banned_from_community = data.banned;
+        }
+      }
+
+      this.state.commentsRes?.comments
+        .filter(c => c.creator.id == data.person_view.person.id)
+        .forEach(c => (c.creator_banned_from_community = data.banned));
+      this.setState(this.state);
     } else if (op == UserOperation.AddModToCommunity) {
-      let data = wsJsonToRes<AddModToCommunityResponse>(
-        msg,
-        AddModToCommunityResponse
-      );
-      this.state.postRes.match({
-        some: res => {
-          res.moderators = data.moderators;
-          this.setState(this.state);
-        },
-        none: void 0,
-      });
+      let data = wsJsonToRes<AddModToCommunityResponse>(msg);
+      let postRes = this.state.postRes;
+      if (postRes) {
+        postRes.moderators = data.moderators;
+        this.setState(this.state);
+      }
     } else if (op == UserOperation.BanPerson) {
-      let data = wsJsonToRes<BanPersonResponse>(msg, BanPersonResponse);
-      this.state.postRes.match({
-        some: postRes =>
-          this.state.commentsRes.match({
-            some: commentsRes => {
-              commentsRes.comments
-                .filter(c => c.creator.id == data.person_view.person.id)
-                .forEach(c => (c.creator.banned = data.banned));
-              if (postRes.post_view.creator.id == data.person_view.person.id) {
-                postRes.post_view.creator.banned = data.banned;
-              }
-              this.setState(this.state);
-            },
-            none: void 0,
-          }),
-        none: void 0,
-      });
+      let data = wsJsonToRes<BanPersonResponse>(msg);
+      this.state.commentsRes?.comments
+        .filter(c => c.creator.id == data.person_view.person.id)
+        .forEach(c => (c.creator.banned = data.banned));
+
+      let postRes = this.state.postRes;
+      if (postRes) {
+        if (postRes.post_view.creator.id == data.person_view.person.id) {
+          postRes.post_view.creator.banned = data.banned;
+        }
+      }
+      this.setState(this.state);
     } else if (op == UserOperation.AddAdmin) {
-      let data = wsJsonToRes<AddAdminResponse>(msg, AddAdminResponse);
+      let data = wsJsonToRes<AddAdminResponse>(msg);
       this.setState(s => ((s.siteRes.admins = data.admins), s));
     } else if (op == UserOperation.Search) {
-      let data = wsJsonToRes<SearchResponse>(msg, SearchResponse);
+      let data = wsJsonToRes<SearchResponse>(msg);
       let xPosts = data.posts.filter(
         p => p.post.id != Number(this.props.match.params.id)
       );
-      this.setState({ crossPosts: xPosts.length > 0 ? Some(xPosts) : None });
+      this.setState({ crossPosts: xPosts.length > 0 ? xPosts : undefined });
     } else if (op == UserOperation.LeaveAdmin) {
-      let data = wsJsonToRes<GetSiteResponse>(msg, GetSiteResponse);
+      let data = wsJsonToRes<GetSiteResponse>(msg);
       this.setState({ siteRes: data });
     } else if (op == UserOperation.TransferCommunity) {
-      let data = wsJsonToRes<GetCommunityResponse>(msg, GetCommunityResponse);
-      this.state.postRes.match({
-        some: res => {
-          res.community_view = data.community_view;
-          res.post_view.community = data.community_view.community;
-          res.moderators = data.moderators;
-          this.setState(this.state);
-        },
-        none: void 0,
-      });
+      let data = wsJsonToRes<GetCommunityResponse>(msg);
+      let postRes = this.state.postRes;
+      if (postRes) {
+        postRes.community_view = data.community_view;
+        postRes.post_view.community = data.community_view.community;
+        postRes.moderators = data.moderators;
+        this.setState(this.state);
+      }
     } else if (op == UserOperation.BlockPerson) {
-      let data = wsJsonToRes<BlockPersonResponse>(msg, BlockPersonResponse);
+      let data = wsJsonToRes<BlockPersonResponse>(msg);
       updatePersonBlock(data);
     } else if (op == UserOperation.CreatePostReport) {
-      let data = wsJsonToRes<PostReportResponse>(msg, PostReportResponse);
+      let data = wsJsonToRes<PostReportResponse>(msg);
       if (data) {
         toast(i18n.t("report_created"));
       }
     } else if (op == UserOperation.CreateCommentReport) {
-      let data = wsJsonToRes<CommentReportResponse>(msg, CommentReportResponse);
+      let data = wsJsonToRes<CommentReportResponse>(msg);
       if (data) {
         toast(i18n.t("report_created"));
       }
@@ -880,7 +787,7 @@ export class Post extends Component<any, PostState> {
       op == UserOperation.PurgeComment ||
       op == UserOperation.PurgeCommunity
     ) {
-      let data = wsJsonToRes<PurgeItemResponse>(msg, PurgeItemResponse);
+      let data = wsJsonToRes<PurgeItemResponse>(msg);
       if (data.success) {
         toast(i18n.t("purge_success"));
         this.context.router.history.push(`/`);
index ae7fb215cabfd44bab52936dc5d2f00a3145cce4..e3aba7a28becebda394a7852aaea02f1cddd89d7 100644 (file)
@@ -1,4 +1,3 @@
-import { None, Option, Some } from "@sniptt/monads";
 import { Component } from "inferno";
 import {
   GetPersonDetails,
@@ -14,9 +13,9 @@ import { i18n } from "../../i18next";
 import { InitialFetchRequest } from "../../interfaces";
 import { UserService, WebSocketService } from "../../services";
 import {
-  auth,
   getRecipientIdFromProps,
   isBrowser,
+  myAuth,
   setIsoData,
   toast,
   wsClient,
@@ -28,7 +27,7 @@ import { PrivateMessageForm } from "./private-message-form";
 
 interface CreatePrivateMessageState {
   siteRes: GetSiteResponse;
-  recipientDetailsRes: Option<GetPersonDetailsResponse>;
+  recipientDetailsRes?: GetPersonDetailsResponse;
   recipient_id: number;
   loading: boolean;
 }
@@ -37,25 +36,23 @@ export class CreatePrivateMessage extends Component<
   any,
   CreatePrivateMessageState
 > {
-  private isoData = setIsoData(this.context, GetPersonDetailsResponse);
-  private subscription: Subscription;
-  private emptyState: CreatePrivateMessageState = {
+  private isoData = setIsoData(this.context);
+  private subscription?: Subscription;
+  state: CreatePrivateMessageState = {
     siteRes: this.isoData.site_res,
-    recipientDetailsRes: None,
     recipient_id: getRecipientIdFromProps(this.props),
     loading: true,
   };
 
   constructor(props: any, context: any) {
     super(props, context);
-    this.state = this.emptyState;
     this.handlePrivateMessageCreate =
       this.handlePrivateMessageCreate.bind(this);
 
     this.parseMessage = this.parseMessage.bind(this);
     this.subscription = wsSubscribe(this.parseMessage);
 
-    if (UserService.Instance.myUserInfo.isNone() && isBrowser()) {
+    if (!UserService.Instance.myUserInfo && isBrowser()) {
       toast(i18n.t("not_logged_in"), "danger");
       this.context.router.history.push(`/login`);
     }
@@ -64,9 +61,8 @@ export class CreatePrivateMessage extends Component<
     if (this.isoData.path == this.context.router.route.match.url) {
       this.state = {
         ...this.state,
-        recipientDetailsRes: Some(
-          this.isoData.routeData[0] as GetPersonDetailsResponse
-        ),
+        recipientDetailsRes: this.isoData
+          .routeData[0] as GetPersonDetailsResponse,
         loading: false,
       };
     } else {
@@ -75,77 +71,61 @@ export class CreatePrivateMessage extends Component<
   }
 
   fetchPersonDetails() {
-    let form = new GetPersonDetails({
-      person_id: Some(this.state.recipient_id),
-      sort: Some(SortType.New),
-      saved_only: Some(false),
-      username: None,
-      page: None,
-      limit: None,
-      community_id: None,
-      auth: auth(false).ok(),
-    });
+    let form: GetPersonDetails = {
+      person_id: this.state.recipient_id,
+      sort: SortType.New,
+      saved_only: false,
+      auth: myAuth(false),
+    };
     WebSocketService.Instance.send(wsClient.getPersonDetails(form));
   }
 
   static fetchInitialData(req: InitialFetchRequest): Promise<any>[] {
-    let person_id = Some(Number(req.path.split("/").pop()));
-    let form = new GetPersonDetails({
+    let person_id = Number(req.path.split("/").pop());
+    let form: GetPersonDetails = {
       person_id,
-      sort: Some(SortType.New),
-      saved_only: Some(false),
-      username: None,
-      page: None,
-      limit: None,
-      community_id: None,
+      sort: SortType.New,
+      saved_only: false,
       auth: req.auth,
-    });
+    };
     return [req.client.getPersonDetails(form)];
   }
 
   get documentTitle(): string {
-    return this.state.recipientDetailsRes.match({
-      some: res =>
-        `${i18n.t("create_private_message")} - ${res.person_view.person.name}`,
-      none: "",
-    });
+    let name_ = this.state.recipientDetailsRes?.person_view.person.name;
+    return name_ ? `${i18n.t("create_private_message")} - ${name_}` : "";
   }
 
   componentWillUnmount() {
     if (isBrowser()) {
-      this.subscription.unsubscribe();
+      this.subscription?.unsubscribe();
     }
   }
 
   render() {
+    let res = this.state.recipientDetailsRes;
     return (
       <div className="container-lg">
         <HtmlTags
           title={this.documentTitle}
           path={this.context.router.route.match.url}
-          description={None}
-          image={None}
         />
         {this.state.loading ? (
           <h5>
             <Spinner large />
           </h5>
         ) : (
-          this.state.recipientDetailsRes.match({
-            some: res => (
-              <div className="row">
-                <div className="col-12 col-lg-6 offset-lg-3 mb-4">
-                  <h5>{i18n.t("create_private_message")}</h5>
-                  <PrivateMessageForm
-                    privateMessageView={None}
-                    onCreate={this.handlePrivateMessageCreate}
-                    recipient={res.person_view.person}
-                  />
-                </div>
+          res && (
+            <div className="row">
+              <div className="col-12 col-lg-6 offset-lg-3 mb-4">
+                <h5>{i18n.t("create_private_message")}</h5>
+                <PrivateMessageForm
+                  onCreate={this.handlePrivateMessageCreate}
+                  recipient={res.person_view.person}
+                />
               </div>
-            ),
-            none: <></>,
-          })
+            </div>
+          )
         )}
       </div>
     );
@@ -166,11 +146,8 @@ export class CreatePrivateMessage extends Component<
       this.setState({ loading: false });
       return;
     } else if (op == UserOperation.GetPersonDetails) {
-      let data = wsJsonToRes<GetPersonDetailsResponse>(
-        msg,
-        GetPersonDetailsResponse
-      );
-      this.setState({ recipientDetailsRes: Some(data), loading: false });
+      let data = wsJsonToRes<GetPersonDetailsResponse>(msg);
+      this.setState({ recipientDetailsRes: data, loading: false });
     }
   }
 }
index 86b58bea8abbb24b448e78a9b3bde410205bd791..31dc29273290437408da6851d035859170bd7329 100644 (file)
@@ -1,4 +1,3 @@
-import { None, Option, Some } from "@sniptt/monads";
 import { Component, linkEvent } from "inferno";
 import { T } from "inferno-i18next-dess";
 import { Prompt } from "inferno-router";
@@ -16,9 +15,9 @@ import { Subscription } from "rxjs";
 import { i18n } from "../../i18next";
 import { WebSocketService } from "../../services";
 import {
-  auth,
   capitalizeFirstLetter,
   isBrowser,
+  myAuth,
   relTags,
   setupTippy,
   toast,
@@ -31,14 +30,14 @@ import { PersonListing } from "../person/person-listing";
 
 interface PrivateMessageFormProps {
   recipient: PersonSafe;
-  privateMessageView: Option<PrivateMessageView>; // If a pm is given, that means this is an edit
+  privateMessageView?: PrivateMessageView; // If a pm is given, that means this is an edit
   onCancel?(): any;
   onCreate?(message: PrivateMessageView): any;
   onEdit?(message: PrivateMessageView): any;
 }
 
 interface PrivateMessageFormState {
-  privateMessageForm: CreatePrivateMessage;
+  content?: string;
   loading: boolean;
   previewMode: boolean;
   showDisclaimer: boolean;
@@ -48,13 +47,8 @@ export class PrivateMessageForm extends Component<
   PrivateMessageFormProps,
   PrivateMessageFormState
 > {
-  private subscription: Subscription;
-  private emptyState: PrivateMessageFormState = {
-    privateMessageForm: new CreatePrivateMessage({
-      content: null,
-      recipient_id: this.props.recipient.id,
-      auth: auth().unwrap(),
-    }),
+  private subscription?: Subscription;
+  state: PrivateMessageFormState = {
     loading: false,
     previewMode: false,
     showDisclaimer: false,
@@ -63,17 +57,15 @@ export class PrivateMessageForm extends Component<
   constructor(props: any, context: any) {
     super(props, context);
 
-    this.state = this.emptyState;
-
     this.handleContentChange = this.handleContentChange.bind(this);
 
     this.parseMessage = this.parseMessage.bind(this);
     this.subscription = wsSubscribe(this.parseMessage);
 
     // Its an edit
-    if (this.props.privateMessageView.isSome()) {
-      this.state.privateMessageForm.content =
-        this.props.privateMessageView.unwrap().private_message.content;
+    if (this.props.privateMessageView) {
+      this.state.content =
+        this.props.privateMessageView.private_message.content;
     }
   }
 
@@ -82,16 +74,16 @@ export class PrivateMessageForm extends Component<
   }
 
   componentDidUpdate() {
-    if (!this.state.loading && this.state.privateMessageForm.content) {
+    if (!this.state.loading && this.state.content) {
       window.onbeforeunload = () => true;
     } else {
-      window.onbeforeunload = undefined;
+      window.onbeforeunload = null;
     }
   }
 
   componentWillUnmount() {
     if (isBrowser()) {
-      this.subscription.unsubscribe();
+      this.subscription?.unsubscribe();
       window.onbeforeunload = null;
     }
   }
@@ -100,11 +92,11 @@ export class PrivateMessageForm extends Component<
     return (
       <div>
         <Prompt
-          when={!this.state.loading && this.state.privateMessageForm.content}
+          when={!this.state.loading && this.state.content}
           message={i18n.t("block_leaving")}
         />
         <form onSubmit={linkEvent(this, this.handlePrivateMessageSubmit)}>
-          {this.props.privateMessageView.isNone() && (
+          {!this.props.privateMessageView && (
             <div className="form-group row">
               <label className="col-sm-2 col-form-label">
                 {capitalizeFirstLetter(i18n.t("to"))}
@@ -129,11 +121,7 @@ export class PrivateMessageForm extends Component<
             </label>
             <div className="col-sm-10">
               <MarkdownTextArea
-                initialContent={Some(this.state.privateMessageForm.content)}
-                initialLanguageId={None}
-                placeholder={None}
-                buttonTitle={None}
-                maxLength={None}
+                initialContent={this.state.content}
                 onContentChange={this.handleContentChange}
                 allLanguages={[]}
                 siteLanguages={[]}
@@ -168,13 +156,13 @@ export class PrivateMessageForm extends Component<
               >
                 {this.state.loading ? (
                   <Spinner />
-                ) : this.props.privateMessageView.isSome() ? (
+                ) : this.props.privateMessageView ? (
                   capitalizeFirstLetter(i18n.t("save"))
                 ) : (
                   capitalizeFirstLetter(i18n.t("send_message"))
                 )}
               </button>
-              {this.props.privateMessageView.isSome() && (
+              {this.props.privateMessageView && (
                 <button
                   type="button"
                   className="btn btn-secondary"
@@ -195,28 +183,35 @@ export class PrivateMessageForm extends Component<
 
   handlePrivateMessageSubmit(i: PrivateMessageForm, event: any) {
     event.preventDefault();
-    i.props.privateMessageView.match({
-      some: pm => {
-        let form = new EditPrivateMessage({
+    let pm = i.props.privateMessageView;
+    let auth = myAuth();
+    let content = i.state.content;
+    if (auth && content) {
+      if (pm) {
+        let form: EditPrivateMessage = {
           private_message_id: pm.private_message.id,
-          content: i.state.privateMessageForm.content,
-          auth: auth().unwrap(),
-        });
+          content,
+          auth,
+        };
         WebSocketService.Instance.send(wsClient.editPrivateMessage(form));
-      },
-      none: WebSocketService.Instance.send(
-        wsClient.createPrivateMessage(i.state.privateMessageForm)
-      ),
-    });
-    i.setState({ loading: true });
+      } else {
+        let form: CreatePrivateMessage = {
+          content,
+          recipient_id: i.props.recipient.id,
+          auth,
+        };
+        WebSocketService.Instance.send(wsClient.createPrivateMessage(form));
+      }
+      i.setState({ loading: true });
+    }
   }
 
   handleContentChange(val: string) {
-    this.setState(s => ((s.privateMessageForm.content = val), s));
+    this.setState({ content: val });
   }
 
   handleCancel(i: PrivateMessageForm) {
-    i.props.onCancel();
+    i.props.onCancel?.();
   }
 
   handlePreviewToggle(i: PrivateMessageForm, event: any) {
@@ -240,18 +235,12 @@ export class PrivateMessageForm extends Component<
       op == UserOperation.DeletePrivateMessage ||
       op == UserOperation.MarkPrivateMessageAsRead
     ) {
-      let data = wsJsonToRes<PrivateMessageResponse>(
-        msg,
-        PrivateMessageResponse
-      );
+      let data = wsJsonToRes<PrivateMessageResponse>(msg);
       this.setState({ loading: false });
-      this.props.onEdit(data.private_message_view);
+      this.props.onEdit?.(data.private_message_view);
     } else if (op == UserOperation.CreatePrivateMessage) {
-      let data = wsJsonToRes<PrivateMessageResponse>(
-        msg,
-        PrivateMessageResponse
-      );
-      this.props.onCreate(data.private_message_view);
+      let data = wsJsonToRes<PrivateMessageResponse>(msg);
+      this.props.onCreate?.(data.private_message_view);
     }
   }
 }
index 3d6198c6886815c56b0c2d2515e34147755b5f83..68ca0ed6d6ceeee76134afb69711080677251cf1 100644 (file)
@@ -6,7 +6,7 @@ import {
 } from "lemmy-js-client";
 import { i18n } from "../../i18next";
 import { WebSocketService } from "../../services";
-import { auth, mdToHtml, wsClient } from "../../utils";
+import { mdToHtml, myAuth, wsClient } from "../../utils";
 import { Icon } from "../common/icon";
 import { PersonListing } from "../person/person-listing";
 
@@ -45,24 +45,21 @@ export class PrivateMessageReport extends Component<Props, any> {
         <div>
           {i18n.t("reason")}: {pmr.reason}
         </div>
-        {r.resolver.match({
-          some: resolver => (
-            <div>
-              {pmr.resolved ? (
-                <T i18nKey="resolved_by">
-                  #
-                  <PersonListing person={resolver} />
-                </T>
-              ) : (
-                <T i18nKey="unresolved_by">
-                  #
-                  <PersonListing person={resolver} />
-                </T>
-              )}
-            </div>
-          ),
-          none: <></>,
-        })}
+        {r.resolver && (
+          <div>
+            {pmr.resolved ? (
+              <T i18nKey="resolved_by">
+                #
+                <PersonListing person={r.resolver} />
+              </T>
+            ) : (
+              <T i18nKey="unresolved_by">
+                #
+                <PersonListing person={r.resolver} />
+              </T>
+            )}
+          </div>
+        )}
         <button
           className="btn btn-link btn-animate text-muted py-0"
           onClick={linkEvent(this, this.handleResolveReport)}
@@ -82,11 +79,16 @@ export class PrivateMessageReport extends Component<Props, any> {
 
   handleResolveReport(i: PrivateMessageReport) {
     let pmr = i.props.report.private_message_report;
-    let form = new ResolvePrivateMessageReport({
-      report_id: pmr.id,
-      resolved: !pmr.resolved,
-      auth: auth().unwrap(),
-    });
-    WebSocketService.Instance.send(wsClient.resolvePrivateMessageReport(form));
+    let auth = myAuth();
+    if (auth) {
+      let form: ResolvePrivateMessageReport = {
+        report_id: pmr.id,
+        resolved: !pmr.resolved,
+        auth,
+      };
+      WebSocketService.Instance.send(
+        wsClient.resolvePrivateMessageReport(form)
+      );
+    }
   }
 }
index b7d1bd1f7a164e40fc70d9eb0b2711004185c224..f9b30a117f7cfd6418c9fd4558494f9e9337957f 100644 (file)
@@ -1,4 +1,3 @@
-import { None, Option, Some } from "@sniptt/monads/build";
 import { Component, linkEvent } from "inferno";
 import {
   CreatePrivateMessageReport,
@@ -6,11 +5,10 @@ import {
   MarkPrivateMessageAsRead,
   PersonSafe,
   PrivateMessageView,
-  toUndefined,
 } from "lemmy-js-client";
 import { i18n } from "../../i18next";
 import { UserService, WebSocketService } from "../../services";
-import { auth, mdToHtml, toast, wsClient } from "../../utils";
+import { mdToHtml, myAuth, toast, wsClient } from "../../utils";
 import { Icon } from "../common/icon";
 import { MomentTime } from "../common/moment-time";
 import { PersonListing } from "../person/person-listing";
@@ -22,7 +20,7 @@ interface PrivateMessageState {
   collapsed: boolean;
   viewSource: boolean;
   showReportDialog: boolean;
-  reportReason: Option<string>;
+  reportReason?: string;
 }
 
 interface PrivateMessageProps {
@@ -33,19 +31,17 @@ export class PrivateMessage extends Component<
   PrivateMessageProps,
   PrivateMessageState
 > {
-  private emptyState: PrivateMessageState = {
+  state: PrivateMessageState = {
     showReply: false,
     showEdit: false,
     collapsed: false,
     viewSource: false,
     showReportDialog: false,
-    reportReason: None,
   };
 
   constructor(props: any, context: any) {
     super(props, context);
 
-    this.state = this.emptyState;
     this.handleReplyCancel = this.handleReplyCancel.bind(this);
     this.handlePrivateMessageCreate =
       this.handlePrivateMessageCreate.bind(this);
@@ -53,13 +49,10 @@ export class PrivateMessage extends Component<
   }
 
   get mine(): boolean {
-    return UserService.Instance.myUserInfo
-      .map(
-        m =>
-          m.local_user_view.person.id ==
-          this.props.private_message_view.creator.id
-      )
-      .unwrapOr(false);
+    return (
+      UserService.Instance.myUserInfo?.local_user_view.person.id ==
+      this.props.private_message_view.creator.id
+    );
   }
 
   render() {
@@ -104,7 +97,7 @@ export class PrivateMessage extends Component<
           {this.state.showEdit && (
             <PrivateMessageForm
               recipient={otherPerson}
-              privateMessageView={Some(message_view)}
+              privateMessageView={message_view}
               onEdit={this.handlePrivateMessageEdit}
               onCreate={this.handlePrivateMessageCreate}
               onCancel={this.handleReplyCancel}
@@ -230,7 +223,7 @@ export class PrivateMessage extends Component<
               className="form-control mr-2"
               placeholder={i18n.t("reason")}
               required
-              value={toUndefined(this.state.reportReason)}
+              value={this.state.reportReason}
               onInput={linkEvent(this, this.handleReportReasonChange)}
             />
             <button
@@ -245,7 +238,6 @@ export class PrivateMessage extends Component<
         {this.state.showReply && (
           <PrivateMessageForm
             recipient={otherPerson}
-            privateMessageView={None}
             onCreate={this.handlePrivateMessageCreate}
           />
         )}
@@ -283,12 +275,15 @@ export class PrivateMessage extends Component<
   }
 
   handleDeleteClick(i: PrivateMessage) {
-    let form = new DeletePrivateMessage({
-      private_message_id: i.props.private_message_view.private_message.id,
-      deleted: !i.props.private_message_view.private_message.deleted,
-      auth: auth().unwrap(),
-    });
-    WebSocketService.Instance.send(wsClient.deletePrivateMessage(form));
+    let auth = myAuth();
+    if (auth) {
+      let form: DeletePrivateMessage = {
+        private_message_id: i.props.private_message_view.private_message.id,
+        deleted: !i.props.private_message_view.private_message.deleted,
+        auth,
+      };
+      WebSocketService.Instance.send(wsClient.deletePrivateMessage(form));
+    }
   }
 
   handleReplyCancel() {
@@ -296,12 +291,15 @@ export class PrivateMessage extends Component<
   }
 
   handleMarkRead(i: PrivateMessage) {
-    let form = new MarkPrivateMessageAsRead({
-      private_message_id: i.props.private_message_view.private_message.id,
-      read: !i.props.private_message_view.private_message.read,
-      auth: auth().unwrap(),
-    });
-    WebSocketService.Instance.send(wsClient.markPrivateMessageAsRead(form));
+    let auth = myAuth();
+    if (auth) {
+      let form: MarkPrivateMessageAsRead = {
+        private_message_id: i.props.private_message_view.private_message.id,
+        read: !i.props.private_message_view.private_message.read,
+        auth,
+      };
+      WebSocketService.Instance.send(wsClient.markPrivateMessageAsRead(form));
+    }
   }
 
   handleMessageCollapse(i: PrivateMessage) {
@@ -317,19 +315,23 @@ export class PrivateMessage extends Component<
   }
 
   handleReportReasonChange(i: PrivateMessage, event: any) {
-    i.setState({ reportReason: Some(event.target.value) });
+    i.setState({ reportReason: event.target.value });
   }
 
   handleReportSubmit(i: PrivateMessage, event: any) {
     event.preventDefault();
-    let form = new CreatePrivateMessageReport({
-      private_message_id: i.props.private_message_view.private_message.id,
-      reason: toUndefined(i.state.reportReason),
-      auth: auth().unwrap(),
-    });
-    WebSocketService.Instance.send(wsClient.createPrivateMessageReport(form));
+    let auth = myAuth();
+    let reason = i.state.reportReason;
+    if (auth && reason) {
+      let form: CreatePrivateMessageReport = {
+        private_message_id: i.props.private_message_view.private_message.id,
+        reason,
+        auth,
+      };
+      WebSocketService.Instance.send(wsClient.createPrivateMessageReport(form));
 
-    i.setState({ showReportDialog: false });
+      i.setState({ showReportDialog: false });
+    }
   }
 
   handlePrivateMessageEdit() {
@@ -337,14 +339,12 @@ export class PrivateMessage extends Component<
   }
 
   handlePrivateMessageCreate(message: PrivateMessageView) {
-    UserService.Instance.myUserInfo.match({
-      some: mui => {
-        if (message.creator.id == mui.local_user_view.person.id) {
-          this.setState({ showReply: false });
-          toast(i18n.t("message_sent"));
-        }
-      },
-      none: void 0,
-    });
+    if (
+      message.creator.id ==
+      UserService.Instance.myUserInfo?.local_user_view.person.id
+    ) {
+      this.setState({ showReply: false });
+      toast(i18n.t("message_sent"));
+    }
   }
 }
index b9aacf3afc0602215f995eb350b1adbc6de30c41..102e0fcae1b8ecd7734ae49bba877eb6960737d8 100644 (file)
@@ -1,4 +1,3 @@
-import { None, Option, Some } from "@sniptt/monads";
 import { Component, linkEvent } from "inferno";
 import {
   CommentResponse,
@@ -30,7 +29,6 @@ import { i18n } from "../i18next";
 import { CommentViewType, InitialFetchRequest } from "../interfaces";
 import { WebSocketService } from "../services";
 import {
-  auth,
   capitalizeFirstLetter,
   choicesConfig,
   commentsToFlatNodes,
@@ -45,6 +43,7 @@ import {
   fetchLimit,
   fetchUsers,
   isBrowser,
+  myAuth,
   numToSI,
   personSelectName,
   personToChoice,
@@ -93,13 +92,13 @@ interface SearchState {
   communityId: number;
   creatorId: number;
   page: number;
-  searchResponse: Option<SearchResponse>;
+  searchResponse?: SearchResponse;
   communities: CommunityView[];
-  creatorDetails: Option<GetPersonDetailsResponse>;
+  creatorDetails?: GetPersonDetailsResponse;
   loading: boolean;
   siteRes: GetSiteResponse;
   searchText: string;
-  resolveObjectResponse: Option<ResolveObjectResponse>;
+  resolveObjectResponse?: ResolveObjectResponse;
 }
 
 interface UrlParams {
@@ -119,18 +118,11 @@ interface Combined {
 }
 
 export class Search extends Component<any, SearchState> {
-  private isoData = setIsoData(
-    this.context,
-    GetCommunityResponse,
-    ListCommunitiesResponse,
-    GetPersonDetailsResponse,
-    SearchResponse,
-    ResolveObjectResponse
-  );
+  private isoData = setIsoData(this.context);
   private communityChoices: any;
   private creatorChoices: any;
-  private subscription: Subscription;
-  private emptyState: SearchState = {
+  private subscription?: Subscription;
+  state: SearchState = {
     q: Search.getSearchQueryFromProps(this.props.match.params.q),
     type_: Search.getSearchTypeFromProps(this.props.match.params.type),
     sort: Search.getSortTypeFromProps(this.props.match.params.sort),
@@ -143,9 +135,6 @@ export class Search extends Component<any, SearchState> {
       this.props.match.params.community_id
     ),
     creatorId: Search.getCreatorIdFromProps(this.props.match.params.creator_id),
-    searchResponse: None,
-    resolveObjectResponse: None,
-    creatorDetails: None,
     loading: true,
     siteRes: this.isoData.site_res,
     communities: [],
@@ -182,7 +171,6 @@ export class Search extends Component<any, SearchState> {
   constructor(props: any, context: any) {
     super(props, context);
 
-    this.state = this.emptyState;
     this.handleSortChange = this.handleSortChange.bind(this);
     this.handleListingTypeChange = this.handleListingTypeChange.bind(this);
     this.handlePageChange = this.handlePageChange.bind(this);
@@ -192,42 +180,38 @@ export class Search extends Component<any, SearchState> {
 
     // Only fetch the data if coming from another route
     if (this.isoData.path == this.context.router.route.match.url) {
-      let communityRes = Some(
-        this.isoData.routeData[0] as GetCommunityResponse
-      );
-      let communitiesRes = Some(
-        this.isoData.routeData[1] as ListCommunitiesResponse
-      );
-
+      let communityRes = this.isoData.routeData[0] as
+        | GetCommunityResponse
+        | undefined;
+      let communitiesRes = this.isoData.routeData[1] as
+        | ListCommunitiesResponse
+        | undefined;
       // This can be single or multiple communities given
-      if (communitiesRes.isSome()) {
+      if (communitiesRes) {
         this.state = {
           ...this.state,
-          communities: communitiesRes.unwrap().communities,
+          communities: communitiesRes.communities,
         };
       }
 
-      if (communityRes.isSome()) {
+      if (communityRes) {
         this.state = {
           ...this.state,
-          communities: [communityRes.unwrap().community_view],
+          communities: [communityRes.community_view],
         };
       }
 
       this.state = {
         ...this.state,
-        creatorDetails: Some(
-          this.isoData.routeData[2] as GetPersonDetailsResponse
-        ),
+        creatorDetails: this.isoData.routeData[2] as GetPersonDetailsResponse,
       };
 
       if (this.state.q != "") {
         this.state = {
           ...this.state,
-          searchResponse: Some(this.isoData.routeData[3] as SearchResponse),
-          resolveObjectResponse: Some(
-            this.isoData.routeData[4] as ResolveObjectResponse
-          ),
+          searchResponse: this.isoData.routeData[3] as SearchResponse,
+          resolveObjectResponse: this.isoData
+            .routeData[4] as ResolveObjectResponse,
           loading: false,
         };
       } else {
@@ -240,7 +224,7 @@ export class Search extends Component<any, SearchState> {
   }
 
   componentWillUnmount() {
-    this.subscription.unsubscribe();
+    this.subscription?.unsubscribe();
     saveScrollPosition(this.context);
   }
 
@@ -266,13 +250,12 @@ export class Search extends Component<any, SearchState> {
   }
 
   fetchCommunities() {
-    let listCommunitiesForm = new ListCommunities({
-      type_: Some(ListingType.All),
-      sort: Some(SortType.TopAll),
-      limit: Some(fetchLimit),
-      page: None,
-      auth: auth(false).ok(),
-    });
+    let listCommunitiesForm: ListCommunities = {
+      type_: ListingType.All,
+      sort: SortType.TopAll,
+      limit: fetchLimit,
+      auth: myAuth(false),
+    };
     WebSocketService.Instance.send(
       wsClient.listCommunities(listCommunitiesForm)
     );
@@ -281,71 +264,56 @@ export class Search extends Component<any, SearchState> {
   static fetchInitialData(req: InitialFetchRequest): Promise<any>[] {
     let pathSplit = req.path.split("/");
     let promises: Promise<any>[] = [];
+    let auth = req.auth;
 
     let communityId = this.getCommunityIdFromProps(pathSplit[11]);
-    let community_id: Option<number> =
-      communityId == 0 ? None : Some(communityId);
-    community_id.match({
-      some: id => {
-        let getCommunityForm = new GetCommunity({
-          id: Some(id),
-          name: None,
-          auth: req.auth,
-        });
-        promises.push(req.client.getCommunity(getCommunityForm));
-        promises.push(Promise.resolve());
-      },
-      none: () => {
-        let listCommunitiesForm = new ListCommunities({
-          type_: Some(ListingType.All),
-          sort: Some(SortType.TopAll),
-          limit: Some(fetchLimit),
-          page: None,
-          auth: req.auth,
-        });
-        promises.push(Promise.resolve());
-        promises.push(req.client.listCommunities(listCommunitiesForm));
-      },
-    });
+    let community_id = communityId == 0 ? undefined : communityId;
+    if (community_id) {
+      let getCommunityForm: GetCommunity = {
+        id: community_id,
+        auth,
+      };
+      promises.push(req.client.getCommunity(getCommunityForm));
+      promises.push(Promise.resolve());
+    } else {
+      let listCommunitiesForm: ListCommunities = {
+        type_: ListingType.All,
+        sort: SortType.TopAll,
+        limit: fetchLimit,
+        auth: req.auth,
+      };
+      promises.push(Promise.resolve());
+      promises.push(req.client.listCommunities(listCommunitiesForm));
+    }
 
     let creatorId = this.getCreatorIdFromProps(pathSplit[13]);
-    let creator_id: Option<number> = creatorId == 0 ? None : Some(creatorId);
-    creator_id.match({
-      some: id => {
-        let getCreatorForm = new GetPersonDetails({
-          person_id: Some(id),
-          username: None,
-          sort: None,
-          page: None,
-          limit: None,
-          community_id: None,
-          saved_only: None,
-          auth: req.auth,
-        });
-        promises.push(req.client.getPersonDetails(getCreatorForm));
-      },
-      none: () => {
-        promises.push(Promise.resolve());
-      },
-    });
+    let creator_id = creatorId == 0 ? undefined : creatorId;
+    if (creator_id) {
+      let getCreatorForm: GetPersonDetails = {
+        person_id: creator_id,
+        auth: req.auth,
+      };
+      promises.push(req.client.getPersonDetails(getCreatorForm));
+    } else {
+      promises.push(Promise.resolve());
+    }
 
-    let form = new SearchForm({
+    let form: SearchForm = {
       q: this.getSearchQueryFromProps(pathSplit[3]),
       community_id,
-      community_name: None,
       creator_id,
-      type_: Some(this.getSearchTypeFromProps(pathSplit[5])),
-      sort: Some(this.getSortTypeFromProps(pathSplit[7])),
-      listing_type: Some(this.getListingTypeFromProps(pathSplit[9])),
-      page: Some(this.getPageFromProps(pathSplit[15])),
-      limit: Some(fetchLimit),
+      type_: this.getSearchTypeFromProps(pathSplit[5]),
+      sort: this.getSortTypeFromProps(pathSplit[7]),
+      listing_type: this.getListingTypeFromProps(pathSplit[9]),
+      page: this.getPageFromProps(pathSplit[15]),
+      limit: fetchLimit,
       auth: req.auth,
-    });
+    };
 
-    let resolveObjectForm = new ResolveObject({
+    let resolveObjectForm: ResolveObject = {
       q: this.getSearchQueryFromProps(pathSplit[3]),
       auth: req.auth,
-    });
+    };
 
     if (form.q != "") {
       promises.push(req.client.search(form));
@@ -371,8 +339,6 @@ export class Search extends Component<any, SearchState> {
       this.setState({
         loading: true,
         searchText: this.state.q,
-        searchResponse: None,
-        resolveObjectResponse: None,
       });
       this.search();
     }
@@ -391,8 +357,6 @@ export class Search extends Component<any, SearchState> {
         <HtmlTags
           title={this.documentTitle}
           path={this.context.router.route.match.url}
-          description={None}
-          image={None}
         />
         <h5>{i18n.t("search")}</h5>
         {this.selects()}
@@ -512,53 +476,47 @@ export class Search extends Component<any, SearchState> {
   buildCombined(): Combined[] {
     let combined: Combined[] = [];
 
+    let resolveRes = this.state.resolveObjectResponse;
     // Push the possible resolve / federated objects first
-    this.state.resolveObjectResponse.match({
-      some: res => {
-        let resolveComment = res.comment;
-        if (resolveComment.isSome()) {
-          combined.push(this.commentViewToCombined(resolveComment.unwrap()));
-        }
-        let resolvePost = res.post;
-        if (resolvePost.isSome()) {
-          combined.push(this.postViewToCombined(resolvePost.unwrap()));
-        }
-        let resolveCommunity = res.community;
-        if (resolveCommunity.isSome()) {
-          combined.push(
-            this.communityViewToCombined(resolveCommunity.unwrap())
-          );
-        }
-        let resolveUser = res.person;
-        if (resolveUser.isSome()) {
-          combined.push(this.personViewSafeToCombined(resolveUser.unwrap()));
-        }
-      },
-      none: void 0,
-    });
+    if (resolveRes) {
+      let resolveComment = resolveRes.comment;
+      if (resolveComment) {
+        combined.push(this.commentViewToCombined(resolveComment));
+      }
+      let resolvePost = resolveRes.post;
+      if (resolvePost) {
+        combined.push(this.postViewToCombined(resolvePost));
+      }
+      let resolveCommunity = resolveRes.community;
+      if (resolveCommunity) {
+        combined.push(this.communityViewToCombined(resolveCommunity));
+      }
+      let resolveUser = resolveRes.person;
+      if (resolveUser) {
+        combined.push(this.personViewSafeToCombined(resolveUser));
+      }
+    }
 
     // Push the search results
-    this.state.searchResponse.match({
-      some: res => {
-        pushNotNull(
-          combined,
-          res.comments?.map(e => this.commentViewToCombined(e))
-        );
-        pushNotNull(
-          combined,
-          res.posts?.map(e => this.postViewToCombined(e))
-        );
-        pushNotNull(
-          combined,
-          res.communities?.map(e => this.communityViewToCombined(e))
-        );
-        pushNotNull(
-          combined,
-          res.users?.map(e => this.personViewSafeToCombined(e))
-        );
-      },
-      none: void 0,
-    });
+    let searchRes = this.state.searchResponse;
+    if (searchRes) {
+      pushNotNull(
+        combined,
+        searchRes.comments?.map(e => this.commentViewToCombined(e))
+      );
+      pushNotNull(
+        combined,
+        searchRes.posts?.map(e => this.postViewToCombined(e))
+      );
+      pushNotNull(
+        combined,
+        searchRes.communities?.map(e => this.communityViewToCombined(e))
+      );
+      pushNotNull(
+        combined,
+        searchRes.users?.map(e => this.personViewSafeToCombined(e))
+      );
+    }
 
     // Sort it
     if (this.state.sort == SortType.New) {
@@ -588,9 +546,6 @@ export class Search extends Component<any, SearchState> {
                 <PostListing
                   key={(i.data as PostView).post.id}
                   post_view={i.data as PostView}
-                  duplicates={None}
-                  moderators={None}
-                  admins={None}
                   showCommunity
                   enableDownvotes={enableDownvotes(this.state.siteRes)}
                   enableNsfw={enableNsfw(this.state.siteRes)}
@@ -611,9 +566,6 @@ export class Search extends Component<any, SearchState> {
                   ]}
                   viewType={CommentViewType.Flat}
                   viewOnly
-                  moderators={None}
-                  admins={None}
-                  maxCommentsShown={None}
                   locked
                   noIndent
                   enableDownvotes={enableDownvotes(this.state.siteRes)}
@@ -636,15 +588,8 @@ export class Search extends Component<any, SearchState> {
 
   comments() {
     let comments: CommentView[] = [];
-
-    this.state.resolveObjectResponse.match({
-      some: res => pushNotNull(comments, res.comment),
-      none: void 0,
-    });
-    this.state.searchResponse.match({
-      some: res => pushNotNull(comments, res.comments),
-      none: void 0,
-    });
+    pushNotNull(comments, this.state.resolveObjectResponse?.comment);
+    pushNotNull(comments, this.state.searchResponse?.comments);
 
     return (
       <CommentNodes
@@ -653,9 +598,6 @@ export class Search extends Component<any, SearchState> {
         viewOnly
         locked
         noIndent
-        moderators={None}
-        admins={None}
-        maxCommentsShown={None}
         enableDownvotes={enableDownvotes(this.state.siteRes)}
         allLanguages={this.state.siteRes.all_languages}
         siteLanguages={this.state.siteRes.discussion_languages}
@@ -666,14 +608,8 @@ export class Search extends Component<any, SearchState> {
   posts() {
     let posts: PostView[] = [];
 
-    this.state.resolveObjectResponse.match({
-      some: res => pushNotNull(posts, res.post),
-      none: void 0,
-    });
-    this.state.searchResponse.match({
-      some: res => pushNotNull(posts, res.posts),
-      none: void 0,
-    });
+    pushNotNull(posts, this.state.resolveObjectResponse?.post);
+    pushNotNull(posts, this.state.searchResponse?.posts);
 
     return (
       <>
@@ -683,9 +619,6 @@ export class Search extends Component<any, SearchState> {
               <PostListing
                 post_view={pv}
                 showCommunity
-                duplicates={None}
-                moderators={None}
-                admins={None}
                 enableDownvotes={enableDownvotes(this.state.siteRes)}
                 enableNsfw={enableNsfw(this.state.siteRes)}
                 allLanguages={this.state.siteRes.all_languages}
@@ -702,14 +635,8 @@ export class Search extends Component<any, SearchState> {
   communities() {
     let communities: CommunityView[] = [];
 
-    this.state.resolveObjectResponse.match({
-      some: res => pushNotNull(communities, res.community),
-      none: void 0,
-    });
-    this.state.searchResponse.match({
-      some: res => pushNotNull(communities, res.communities),
-      none: void 0,
-    });
+    pushNotNull(communities, this.state.resolveObjectResponse?.community);
+    pushNotNull(communities, this.state.searchResponse?.communities);
 
     return (
       <>
@@ -725,14 +652,8 @@ export class Search extends Component<any, SearchState> {
   users() {
     let users: PersonViewSafe[] = [];
 
-    this.state.resolveObjectResponse.match({
-      some: res => pushNotNull(users, res.person),
-      none: void 0,
-    });
-    this.state.searchResponse.match({
-      some: res => pushNotNull(users, res.users),
-      none: void 0,
-    });
+    pushNotNull(users, this.state.resolveObjectResponse?.person);
+    pushNotNull(users, this.state.searchResponse?.users);
 
     return (
       <>
@@ -800,6 +721,7 @@ export class Search extends Component<any, SearchState> {
   }
 
   creatorFilter() {
+    let creatorPv = this.state.creatorDetails?.person_view;
     return (
       <div className="form-group col-sm-6">
         <label className="col-form-label" htmlFor="creator-filter">
@@ -812,14 +734,11 @@ export class Search extends Component<any, SearchState> {
             value={this.state.creatorId}
           >
             <option value="0">{i18n.t("all")}</option>
-            {this.state.creatorDetails.match({
-              some: creator => (
-                <option value={creator.person_view.person.id}>
-                  {personSelectName(creator.person_view)}
-                </option>
-              ),
-              none: <></>,
-            })}
+            {creatorPv && (
+              <option value={creatorPv.person.id}>
+                {personSelectName(creatorPv)}
+              </option>
+            )}
           </select>
         </div>
       </div>
@@ -827,19 +746,24 @@ export class Search extends Component<any, SearchState> {
   }
 
   resultsCount(): number {
-    let searchCount = this.state.searchResponse
-      .map(
-        r =>
-          r.posts?.length +
-          r.comments?.length +
-          r.communities?.length +
-          r.users?.length
-      )
-      .unwrapOr(0);
-
-    let resObjCount = this.state.resolveObjectResponse
-      .map(r => (r.post || r.person || r.community || r.comment ? 1 : 0))
-      .unwrapOr(0);
+    let r = this.state.searchResponse;
+
+    let searchCount = r
+      ? r.posts?.length +
+        r.comments?.length +
+        r.communities?.length +
+        r.users?.length
+      : 0;
+
+    let resolveRes = this.state.resolveObjectResponse;
+    let resObjCount = resolveRes
+      ? resolveRes.post ||
+        resolveRes.person ||
+        resolveRes.community ||
+        resolveRes.comment
+        ? 1
+        : 0
+      : 0;
 
     return resObjCount + searchCount;
   }
@@ -849,33 +773,33 @@ export class Search extends Component<any, SearchState> {
   }
 
   search() {
-    let community_id: Option<number> =
-      this.state.communityId == 0 ? None : Some(this.state.communityId);
-    let creator_id: Option<number> =
-      this.state.creatorId == 0 ? None : Some(this.state.creatorId);
+    let community_id =
+      this.state.communityId == 0 ? undefined : this.state.communityId;
+    let creator_id =
+      this.state.creatorId == 0 ? undefined : this.state.creatorId;
 
-    let form = new SearchForm({
+    let auth = myAuth(false);
+    let form: SearchForm = {
       q: this.state.q,
       community_id,
-      community_name: None,
       creator_id,
-      type_: Some(this.state.type_),
-      sort: Some(this.state.sort),
-      listing_type: Some(this.state.listingType),
-      page: Some(this.state.page),
-      limit: Some(fetchLimit),
-      auth: auth(false).ok(),
-    });
+      type_: this.state.type_,
+      sort: this.state.sort,
+      listing_type: this.state.listingType,
+      page: this.state.page,
+      limit: fetchLimit,
+      auth,
+    };
 
-    let resolveObjectForm = new ResolveObject({
+    let resolveObjectForm: ResolveObject = {
       q: this.state.q,
-      auth: auth(false).ok(),
-    });
+      auth,
+    };
 
     if (this.state.q != "") {
       this.setState({
-        searchResponse: None,
-        resolveObjectResponse: None,
+        searchResponse: undefined,
+        resolveObjectResponse: undefined,
         loading: true,
       });
       WebSocketService.Instance.send(wsClient.search(form));
@@ -1019,12 +943,7 @@ export class Search extends Component<any, SearchState> {
     if (msg.error) {
       if (msg.error == "couldnt_find_object") {
         this.setState({
-          resolveObjectResponse: Some({
-            comment: None,
-            post: None,
-            community: None,
-            person: None,
-          }),
+          resolveObjectResponse: {},
         });
         this.checkFinishedLoading();
       } else {
@@ -1032,44 +951,35 @@ export class Search extends Component<any, SearchState> {
         return;
       }
     } else if (op == UserOperation.Search) {
-      let data = wsJsonToRes<SearchResponse>(msg, SearchResponse);
-      this.setState({ searchResponse: Some(data) });
+      let data = wsJsonToRes<SearchResponse>(msg);
+      this.setState({ searchResponse: data });
       window.scrollTo(0, 0);
       this.checkFinishedLoading();
       restoreScrollPosition(this.context);
     } else if (op == UserOperation.CreateCommentLike) {
-      let data = wsJsonToRes<CommentResponse>(msg, CommentResponse);
+      let data = wsJsonToRes<CommentResponse>(msg);
       createCommentLikeRes(
         data.comment_view,
-        this.state.searchResponse.map(r => r.comments).unwrapOr([])
+        this.state.searchResponse?.comments
       );
       this.setState(this.state);
     } else if (op == UserOperation.CreatePostLike) {
-      let data = wsJsonToRes<PostResponse>(msg, PostResponse);
-      createPostLikeFindRes(
-        data.post_view,
-        this.state.searchResponse.map(r => r.posts).unwrapOr([])
-      );
+      let data = wsJsonToRes<PostResponse>(msg);
+      createPostLikeFindRes(data.post_view, this.state.searchResponse?.posts);
       this.setState(this.state);
     } else if (op == UserOperation.ListCommunities) {
-      let data = wsJsonToRes<ListCommunitiesResponse>(
-        msg,
-        ListCommunitiesResponse
-      );
+      let data = wsJsonToRes<ListCommunitiesResponse>(msg);
       this.setState({ communities: data.communities });
       this.setupCommunityFilter();
     } else if (op == UserOperation.ResolveObject) {
-      let data = wsJsonToRes<ResolveObjectResponse>(msg, ResolveObjectResponse);
-      this.setState({ resolveObjectResponse: Some(data) });
+      let data = wsJsonToRes<ResolveObjectResponse>(msg);
+      this.setState({ resolveObjectResponse: data });
       this.checkFinishedLoading();
     }
   }
 
   checkFinishedLoading() {
-    if (
-      this.state.searchResponse.isSome() &&
-      this.state.resolveObjectResponse.isSome()
-    ) {
+    if (this.state.searchResponse && this.state.resolveObjectResponse) {
       this.setState({ loading: false });
     }
   }
index c25f5b16d155f09de702cdcb94707b2b442a3d2d..2d24bd53d17cb26dba32066cc8ee30a411d56fed 100644 (file)
@@ -1,4 +1,3 @@
-import { Either, Option } from "@sniptt/monads";
 import { GetSiteResponse, LemmyHttp } from "lemmy-js-client";
 
 /**
@@ -22,16 +21,16 @@ declare global {
 }
 
 export interface InitialFetchRequest {
-  auth: Option<string>;
+  auth?: string;
   client: LemmyHttp;
   path: string;
 }
 
 export interface PostFormParams {
-  name: Option<string>;
-  url: Option<string>;
-  body: Option<string>;
-  nameOrId: Option<Either<string, number>>;
+  name?: string;
+  url?: string;
+  body?: string;
+  nameOrId?: string | number;
 }
 
 export enum CommentViewType {
index 336503f220e4fc450340160e8bdc2a4d87970d84..b4404a7983a32b56a749649b39af6f882d2ef7a3 100644 (file)
@@ -1,3 +1,4 @@
+import { Inferno } from "inferno";
 import { IRouteProps } from "inferno-router/dist/Route";
 import { Communities } from "./components/community/communities";
 import { Community } from "./components/community/community";
@@ -24,6 +25,8 @@ import { Search } from "./components/search";
 import { InitialFetchRequest } from "./interfaces";
 
 interface IRoutePropsWithFetch extends IRouteProps {
+  // TODO Make sure this one is good.
+  component: Inferno.ComponentClass;
   fetchInitialData?(req: InitialFetchRequest): Promise<any>[];
 }
 
index 02b74212a163ed1ad1c75b12bf322c513bd50174..34a08b3ff52e85d46c235bdd93aa64c6defcd212 100644 (file)
@@ -1,5 +1,4 @@
 // import Cookies from 'js-cookie';
-import { Err, None, Ok, Option, Result, Some } from "@sniptt/monads";
 import IsomorphicCookie from "isomorphic-cookie";
 import jwt_decode from "jwt-decode";
 import { LoginResponse, MyUserInfo } from "lemmy-js-client";
@@ -21,8 +20,8 @@ interface JwtInfo {
 
 export class UserService {
   private static _instance: UserService;
-  public myUserInfo: Option<MyUserInfo> = None;
-  public jwtInfo: Option<JwtInfo> = None;
+  public myUserInfo?: MyUserInfo;
+  public jwtInfo?: JwtInfo;
   public unreadInboxCountSub: BehaviorSubject<number> =
     new BehaviorSubject<number>(0);
   public unreadReportCountSub: BehaviorSubject<number> =
@@ -37,45 +36,41 @@ export class UserService {
   public login(res: LoginResponse) {
     let expires = new Date();
     expires.setDate(expires.getDate() + 365);
-    res.jwt.match({
-      some: jwt => {
-        toast(i18n.t("logged_in"));
-        IsomorphicCookie.save("jwt", jwt, { expires, secure: isHttps });
-        this.setJwtInfo();
-      },
-      none: void 0,
-    });
+    if (res.jwt) {
+      toast(i18n.t("logged_in"));
+      IsomorphicCookie.save("jwt", res.jwt, { expires, secure: isHttps });
+      this.setJwtInfo();
+    }
   }
 
   public logout() {
-    this.jwtInfo = None;
-    this.myUserInfo = None;
+    this.jwtInfo = undefined;
+    this.myUserInfo = undefined;
     IsomorphicCookie.remove("jwt"); // TODO is sometimes unreliable for some reason
     document.cookie = "jwt=; Max-Age=0; path=/; domain=" + location.hostname;
     location.reload();
   }
 
-  public auth(throwErr = true): Result<string, string> {
-    // Can't use match to convert to result for some reason
-    let jwt = this.jwtInfo.map(j => j.jwt);
-    if (jwt.isSome()) {
-      return Ok(jwt.unwrap());
+  public auth(throwErr = true): string | undefined {
+    let jwt = this.jwtInfo?.jwt;
+    if (jwt) {
+      return jwt;
     } else {
       let msg = "No JWT cookie found";
       if (throwErr && isBrowser()) {
-        console.log(msg);
+        console.error(msg);
         toast(i18n.t("not_logged_in"), "danger");
       }
-      return Err(msg);
+      return undefined;
+      // throw msg;
     }
   }
 
   private setJwtInfo() {
-    let jwt = IsomorphicCookie.load("jwt");
+    let jwt: string | undefined = IsomorphicCookie.load("jwt");
 
     if (jwt) {
-      let jwtInfo: JwtInfo = { jwt, claims: jwt_decode(jwt) };
-      this.jwtInfo = Some(jwtInfo);
+      this.jwtInfo = { jwt, claims: jwt_decode(jwt) };
     }
   }
 
index 5309832505f2ec1c33d3bd6033aa091568ec0d3f..09da9dbab73dc08960845d62888cc253e8138536 100644 (file)
@@ -1,5 +1,3 @@
-import { Err, None, Ok, Option, Result, Some } from "@sniptt/monads";
-import { ClassConstructor, deserialize, serialize } from "class-transformer";
 import emojiShortName from "emoji-short-name";
 import {
   BlockCommunityResponse,
@@ -9,7 +7,6 @@ import {
   CommentReportView,
   CommentSortType,
   CommentView,
-  CommunityBlockView,
   CommunityModeratorView,
   CommunityView,
   GetSiteMetadata,
@@ -18,8 +15,6 @@ import {
   LemmyHttp,
   LemmyWebsocket,
   ListingType,
-  MyUserInfo,
-  PersonBlockView,
   PersonSafe,
   PersonViewSafe,
   PostReportView,
@@ -30,7 +25,6 @@ import {
   Search,
   SearchType,
   SortType,
-  toUndefined,
 } from "lemmy-js-client";
 import { default as MarkdownIt } from "markdown-it";
 import markdown_it_container from "markdown-it-container";
@@ -190,11 +184,11 @@ export function mdToHtmlInline(text: string) {
   return { __html: md.renderInline(text) };
 }
 
-export function getUnixTime(text: string): number {
+export function getUnixTime(text?: string): number | undefined {
   return text ? new Date(text).getTime() / 1000 : undefined;
 }
 
-export function futureDaysToUnixTime(days: number): number {
+export function futureDaysToUnixTime(days?: number): number | undefined {
   return days
     ? Math.trunc(
         new Date(Date.now() + 1000 * 60 * 60 * 24 * days).getTime() / 1000
@@ -203,132 +197,89 @@ export function futureDaysToUnixTime(days: number): number {
 }
 
 export function canMod(
-  mods: Option<CommunityModeratorView[]>,
-  admins: Option<PersonViewSafe[]>,
   creator_id: number,
+  mods?: CommunityModeratorView[],
+  admins?: PersonViewSafe[],
   myUserInfo = UserService.Instance.myUserInfo,
   onSelf = false
 ): boolean {
   // You can do moderator actions only on the mods added after you.
-  let adminsThenMods = admins
-    .unwrapOr([])
-    .map(a => a.person.id)
-    .concat(mods.unwrapOr([]).map(m => m.moderator.id));
-
-  return myUserInfo.match({
-    some: me => {
-      let myIndex = adminsThenMods.findIndex(
-        id => id == me.local_user_view.person.id
-      );
-      if (myIndex == -1) {
-        return false;
-      } else {
-        // onSelf +1 on mod actions not for yourself, IE ban, remove, etc
-        adminsThenMods = adminsThenMods.slice(0, myIndex + (onSelf ? 0 : 1));
-        return !adminsThenMods.includes(creator_id);
-      }
-    },
-    none: false,
-  });
+  let adminsThenMods =
+    admins
+      ?.map(a => a.person.id)
+      .concat(mods?.map(m => m.moderator.id) ?? []) ?? [];
+
+  if (myUserInfo) {
+    let myIndex = adminsThenMods.findIndex(
+      id => id == myUserInfo.local_user_view.person.id
+    );
+    if (myIndex == -1) {
+      return false;
+    } else {
+      // onSelf +1 on mod actions not for yourself, IE ban, remove, etc
+      adminsThenMods = adminsThenMods.slice(0, myIndex + (onSelf ? 0 : 1));
+      return !adminsThenMods.includes(creator_id);
+    }
+  } else {
+    return false;
+  }
 }
 
 export function canAdmin(
-  admins: Option<PersonViewSafe[]>,
-  creator_id: number,
+  creatorId: number,
+  admins?: PersonViewSafe[],
   myUserInfo = UserService.Instance.myUserInfo,
   onSelf = false
 ): boolean {
-  return canMod(None, admins, creator_id, myUserInfo, onSelf);
+  return canMod(creatorId, undefined, admins, myUserInfo, onSelf);
 }
 
 export function isMod(
-  mods: Option<CommunityModeratorView[]>,
-  creator_id: number
+  creatorId: number,
+  mods?: CommunityModeratorView[]
 ): boolean {
-  return mods.match({
-    some: mods => mods.map(m => m.moderator.id).includes(creator_id),
-    none: false,
-  });
+  return mods?.map(m => m.moderator.id).includes(creatorId) ?? false;
 }
 
 export function amMod(
-  mods: Option<CommunityModeratorView[]>,
+  mods?: CommunityModeratorView[],
   myUserInfo = UserService.Instance.myUserInfo
 ): boolean {
-  return myUserInfo.match({
-    some: mui => isMod(mods, mui.local_user_view.person.id),
-    none: false,
-  });
+  return myUserInfo ? isMod(myUserInfo.local_user_view.person.id, mods) : false;
 }
 
-export function isAdmin(
-  admins: Option<PersonViewSafe[]>,
-  creator_id: number
-): boolean {
-  return admins.match({
-    some: admins => admins.map(a => a.person.id).includes(creator_id),
-    none: false,
-  });
+export function isAdmin(creatorId: number, admins?: PersonViewSafe[]): boolean {
+  return admins?.map(a => a.person.id).includes(creatorId) ?? false;
 }
 
 export function amAdmin(myUserInfo = UserService.Instance.myUserInfo): boolean {
-  return myUserInfo
-    .map(mui => mui.local_user_view.person.admin)
-    .unwrapOr(false);
+  return myUserInfo?.local_user_view.person.admin ?? false;
 }
 
 export function amCommunityCreator(
-  mods: Option<CommunityModeratorView[]>,
   creator_id: number,
+  mods?: CommunityModeratorView[],
   myUserInfo = UserService.Instance.myUserInfo
 ): boolean {
-  return mods.match({
-    some: mods =>
-      myUserInfo
-        .map(mui => mui.local_user_view.person.id)
-        .match({
-          some: myId =>
-            myId == mods[0].moderator.id &&
-            // Don't allow mod actions on yourself
-            myId != creator_id,
-          none: false,
-        }),
-    none: false,
-  });
+  let myId = myUserInfo?.local_user_view.person.id;
+  // Don't allow mod actions on yourself
+  return myId == mods?.at(0)?.moderator.id && myId != creator_id;
 }
 
 export function amSiteCreator(
-  admins: Option<PersonViewSafe[]>,
   creator_id: number,
+  admins?: PersonViewSafe[],
   myUserInfo = UserService.Instance.myUserInfo
 ): boolean {
-  return admins.match({
-    some: admins =>
-      myUserInfo
-        .map(mui => mui.local_user_view.person.id)
-        .match({
-          some: myId =>
-            myId == admins[0].person.id &&
-            // Don't allow mod actions on yourself
-            myId != creator_id,
-          none: false,
-        }),
-    none: false,
-  });
+  let myId = myUserInfo?.local_user_view.person.id;
+  return myId == admins?.at(0)?.person.id && myId != creator_id;
 }
 
 export function amTopMod(
-  mods: Option<CommunityModeratorView[]>,
+  mods: CommunityModeratorView[],
   myUserInfo = UserService.Instance.myUserInfo
 ): boolean {
-  return mods.match({
-    some: mods =>
-      myUserInfo.match({
-        some: mui => mods[0].moderator.id == mui.local_user_view.person.id,
-        none: false,
-      }),
-    none: false,
-  });
+  return mods.at(0)?.moderator.id == myUserInfo?.local_user_view.person.id;
 }
 
 const imageRegex = /(http)?s?:?(\/\/[^"']*\.(?:jpg|jpeg|gif|png|svg|webp))/;
@@ -386,9 +337,7 @@ export function routeSearchTypeToEnum(type: string): SearchType {
 }
 
 export async function getSiteMetadata(url: string) {
-  let form = new GetSiteMetadata({
-    url,
-  });
+  let form: GetSiteMetadata = { url };
   let client = new LemmyHttp(httpBase);
   return client.getSiteMetadata(form);
 }
@@ -438,10 +387,8 @@ export function getLanguages(
   override?: string,
   myUserInfo = UserService.Instance.myUserInfo
 ): string[] {
-  let myLang = myUserInfo
-    .map(m => m.local_user_view.local_user.interface_language)
-    .unwrapOr("browser");
-  let lang = override || myLang;
+  let myLang = myUserInfo?.local_user_view.local_user.interface_language;
+  let lang = override || myLang || "browser";
 
   if (lang == "browser" && isBrowser()) {
     return getBrowserLanguages();
@@ -496,7 +443,7 @@ export async function setTheme(theme: string, forceReload = false) {
   let cssLoc = `/css/themes/${theme}.css`;
 
   loadCss(theme, cssLoc);
-  document.getElementById(theme).removeAttribute("disabled");
+  document.getElementById(theme)?.removeAttribute("disabled");
 }
 
 export function loadCss(id: string, loc: string) {
@@ -521,19 +468,15 @@ export function objectFlip(obj: any) {
 }
 
 export function showAvatars(
-  myUserInfo: Option<MyUserInfo> = UserService.Instance.myUserInfo
+  myUserInfo = UserService.Instance.myUserInfo
 ): boolean {
-  return myUserInfo
-    .map(m => m.local_user_view.local_user.show_avatars)
-    .unwrapOr(true);
+  return myUserInfo?.local_user_view.local_user.show_avatars ?? true;
 }
 
 export function showScores(
-  myUserInfo: Option<MyUserInfo> = UserService.Instance.myUserInfo
+  myUserInfo = UserService.Instance.myUserInfo
 ): boolean {
-  return myUserInfo
-    .map(m => m.local_user_view.local_user.show_scores)
-    .unwrapOr(true);
+  return myUserInfo?.local_user_view.local_user.show_scores ?? true;
 }
 
 export function isCakeDay(published: string): boolean {
@@ -595,9 +538,9 @@ export function pictrsDeleteToast(
 
 interface NotifyInfo {
   name: string;
-  icon: Option<string>;
+  icon?: string;
   link: string;
-  body: string;
+  body?: string;
 }
 
 export function messageToastify(info: NotifyInfo, router: any) {
@@ -607,7 +550,7 @@ export function messageToastify(info: NotifyInfo, router: any) {
 
     let toast = Toastify({
       text: `${htmlBody}<br />${info.name}`,
-      avatar: toUndefined(info.icon),
+      avatar: info.icon,
       backgroundColor: backgroundColor,
       className: "text-dark",
       close: true,
@@ -663,7 +606,7 @@ function notify(info: NotifyInfo, router: any) {
   else {
     var notification = new Notification(info.name, {
       ...{ body: info.body },
-      ...(info.icon.isSome() && { icon: info.icon.unwrap() }),
+      ...(info.icon && { icon: info.icon }),
     });
 
     notification.onclick = (ev: Event): any => {
@@ -790,15 +733,12 @@ export function getListingTypeFromProps(
   defaultListingType: ListingType,
   myUserInfo = UserService.Instance.myUserInfo
 ): ListingType {
+  let myLt = myUserInfo?.local_user_view.local_user.default_listing_type;
   return props.match.params.listing_type
     ? routeListingTypeToEnum(props.match.params.listing_type)
-    : myUserInfo.match({
-        some: me =>
-          Object.values(ListingType)[
-            me.local_user_view.local_user.default_listing_type
-          ],
-        none: defaultListingType,
-      });
+    : myLt
+    ? Object.values(ListingType)[myLt]
+    : defaultListingType;
 }
 
 export function getListingTypeFromPropsNoDefault(props: any): ListingType {
@@ -817,15 +757,12 @@ export function getSortTypeFromProps(
   props: any,
   myUserInfo = UserService.Instance.myUserInfo
 ): SortType {
+  let mySortType = myUserInfo?.local_user_view.local_user.default_sort_type;
   return props.match.params.sort
     ? routeSortTypeToEnum(props.match.params.sort)
-    : myUserInfo.match({
-        some: mui =>
-          Object.values(SortType)[
-            mui.local_user_view.local_user.default_sort_type
-          ],
-        none: SortType.Active,
-      });
+    : mySortType
+    ? Object.values(SortType)[mySortType]
+    : SortType.Active;
 }
 
 export function getPageFromProps(props: any): number {
@@ -838,22 +775,22 @@ export function getRecipientIdFromProps(props: any): number {
     : 1;
 }
 
-export function getIdFromProps(props: any): Option<number> {
-  let id: string = props.match.params.post_id;
-  return id ? Some(Number(id)) : None;
+export function getIdFromProps(props: any): number | undefined {
+  let id = props.match.params.post_id;
+  return id ? Number(id) : undefined;
 }
 
-export function getCommentIdFromProps(props: any): Option<number> {
-  let id: string = props.match.params.comment_id;
-  return id ? Some(Number(id)) : None;
+export function getCommentIdFromProps(props: any): number | undefined {
+  let id = props.match.params.comment_id;
+  return id ? Number(id) : undefined;
 }
 
 export function getUsernameFromProps(props: any): string {
   return props.match.params.username;
 }
 
-export function editCommentRes(data: CommentView, comments: CommentView[]) {
-  let found = comments.find(c => c.comment.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.comment.content = data.comment.content;
     found.comment.distinguished = data.comment.distinguished;
@@ -866,67 +803,60 @@ export function editCommentRes(data: CommentView, comments: CommentView[]) {
   }
 }
 
-export function saveCommentRes(data: CommentView, comments: CommentView[]) {
-  let found = comments.find(c => c.comment.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.saved;
   }
 }
 
-// TODO Should only use the return now, no state?
 export function updatePersonBlock(
   data: BlockPersonResponse,
   myUserInfo = UserService.Instance.myUserInfo
-): Option<PersonBlockView[]> {
-  return myUserInfo.match({
-    some: (mui: MyUserInfo) => {
-      if (data.blocked) {
-        mui.person_blocks.push({
-          person: mui.local_user_view.person,
-          target: data.person_view.person,
-        });
-        toast(`${i18n.t("blocked")} ${data.person_view.person.name}`);
-      } else {
-        mui.person_blocks = mui.person_blocks.filter(
-          i => i.target.id != data.person_view.person.id
-        );
-        toast(`${i18n.t("unblocked")} ${data.person_view.person.name}`);
-      }
-      return Some(mui.person_blocks);
-    },
-    none: None,
-  });
+) {
+  let mui = myUserInfo;
+  if (mui) {
+    if (data.blocked) {
+      mui.person_blocks.push({
+        person: mui.local_user_view.person,
+        target: data.person_view.person,
+      });
+      toast(`${i18n.t("blocked")} ${data.person_view.person.name}`);
+    } else {
+      mui.person_blocks = mui.person_blocks.filter(
+        i => i.target.id != data.person_view.person.id
+      );
+      toast(`${i18n.t("unblocked")} ${data.person_view.person.name}`);
+    }
+  }
 }
 
 export function updateCommunityBlock(
   data: BlockCommunityResponse,
   myUserInfo = UserService.Instance.myUserInfo
-): Option<CommunityBlockView[]> {
-  return myUserInfo.match({
-    some: (mui: MyUserInfo) => {
-      if (data.blocked) {
-        mui.community_blocks.push({
-          person: mui.local_user_view.person,
-          community: data.community_view.community,
-        });
-        toast(`${i18n.t("blocked")} ${data.community_view.community.name}`);
-      } else {
-        mui.community_blocks = mui.community_blocks.filter(
-          i => i.community.id != data.community_view.community.id
-        );
-        toast(`${i18n.t("unblocked")} ${data.community_view.community.name}`);
-      }
-      return Some(mui.community_blocks);
-    },
-    none: None,
-  });
+) {
+  let mui = myUserInfo;
+  if (mui) {
+    if (data.blocked) {
+      mui.community_blocks.push({
+        person: mui.local_user_view.person,
+        community: data.community_view.community,
+      });
+      toast(`${i18n.t("blocked")} ${data.community_view.community.name}`);
+    } else {
+      mui.community_blocks = mui.community_blocks.filter(
+        i => i.community.id != data.community_view.community.id
+      );
+      toast(`${i18n.t("unblocked")} ${data.community_view.community.name}`);
+    }
+  }
 }
 
 export function createCommentLikeRes(
   data: CommentView,
-  comments: CommentView[]
+  comments?: CommentView[]
 ) {
-  let found = comments.find(c => c.comment.id === data.comment.id);
+  let found = comments?.find(c => c.comment.id === data.comment.id);
   if (found) {
     found.counts.score = data.counts.score;
     found.counts.upvotes = data.counts.upvotes;
@@ -937,14 +867,14 @@ export function createCommentLikeRes(
   }
 }
 
-export function createPostLikeFindRes(data: PostView, posts: PostView[]) {
-  let found = posts.find(p => p.post.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: PostView, post_view: PostView) {
+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;
@@ -955,8 +885,8 @@ export function createPostLikeRes(data: PostView, post_view: PostView) {
   }
 }
 
-export function editPostFindRes(data: PostView, posts: PostView[]) {
-  let found = posts.find(p => p.post.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);
   }
@@ -980,9 +910,9 @@ export function editPostRes(data: PostView, post: PostView) {
 // TODO possible to make these generic?
 export function updatePostReportRes(
   data: PostReportView,
-  reports: PostReportView[]
+  reports?: PostReportView[]
 ) {
-  let found = reports.find(p => p.post_report.id == data.post_report.id);
+  let found = reports?.find(p => p.post_report.id == data.post_report.id);
   if (found) {
     found.post_report = data.post_report;
   }
@@ -990,9 +920,9 @@ export function updatePostReportRes(
 
 export function updateCommentReportRes(
   data: CommentReportView,
-  reports: CommentReportView[]
+  reports?: CommentReportView[]
 ) {
-  let found = reports.find(c => c.comment_report.id == data.comment_report.id);
+  let found = reports?.find(c => c.comment_report.id == data.comment_report.id);
   if (found) {
     found.comment_report = data.comment_report;
   }
@@ -1000,9 +930,9 @@ export function updateCommentReportRes(
 
 export function updatePrivateMessageReportRes(
   data: PrivateMessageReportView,
-  reports: PrivateMessageReportView[]
+  reports?: PrivateMessageReportView[]
 ) {
-  let found = reports.find(
+  let found = reports?.find(
     c => c.private_message_report.id == data.private_message_report.id
   );
   if (found) {
@@ -1012,9 +942,9 @@ export function updatePrivateMessageReportRes(
 
 export function updateRegistrationApplicationRes(
   data: RegistrationApplicationView,
-  applications: RegistrationApplicationView[]
+  applications?: RegistrationApplicationView[]
 ) {
-  let found = applications.find(
+  let found = applications?.find(
     ra => ra.registration_application.id == data.registration_application.id
   );
   if (found) {
@@ -1057,13 +987,15 @@ export function buildCommentsTree(
   let map = new Map<number, CommentNodeI>();
   let depthOffset = !parentComment
     ? 0
-    : getDepthFromComment(comments[0].comment);
+    : getDepthFromComment(comments[0].comment) ?? 0;
 
   for (let comment_view of comments) {
+    let depthI = getDepthFromComment(comment_view.comment) ?? 0;
+    let depth = depthI ? depthI - depthOffset : 0;
     let node: CommentNodeI = {
-      comment_view: comment_view,
+      comment_view,
       children: [],
-      depth: getDepthFromComment(comment_view.comment) - depthOffset,
+      depth,
     };
     map.set(comment_view.comment.id, { ...node });
   }
@@ -1072,45 +1004,46 @@ export function buildCommentsTree(
 
   // if its a parent comment fetch, then push the first comment to the top node.
   if (parentComment) {
-    tree.push(map.get(comments[0].comment.id));
+    let cNode = map.get(comments[0].comment.id);
+    if (cNode) {
+      tree.push(cNode);
+    }
   }
 
   for (let comment_view of comments) {
     let child = map.get(comment_view.comment.id);
-    let parent_id = getCommentParentId(comment_view.comment);
-    parent_id.match({
-      some: parentId => {
-        let parent = map.get(parentId);
+    if (child) {
+      let parent_id = getCommentParentId(comment_view.comment);
+      if (parent_id) {
+        let parent = map.get(parent_id);
         // Necessary because blocked comment might not exist
         if (parent) {
           parent.children.push(child);
         }
-      },
-      none: () => {
+      } else {
         if (!parentComment) {
           tree.push(child);
         }
-      },
-    });
+      }
+    }
   }
 
   return tree;
 }
 
-export function getCommentParentId(comment: CommentI): Option<number> {
-  let split = comment.path.split(".");
+export function getCommentParentId(comment?: CommentI): number | undefined {
+  let split = comment?.path.split(".");
   // remove the 0
-  split.shift();
+  split?.shift();
 
-  if (split.length > 1) {
-    return Some(Number(split[split.length - 2]));
-  } else {
-    return None;
-  }
+  return split && split.length > 1
+    ? Number(split.at(split.length - 2))
+    : undefined;
 }
 
-export function getDepthFromComment(comment: CommentI): number {
-  return comment.path.split(".").length - 2;
+export function getDepthFromComment(comment?: CommentI): number | undefined {
+  let len = comment?.path.split(".").length;
+  return len ? len - 2 : undefined;
 }
 
 export function insertCommentIntoTree(
@@ -1125,43 +1058,36 @@ export function insertCommentIntoTree(
     depth: 0,
   };
 
-  getCommentParentId(cv.comment).match({
-    some: parentId => {
-      let parentComment = searchCommentTree(tree, parentId);
-      parentComment.match({
-        some: pComment => {
-          node.depth = pComment.depth + 1;
-          pComment.children.unshift(node);
-        },
-        none: void 0,
-      });
-    },
-    none: () => {
-      if (!parentComment) {
-        tree.unshift(node);
-      }
-    },
-  });
+  let parentId = getCommentParentId(cv.comment);
+  if (parentId) {
+    let parent_comment = searchCommentTree(tree, parentId);
+    if (parent_comment) {
+      node.depth = parent_comment.depth + 1;
+      parent_comment.children.unshift(node);
+    }
+  } else if (!parentComment) {
+    tree.unshift(node);
+  }
 }
 
 export function searchCommentTree(
   tree: CommentNodeI[],
   id: number
-): Option<CommentNodeI> {
+): CommentNodeI | undefined {
   for (let node of tree) {
     if (node.comment_view.comment.id === id) {
-      return Some(node);
+      return node;
     }
 
     for (const child of node.children) {
       let res = searchCommentTree([child], id);
 
-      if (res.isSome()) {
+      if (res) {
         return res;
       }
     }
   }
-  return None;
+  return undefined;
 }
 
 export const colorList: string[] = [
@@ -1209,55 +1135,23 @@ export function isBrowser() {
   return typeof window !== "undefined";
 }
 
-export function setIsoData<Type1, Type2, Type3, Type4, Type5>(
-  context: any,
-  cls1?: ClassConstructor<Type1>,
-  cls2?: ClassConstructor<Type2>,
-  cls3?: ClassConstructor<Type3>,
-  cls4?: ClassConstructor<Type4>,
-  cls5?: ClassConstructor<Type5>
-): IsoData {
+export function setIsoData(context: any): IsoData {
   // If its the browser, you need to deserialize the data from the window
   if (isBrowser()) {
     let json = window.isoData;
     let routeData = json.routeData;
-    let routeDataOut: any[] = [];
-
-    // Can't do array looping because of specific type constructor required
-    if (routeData[0]) {
-      routeDataOut[0] = convertWindowJson(cls1, routeData[0]);
-    }
-    if (routeData[1]) {
-      routeDataOut[1] = convertWindowJson(cls2, routeData[1]);
-    }
-    if (routeData[2]) {
-      routeDataOut[2] = convertWindowJson(cls3, routeData[2]);
-    }
-    if (routeData[3]) {
-      routeDataOut[3] = convertWindowJson(cls4, routeData[3]);
-    }
-    if (routeData[4]) {
-      routeDataOut[4] = convertWindowJson(cls5, routeData[4]);
-    }
-    let site_res = convertWindowJson(GetSiteResponse, json.site_res);
+    let site_res = json.site_res;
 
     let isoData: IsoData = {
       path: json.path,
       site_res,
-      routeData: routeDataOut,
+      routeData,
     };
     return isoData;
   } else return context.router.staticContext;
 }
 
-/**
- * Necessary since window ISOData can't store function types like Option
- */
-export function convertWindowJson<T>(cls: ClassConstructor<T>, data: any): T {
-  return deserialize(cls, serialize(data));
-}
-
-export function wsSubscribe(parseMessage: any): Subscription {
+export function wsSubscribe(parseMessage: any): Subscription | undefined {
   if (isBrowser()) {
     return WebSocketService.Instance.subject
       .pipe(retryWhen(errors => errors.pipe(delay(3000), take(10))))
@@ -1267,7 +1161,7 @@ export function wsSubscribe(parseMessage: any): Subscription {
         () => console.log("complete")
       );
   } else {
-    return null;
+    return undefined;
   }
 }
 
@@ -1305,9 +1199,8 @@ export function restoreScrollPosition(context: any) {
 }
 
 export function showLocal(isoData: IsoData): boolean {
-  return isoData.site_res.federated_instances
-    .map(f => f.linked.length > 0)
-    .unwrapOr(false);
+  let linked = isoData.site_res.federated_instances?.linked;
+  return linked ? linked.length > 0 : false;
 }
 
 export interface ChoicesValue {
@@ -1332,35 +1225,29 @@ export function personToChoice(pvs: PersonViewSafe): ChoicesValue {
 }
 
 export async function fetchCommunities(q: string) {
-  let form = new Search({
+  let form: Search = {
     q,
-    type_: Some(SearchType.Communities),
-    sort: Some(SortType.TopAll),
-    listing_type: Some(ListingType.All),
-    page: Some(1),
-    limit: Some(fetchLimit),
-    community_id: None,
-    community_name: None,
-    creator_id: None,
-    auth: auth(false).ok(),
-  });
+    type_: SearchType.Communities,
+    sort: SortType.TopAll,
+    listing_type: ListingType.All,
+    page: 1,
+    limit: fetchLimit,
+    auth: myAuth(false),
+  };
   let client = new LemmyHttp(httpBase);
   return client.search(form);
 }
 
 export async function fetchUsers(q: string) {
-  let form = new Search({
+  let form: Search = {
     q,
-    type_: Some(SearchType.Users),
-    sort: Some(SortType.TopAll),
-    listing_type: Some(ListingType.All),
-    page: Some(1),
-    limit: Some(fetchLimit),
-    community_id: None,
-    community_name: None,
-    creator_id: None,
-    auth: auth(false).ok(),
-  });
+    type_: SearchType.Users,
+    sort: SortType.TopAll,
+    listing_type: ListingType.All,
+    page: 1,
+    limit: fetchLimit,
+    auth: myAuth(false),
+  };
   let client = new LemmyHttp(httpBase);
   return client.search(form);
 }
@@ -1406,7 +1293,7 @@ export function communitySelectName(cv: CommunityView): string {
 }
 
 export function personSelectName(pvs: PersonViewSafe): string {
-  let pName = pvs.person.display_name.unwrapOr(pvs.person.name);
+  let pName = pvs.person.display_name ?? pvs.person.name;
   return pvs.person.local ? pName : `${hostname(pvs.person.actor_id)}/${pName}`;
 }
 
@@ -1430,8 +1317,8 @@ export function isBanned(ps: PersonSafe): boolean {
   let expires = ps.ban_expires;
   // Add Z to convert from UTC date
   // TODO this check probably isn't necessary anymore
-  if (expires.isSome()) {
-    if (ps.banned && new Date(expires.unwrap() + "Z") > new Date()) {
+  if (expires) {
+    if (ps.banned && new Date(expires + "Z") > new Date()) {
       return true;
     } else {
       return false;
@@ -1447,7 +1334,7 @@ export function pushNotNull(array: any[], new_item?: any) {
   }
 }
 
-export function auth(throwErr = true): Result<string, string> {
+export function myAuth(throwErr = true): string | undefined {
   return UserService.Instance.auth(throwErr);
 }
 
@@ -1471,26 +1358,18 @@ export function postToCommentSortType(sort: SortType): CommentSortType {
   }
 }
 
-export function arrayGet<T>(arr: Array<T>, index: number): Result<T, string> {
-  let out = arr.at(index);
-  if (out == undefined) {
-    return Err("Index undefined");
-  } else {
-    return Ok(out);
-  }
-}
-
 export function myFirstDiscussionLanguageId(
   allLanguages: Language[],
   siteLanguages: number[],
   myUserInfo = UserService.Instance.myUserInfo
-): Option<number> {
-  return arrayGet(
-    selectableLanguages(allLanguages, siteLanguages, false, false, myUserInfo),
-    0
-  )
-    .map(l => l.id)
-    .ok();
+): number | undefined {
+  return selectableLanguages(
+    allLanguages,
+    siteLanguages,
+    false,
+    false,
+    myUserInfo
+  ).at(0)?.id;
 }
 
 export function canCreateCommunity(
@@ -1505,15 +1384,15 @@ export function isPostBlocked(
   pv: PostView,
   myUserInfo = UserService.Instance.myUserInfo
 ): boolean {
-  return myUserInfo
-    .map(
-      mui =>
-        mui.community_blocks
-          .map(c => c.community.id)
-          .includes(pv.community.id) ||
-        mui.person_blocks.map(p => p.target.id).includes(pv.creator.id)
-    )
-    .unwrapOr(false);
+  return (
+    (myUserInfo?.community_blocks
+      .map(c => c.community.id)
+      .includes(pv.community.id) ||
+      myUserInfo?.person_blocks
+        .map(p => p.target.id)
+        .includes(pv.creator.id)) ??
+    false
+  );
 }
 
 /// Checks to make sure you can view NSFW posts. Returns true if you can.
@@ -1522,17 +1401,12 @@ export function nsfwCheck(
   myUserInfo = UserService.Instance.myUserInfo
 ): boolean {
   let nsfw = pv.post.nsfw || pv.community.nsfw;
-  return (
-    !nsfw ||
-    (nsfw &&
-      myUserInfo
-        .map(m => m.local_user_view.local_user.show_nsfw)
-        .unwrapOr(false))
-  );
+  let myShowNsfw = myUserInfo?.local_user_view.local_user.show_nsfw ?? false;
+  return !nsfw || (nsfw && myShowNsfw);
 }
 
-export function getRandomFromList<T>(list: T[]): T {
-  return list[Math.floor(Math.random() * list.length)];
+export function getRandomFromList<T>(list?: T[]): T | undefined {
+  return list?.at(Math.floor(Math.random() * list.length));
 }
 
 /**
@@ -1545,14 +1419,12 @@ export function getRandomFromList<T>(list: T[]): T {
 export function selectableLanguages(
   allLanguages: Language[],
   siteLanguages: number[],
-  showAll: boolean,
-  showSite: boolean,
+  showAll?: boolean,
+  showSite?: boolean,
   myUserInfo = UserService.Instance.myUserInfo
 ): Language[] {
   let allLangIds = allLanguages.map(l => l.id);
-  let myLangs = myUserInfo
-    .map(mui => mui.discussion_languages)
-    .unwrapOr(allLangIds);
+  let myLangs = myUserInfo?.discussion_languages ?? allLangIds;
   myLangs = myLangs.length == 0 ? allLangIds : myLangs;
   let siteLangs = siteLanguages.length == 0 ? allLangIds : siteLanguages;
 
index c3dc6cf5b669a8c73272bcbbf8fd54732a941af5..d7c880303bba6d6860a8ea7551c9604711eb20d3 100644 (file)
@@ -19,7 +19,7 @@
                "noUnusedParameters": true,\r
                "noImplicitReturns": true,\r
     "experimentalDecorators": true,\r
-    "strictNullChecks": false,\r
+    "strictNullChecks": true,\r
     "noFallthroughCasesInSwitch": true\r
        },\r
        "include": [\r
index 163e6203ab7c4ccddc6968b64e6c72de3a73d784..cceba437106e325526a018acc47da5907c81379f 100644 (file)
--- a/yarn.lock
+++ b/yarn.lock
     domhandler "^4.2.0"
     selderee "^0.6.0"
 
-"@sniptt/monads@^0.5.10":
-  version "0.5.10"
-  resolved "https://registry.yarnpkg.com/@sniptt/monads/-/monads-0.5.10.tgz#a80cd00738bbd682d36d36dd36bdc0bddc96eb76"
-  integrity sha512-+agDOv9DpDV+9e2zN/Vmdk+XaqGx5Sykl0fqhqgiJ90r18nsBkxe44DmZ2sA1HYK+MSsBeZBiAr6pq4w+5uhfw==
-
 "@types/autosize@^4.0.0":
   version "4.0.1"
   resolved "https://registry.yarnpkg.com/@types/autosize/-/autosize-4.0.1.tgz#999a7c305b96766248044ebaac1a0299961f3b61"
@@ -2263,11 +2258,6 @@ cidr-regex@1.0.6:
   resolved "https://registry.yarnpkg.com/cidr-regex/-/cidr-regex-1.0.6.tgz#74abfd619df370b9d54ab14475568e97dd64c0c1"
   integrity sha512-vIIQZtDT0y3GmcVqi4Uhd43s7HKn5DtH8/CcmHe/XG1Vb4JpUgOfTynZzYSo1zeB+j4GbA38Eu2P9UTbIzDw5g==
 
-class-transformer@^0.5.1:
-  version "0.5.1"
-  resolved "https://registry.yarnpkg.com/class-transformer/-/class-transformer-0.5.1.tgz#24147d5dffd2a6cea930a3250a677addf96ab336"
-  integrity sha512-SQa1Ws6hUbfC98vKGxZH3KFY0Y1lm5Zm0SY8XX9zbK7FJCyVEac3ATW0RIpwzW+oOfmHE5PMPufDG9hCfoEOMw==
-
 classnames@^2.3.1:
   version "2.3.2"
   resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.3.2.tgz#351d813bf0137fcc6a76a16b88208d2560a0d924"
@@ -4982,15 +4972,12 @@ lcid@^1.0.0:
   dependencies:
     invert-kv "^1.0.0"
 
-lemmy-js-client@0.17.0-rc.57:
-  version "0.17.0-rc.57"
-  resolved "https://registry.yarnpkg.com/lemmy-js-client/-/lemmy-js-client-0.17.0-rc.57.tgz#f7a243ed53542810e7446b0a28ad162f3e913254"
-  integrity sha512-7kZHi0B+jiKc50itTwngkS5Vxcuvux3LjgD28IXZ049cWQgZDqer6BCmudcbViP+dAoyWs9Fh2SyWkYFhv7bwQ==
+lemmy-js-client@0.17.0-rc.61:
+  version "0.17.0-rc.61"
+  resolved "https://registry.yarnpkg.com/lemmy-js-client/-/lemmy-js-client-0.17.0-rc.61.tgz#c01e129a3d4c3483ecf337f1e4acf0ad91f9684f"
+  integrity sha512-xauBCD5i4vlUEWqsTMIXLCXeIjAK7ivVIN3C/g+RMAM7mD3CTcRkDZUerwnvLipIfr7V/4iYLWZW0orBaiV1CQ==
   dependencies:
-    "@sniptt/monads" "^0.5.10"
-    class-transformer "^0.5.1"
     node-fetch "2.6.6"
-    reflect-metadata "^0.1.13"
 
 levn@^0.4.1:
   version "0.4.1"
@@ -6969,11 +6956,6 @@ redux@^4.1.2:
   dependencies:
     "@babel/runtime" "^7.9.2"
 
-reflect-metadata@^0.1.13:
-  version "0.1.13"
-  resolved "https://registry.yarnpkg.com/reflect-metadata/-/reflect-metadata-0.1.13.tgz#67ae3ca57c972a2aa1642b10fe363fe32d49dc08"
-  integrity sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg==
-
 regenerate-unicode-properties@^10.1.0:
   version "10.1.0"
   resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-10.1.0.tgz#7c3192cab6dd24e21cb4461e5ddd7dd24fa8374c"