]> Untitled Git - lemmy-ui.git/commitdiff
Upgrade inferno v8.0.0 try2 (#790)
authorDessalines <dessalines@users.noreply.github.com>
Thu, 22 Sep 2022 15:03:35 +0000 (11:03 -0400)
committerGitHub <noreply@github.com>
Thu, 22 Sep 2022 15:03:35 +0000 (11:03 -0400)
* Upgrade non-breaking deps.

* Upgrade to Inferno v8. Fixes #731

* Upgrading inferno-i18next-dess

56 files changed:
.eslintrc.json
package.json
src/shared/components/app/app.tsx
src/shared/components/app/footer.tsx
src/shared/components/app/navbar.tsx
src/shared/components/app/no-match.tsx
src/shared/components/comment/comment-form.tsx
src/shared/components/comment/comment-node.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/icon.tsx
src/shared/components/common/image-upload-form.tsx
src/shared/components/common/listing-type-select.tsx
src/shared/components/common/markdown-textarea.tsx
src/shared/components/common/paginator.tsx
src/shared/components/common/registration-application.tsx
src/shared/components/common/sort-select.tsx
src/shared/components/common/symbols.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/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.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.tsx
src/shared/components/search.tsx
src/shared/utils.ts
yarn.lock

index 4e05f0c5a73bf41a8da67062d94fe3953d2648cd..0c9a5f46f8beaa3b9298e456c0207b2a88228d1e 100644 (file)
@@ -8,7 +8,8 @@
   ],
   "extends": [
     "eslint:recommended",
-    "plugin:@typescript-eslint/recommended"
+    "plugin:@typescript-eslint/recommended",
+    "plugin:inferno/recommended"
   ],
   "parser": "@typescript-eslint/parser",
   "parserOptions": {
index a430314b31de4333fac7cd64c44dd2f6ffdf5fc5..903974e96ed5470b9defdf7a9f93f55715ad6000 100644 (file)
     "emoji-short-name": "^2.0.0",
     "express": "~4.18.1",
     "i18next": "^21.8.14",
-    "inferno": "^7.4.11",
-    "inferno-create-element": "^7.4.11",
+    "inferno": "^8.0.3",
+    "inferno-create-element": "^8.0.3",
     "inferno-helmet": "^5.2.1",
-    "inferno-hydrate": "^7.4.11",
-    "inferno-i18next-dess": "^0.0.1",
-    "inferno-router": "^7.4.11",
-    "inferno-server": "^7.4.11",
+    "inferno-hydrate": "^8.0.3",
+    "inferno-i18next-dess": "0.0.2",
+    "inferno-router": "^8.0.3",
+    "inferno-server": "^8.0.3",
     "isomorphic-cookie": "^1.2.4",
     "jwt-decode": "^3.1.2",
     "markdown-it": "^13.0.1",
@@ -55,7 +55,7 @@
     "@babel/plugin-proposal-decorators": "^7.18.9",
     "@babel/plugin-transform-runtime": "^7.18.9",
     "@babel/plugin-transform-typescript": "^7.18.8",
-    "@babel/preset-env": "7.18.9",
+    "@babel/preset-env": "7.19.1",
     "@babel/preset-typescript": "^7.18.6",
     "@babel/runtime": "^7.18.9",
     "@sniptt/monads": "^0.5.10",
@@ -74,6 +74,7 @@
     "copy-webpack-plugin": "^11.0.0",
     "css-loader": "^6.7.1",
     "eslint": "^8.20.0",
+    "eslint-plugin-inferno": "^7.31.8",
     "eslint-plugin-prettier": "^4.2.1",
     "husky": "^8.0.1",
     "import-sort-style-module": "^6.0.0",
@@ -95,7 +96,7 @@
     "typescript": "^4.7.4",
     "webpack": "5.74.0",
     "webpack-cli": "^4.10.0",
-    "webpack-dev-server": "4.9.3",
+    "webpack-dev-server": "4.11.1",
     "webpack-node-externals": "^3.0.0"
   },
   "engines": {
index 72119a8d601cc9d7f6f21aa98b7e6f933d09e487..960f6badda7b4e50c8ca3058e7c612716c02daa2 100644 (file)
@@ -42,7 +42,7 @@ export class App extends Component<any, any> {
                 none: <></>,
               })}
             <Navbar siteRes={siteRes} />
-            <div class="mt-4 p-0 fl-1">
+            <div className="mt-4 p-0 fl-1">
               <Switch>
                 {routes.map(({ path, exact, component: C, ...rest }) => (
                   <Route
index e5e1db5a5b0a59eb08ecd08ac818506cd9b42885..f2a5e83c916ccb9734a32c2b3d7222551b3c9c00 100644 (file)
@@ -16,16 +16,16 @@ export class Footer extends Component<FooterProps, any> {
 
   render() {
     return (
-      <nav class="container navbar navbar-expand-md navbar-light navbar-bg p-3">
+      <nav className="container navbar navbar-expand-md navbar-light navbar-bg p-3">
         <div className="navbar-collapse">
-          <ul class="navbar-nav ml-auto">
+          <ul className="navbar-nav ml-auto">
             {this.props.site.version !== VERSION && (
-              <li class="nav-item">
-                <span class="nav-link">UI: {VERSION}</span>
+              <li className="nav-item">
+                <span className="nav-link">UI: {VERSION}</span>
               </li>
             )}
-            <li class="nav-item">
-              <span class="nav-link">BE: {this.props.site.version}</span>
+            <li className="nav-item">
+              <span className="nav-link">BE: {this.props.site.version}</span>
             </li>
             <li className="nav-item">
               <NavLink className="nav-link" to="/modlog">
@@ -42,23 +42,23 @@ export class Footer extends Component<FooterProps, any> {
               </li>
             )}
             {this.props.site.federated_instances && (
-              <li class="nav-item">
+              <li className="nav-item">
                 <NavLink className="nav-link" to="/instances">
                   {i18n.t("instances")}
                 </NavLink>
               </li>
             )}
-            <li class="nav-item">
+            <li className="nav-item">
               <a className="nav-link" href={docsUrl}>
                 {i18n.t("docs")}
               </a>
             </li>
-            <li class="nav-item">
+            <li className="nav-item">
               <a className="nav-link" href={repoUrl}>
                 {i18n.t("code")}
               </a>
             </li>
-            <li class="nav-item">
+            <li className="nav-item">
               <a className="nav-link" href={joinLemmyUrl}>
                 {i18n.t("join_lemmy")}
               </a>
index 2408a76b84306fa76476d4e4a023090831bcafeb..345003533acae9cba7b2f9c88f38a39fade3a5a3 100644 (file)
@@ -144,8 +144,8 @@ export class Navbar extends Component<NavbarProps, NavbarState> {
   // TODO class active corresponding to current page
   navbar() {
     return (
-      <nav class="navbar navbar-expand-md navbar-light shadow-sm p-0 px-3">
-        <div class="container">
+      <nav className="navbar navbar-expand-md navbar-light shadow-sm p-0 px-3">
+        <div className="container">
           {this.props.siteRes.site_view.match({
             some: siteView => (
               <NavLink
@@ -166,7 +166,7 @@ export class Navbar extends Component<NavbarProps, NavbarState> {
           })}
           {UserService.Instance.myUserInfo.isSome() && (
             <>
-              <ul class="navbar-nav ml-auto">
+              <ul className="navbar-nav ml-auto">
                 <li className="nav-item">
                   <NavLink
                     to="/inbox"
@@ -179,7 +179,7 @@ export class Navbar extends Component<NavbarProps, NavbarState> {
                   >
                     <Icon icon="bell" />
                     {this.state.unreadInboxCount > 0 && (
-                      <span class="mx-1 badge badge-light">
+                      <span className="mx-1 badge badge-light">
                         {numToSI(this.state.unreadInboxCount)}
                       </span>
                     )}
@@ -187,7 +187,7 @@ export class Navbar extends Component<NavbarProps, NavbarState> {
                 </li>
               </ul>
               {this.moderatesSomething && (
-                <ul class="navbar-nav ml-1">
+                <ul className="navbar-nav ml-1">
                   <li className="nav-item">
                     <NavLink
                       to="/reports"
@@ -200,7 +200,7 @@ export class Navbar extends Component<NavbarProps, NavbarState> {
                     >
                       <Icon icon="shield" />
                       {this.state.unreadReportCount > 0 && (
-                        <span class="mx-1 badge badge-light">
+                        <span className="mx-1 badge badge-light">
                           {numToSI(this.state.unreadReportCount)}
                         </span>
                       )}
@@ -209,7 +209,7 @@ export class Navbar extends Component<NavbarProps, NavbarState> {
                 </ul>
               )}
               {this.amAdmin && (
-                <ul class="navbar-nav ml-1">
+                <ul className="navbar-nav ml-1">
                   <li className="nav-item">
                     <NavLink
                       to="/registration_applications"
@@ -224,7 +224,7 @@ export class Navbar extends Component<NavbarProps, NavbarState> {
                     >
                       <Icon icon="clipboard" />
                       {this.state.unreadApplicationCount > 0 && (
-                        <span class="mx-1 badge badge-light">
+                        <span className="mx-1 badge badge-light">
                           {numToSI(this.state.unreadApplicationCount)}
                         </span>
                       )}
@@ -235,7 +235,7 @@ export class Navbar extends Component<NavbarProps, NavbarState> {
             </>
           )}
           <button
-            class="navbar-toggler border-0 p-1"
+            className="navbar-toggler border-0 p-1"
             type="button"
             aria-label="menu"
             onClick={linkEvent(this, this.handleToggleExpandNavbar)}
@@ -246,8 +246,8 @@ export class Navbar extends Component<NavbarProps, NavbarState> {
           <div
             className={`${!this.state.expanded && "collapse"} navbar-collapse`}
           >
-            <ul class="navbar-nav my-2 mr-auto">
-              <li class="nav-item">
+            <ul className="navbar-nav my-2 mr-auto">
+              <li className="nav-item">
                 <NavLink
                   to="/communities"
                   className="nav-link"
@@ -257,11 +257,15 @@ export class Navbar extends Component<NavbarProps, NavbarState> {
                   {i18n.t("communities")}
                 </NavLink>
               </li>
-              <li class="nav-item">
+              <li className="nav-item">
+                {/* TODO make sure this works: https://github.com/infernojs/inferno/issues/1608 */}
                 <NavLink
                   to={{
                     pathname: "/create_post",
-                    prevPath: this.currentLocation,
+                    search: "",
+                    hash: "",
+                    key: "",
+                    state: { prevPath: this.currentLocation },
                   }}
                   className="nav-link"
                   onMouseUp={linkEvent(this, this.handleHideExpandNavbar)}
@@ -271,7 +275,7 @@ export class Navbar extends Component<NavbarProps, NavbarState> {
                 </NavLink>
               </li>
               {this.canCreateCommunity && (
-                <li class="nav-item">
+                <li className="nav-item">
                   <NavLink
                     to="/create_community"
                     className="nav-link"
@@ -282,7 +286,7 @@ export class Navbar extends Component<NavbarProps, NavbarState> {
                   </NavLink>
                 </li>
               )}
-              <li class="nav-item">
+              <li className="nav-item">
                 <a
                   className="nav-link"
                   title={i18n.t("support_lemmy")}
@@ -292,7 +296,7 @@ export class Navbar extends Component<NavbarProps, NavbarState> {
                 </a>
               </li>
             </ul>
-            <ul class="navbar-nav my-2">
+            <ul className="navbar-nav my-2">
               {this.amAdmin && (
                 <li className="nav-item">
                   <NavLink
@@ -310,12 +314,12 @@ export class Navbar extends Component<NavbarProps, NavbarState> {
               /^\/search/
             ) && (
               <form
-                class="form-inline mr-2"
+                className="form-inline mr-2"
                 onSubmit={linkEvent(this, this.handleSearchSubmit)}
               >
                 <input
                   id="search-input"
-                  class={`form-control mr-0 search-input ${
+                  className={`form-control mr-0 search-input ${
                     this.state.toggleSearch ? "show-input" : "hide-input"
                   }`}
                   onInput={linkEvent(this, this.handleSearchParam)}
@@ -325,13 +329,13 @@ export class Navbar extends Component<NavbarProps, NavbarState> {
                   placeholder={i18n.t("search")}
                   onBlur={linkEvent(this, this.handleSearchBlur)}
                 ></input>
-                <label class="sr-only" htmlFor="search-input">
+                <label className="sr-only" htmlFor="search-input">
                   {i18n.t("search")}
                 </label>
                 <button
                   name="search-btn"
                   onClick={linkEvent(this, this.handleSearchBtn)}
-                  class="px-1 btn btn-link"
+                  className="px-1 btn btn-link"
                   style="color: var(--gray)"
                   aria-label={i18n.t("search")}
                 >
@@ -341,7 +345,7 @@ export class Navbar extends Component<NavbarProps, NavbarState> {
             )}
             {UserService.Instance.myUserInfo.isSome() ? (
               <>
-                <ul class="navbar-nav my-2">
+                <ul className="navbar-nav my-2">
                   <li className="nav-item">
                     <NavLink
                       className="nav-link"
@@ -354,7 +358,7 @@ export class Navbar extends Component<NavbarProps, NavbarState> {
                     >
                       <Icon icon="bell" />
                       {this.state.unreadInboxCount > 0 && (
-                        <span class="ml-1 badge badge-light">
+                        <span className="ml-1 badge badge-light">
                           {numToSI(this.state.unreadInboxCount)}
                         </span>
                       )}
@@ -362,7 +366,7 @@ export class Navbar extends Component<NavbarProps, NavbarState> {
                   </li>
                 </ul>
                 {this.moderatesSomething && (
-                  <ul class="navbar-nav my-2">
+                  <ul className="navbar-nav my-2">
                     <li className="nav-item">
                       <NavLink
                         className="nav-link"
@@ -375,7 +379,7 @@ export class Navbar extends Component<NavbarProps, NavbarState> {
                       >
                         <Icon icon="shield" />
                         {this.state.unreadReportCount > 0 && (
-                          <span class="ml-1 badge badge-light">
+                          <span className="ml-1 badge badge-light">
                             {numToSI(this.state.unreadReportCount)}
                           </span>
                         )}
@@ -384,7 +388,7 @@ export class Navbar extends Component<NavbarProps, NavbarState> {
                   </ul>
                 )}
                 {this.amAdmin && (
-                  <ul class="navbar-nav my-2">
+                  <ul className="navbar-nav my-2">
                     <li className="nav-item">
                       <NavLink
                         to="/registration_applications"
@@ -399,7 +403,7 @@ export class Navbar extends Component<NavbarProps, NavbarState> {
                       >
                         <Icon icon="clipboard" />
                         {this.state.unreadApplicationCount > 0 && (
-                          <span class="mx-1 badge badge-light">
+                          <span className="mx-1 badge badge-light">
                             {numToSI(this.state.unreadApplicationCount)}
                           </span>
                         )}
@@ -411,10 +415,10 @@ export class Navbar extends Component<NavbarProps, NavbarState> {
                   .map(m => m.local_user_view.person)
                   .match({
                     some: person => (
-                      <ul class="navbar-nav">
-                        <li class="nav-item dropdown">
+                      <ul className="navbar-nav">
+                        <li className="nav-item dropdown">
                           <button
-                            class="nav-link btn btn-link dropdown-toggle"
+                            className="nav-link btn btn-link dropdown-toggle"
                             onClick={linkEvent(this, this.handleToggleDropdown)}
                             id="navbarDropdown"
                             role="button"
@@ -433,7 +437,7 @@ export class Navbar extends Component<NavbarProps, NavbarState> {
                           </button>
                           {this.state.showDropdown && (
                             <div
-                              class="dropdown-content"
+                              className="dropdown-content"
                               onMouseLeave={linkEvent(
                                 this,
                                 this.handleToggleDropdown
@@ -460,7 +464,7 @@ export class Navbar extends Component<NavbarProps, NavbarState> {
                                 </NavLink>
                               </li>
                               <li>
-                                <hr class="dropdown-divider" />
+                                <hr className="dropdown-divider" />
                               </li>
                               <li className="nav-item">
                                 <button
@@ -484,7 +488,7 @@ export class Navbar extends Component<NavbarProps, NavbarState> {
                   })}
               </>
             ) : (
-              <ul class="navbar-nav my-2">
+              <ul className="navbar-nav my-2">
                 <li className="nav-item">
                   <NavLink
                     to="/login"
@@ -532,8 +536,7 @@ export class Navbar extends Component<NavbarProps, NavbarState> {
   }
 
   handleToggleExpandNavbar(i: Navbar) {
-    i.state.expanded = !i.state.expanded;
-    i.setState(i.state);
+    i.setState({ expanded: !i.state.expanded });
   }
 
   handleHideExpandNavbar(i: Navbar) {
@@ -541,8 +544,7 @@ export class Navbar extends Component<NavbarProps, NavbarState> {
   }
 
   handleSearchParam(i: Navbar, event: any) {
-    i.state.searchParam = event.target.value;
-    i.setState(i.state);
+    i.setState({ searchParam: event.target.value });
   }
 
   handleSearchSubmit(i: Navbar, event: any) {
@@ -563,8 +565,7 @@ export class Navbar extends Component<NavbarProps, NavbarState> {
 
   handleSearchBlur(i: Navbar, event: any) {
     if (!(event.relatedTarget && event.relatedTarget.name !== "search-btn")) {
-      i.state.toggleSearch = false;
-      i.setState(i.state);
+      i.setState({ toggleSearch: false });
     }
   }
 
@@ -574,8 +575,7 @@ export class Navbar extends Component<NavbarProps, NavbarState> {
   }
 
   handleToggleDropdown(i: Navbar) {
-    i.state.showDropdown = !i.state.showDropdown;
-    i.setState(i.state);
+    i.setState({ showDropdown: !i.state.showDropdown });
   }
 
   parseMessage(msg: any) {
@@ -601,25 +601,25 @@ export class Navbar extends Component<NavbarProps, NavbarState> {
         msg,
         GetUnreadCountResponse
       );
-      this.state.unreadInboxCount =
-        data.replies + data.mentions + data.private_messages;
-      this.setState(this.state);
+      this.setState({
+        unreadInboxCount: data.replies + data.mentions + data.private_messages,
+      });
       this.sendUnreadCount();
     } else if (op == UserOperation.GetReportCount) {
       let data = wsJsonToRes<GetReportCountResponse>(
         msg,
         GetReportCountResponse
       );
-      this.state.unreadReportCount = data.post_reports + data.comment_reports;
-      this.setState(this.state);
+      this.setState({
+        unreadReportCount: data.post_reports + data.comment_reports,
+      });
       this.sendReportUnread();
     } else if (op == UserOperation.GetUnreadRegistrationApplicationCount) {
       let data = wsJsonToRes<GetUnreadRegistrationApplicationCountResponse>(
         msg,
         GetUnreadRegistrationApplicationCountResponse
       );
-      this.state.unreadApplicationCount = data.registration_applications;
-      this.setState(this.state);
+      this.setState({ unreadApplicationCount: data.registration_applications });
       this.sendApplicationUnread();
     } else if (op == UserOperation.CreateComment) {
       let data = wsJsonToRes<CommentResponse>(msg, CommentResponse);
@@ -627,8 +627,9 @@ export class Navbar extends Component<NavbarProps, NavbarState> {
       UserService.Instance.myUserInfo.match({
         some: mui => {
           if (data.recipient_ids.includes(mui.local_user_view.local_user.id)) {
-            this.state.unreadInboxCount++;
-            this.setState(this.state);
+            this.setState({
+              unreadInboxCount: this.state.unreadInboxCount + 1,
+            });
             this.sendUnreadCount();
             notifyComment(data.comment_view, this.context.router);
           }
@@ -647,8 +648,9 @@ export class Navbar extends Component<NavbarProps, NavbarState> {
             data.private_message_view.recipient.id ==
             mui.local_user_view.person.id
           ) {
-            this.state.unreadInboxCount++;
-            this.setState(this.state);
+            this.setState({
+              unreadInboxCount: this.state.unreadInboxCount + 1,
+            });
             this.sendUnreadCount();
             notifyPrivateMessage(
               data.private_message_view,
index ca3fe64f0bcf889e828f2a3dc8625646136b97e5..45ba65e99879177436cac141660b585d5406a86d 100644 (file)
@@ -13,7 +13,7 @@ export class NoMatch extends Component<any, any> {
 
   render() {
     return (
-      <div class="container">
+      <div className="container">
         <h1>404</h1>
         {this.errCode && (
           <h3>
index 6778b68fc16fb562a0c3eabedb6d90262c8fb5d5..d91e86c17c69b771bf492f4e412aae24ee88ef7a 100644 (file)
@@ -75,7 +75,7 @@ export class CommentForm extends Component<CommentFormProps, CommentFormState> {
       right: () => None,
     });
     return (
-      <div class="mb-3">
+      <div className="mb-3">
         {UserService.Instance.myUserInfo.isSome() ? (
           <MarkdownTextArea
             initialContent={initialContent}
@@ -90,7 +90,7 @@ export class CommentForm extends Component<CommentFormProps, CommentFormState> {
             placeholder={Some(i18n.t("comment_here"))}
           />
         ) : (
-          <div class="alert alert-warning" role="alert">
+          <div className="alert alert-warning" role="alert">
             <Icon icon="alert-triangle" classes="icon-inline mr-2" />
             <T i18nKey="must_login" class="d-inline">
               #
@@ -106,7 +106,7 @@ export class CommentForm extends Component<CommentFormProps, CommentFormState> {
 
   handleCommentSubmit(msg: { val: string; formId: string }) {
     let content = msg.val;
-    this.state.formId = Some(msg.formId);
+    this.setState({ formId: Some(msg.formId) });
 
     this.props.node.match({
       left: node => {
index f654b65a2a35aef80a3f12356caa93a539c9d776..f5fc69813d0c6b31d5c280fc16be4b339b39029d 100644 (file)
@@ -147,13 +147,14 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
   // TODO see if there's a better way to do this, and all willReceiveProps
   componentWillReceiveProps(nextProps: CommentNodeProps) {
     let cv = nextProps.node.comment_view;
-    this.state.my_vote = cv.my_vote;
-    this.state.upvotes = cv.counts.upvotes;
-    this.state.downvotes = cv.counts.downvotes;
-    this.state.score = cv.counts.score;
-    this.state.readLoading = false;
-    this.state.saveLoading = false;
-    this.setState(this.state);
+    this.setState({
+      my_vote: cv.my_vote,
+      upvotes: cv.counts.upvotes,
+      downvotes: cv.counts.downvotes,
+      score: cv.counts.score,
+      readLoading: false,
+      saveLoading: false,
+    });
   }
 
   render() {
@@ -227,10 +228,12 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
           }
         >
           <div
-            class={`${!this.props.noIndent && this.props.node.depth && "ml-2"}`}
+            className={`${
+              !this.props.noIndent && this.props.node.depth && "ml-2"
+            }`}
           >
-            <div class="d-flex flex-wrap align-items-center text-muted small">
-              <span class="mr-2">
+            <div className="d-flex flex-wrap align-items-center text-muted small">
+              <span className="mr-2">
                 <PersonListing person={cv.creator} />
               </span>
               {cv.comment.distinguished && (
@@ -263,16 +266,16 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
               )}
               {this.props.showCommunity && (
                 <>
-                  <span class="mx-1">{i18n.t("to")}</span>
+                  <span className="mx-1">{i18n.t("to")}</span>
                   <CommunityLink community={cv.community} />
-                  <span class="mx-2">•</span>
+                  <span className="mx-2">•</span>
                   <Link className="mr-2" to={`/post/${cv.post.id}`}>
                     {cv.post.name}
                   </Link>
                 </>
               )}
               <button
-                class="btn btn-sm text-muted"
+                className="btn btn-sm text-muted"
                 onClick={linkEvent(this, this.handleCommentCollapse)}
                 aria-label={this.expandText}
                 data-tippy-content={this.expandText}
@@ -294,7 +297,7 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
                     data-tippy-content={this.pointsTippy}
                   >
                     <span
-                      class="mr-1 font-weight-bold"
+                      className="mr-1 font-weight-bold"
                       aria-label={i18n.t("number_of_points", {
                         count: this.state.score,
                         formattedCount: this.state.score,
@@ -335,11 +338,11 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
                     )}
                   />
                 )}
-                <div class="d-flex justify-content-between justify-content-lg-start flex-wrap text-muted font-weight-bold">
+                <div className="d-flex justify-content-between justify-content-lg-start flex-wrap text-muted font-weight-bold">
                   {this.props.showContext && this.linkBtn()}
                   {this.props.markable && (
                     <button
-                      class="btn btn-link btn-animate text-muted"
+                      className="btn btn-link btn-animate text-muted"
                       onClick={linkEvent(this, this.handleMarkRead)}
                       data-tippy-content={
                         this.commentReplyOrMentionRead
@@ -380,7 +383,7 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
                           <Icon icon="arrow-up1" classes="icon-inline" />
                           {showScores() &&
                             this.state.upvotes !== this.state.score && (
-                              <span class="ml-1">
+                              <span className="ml-1">
                                 {numToSI(this.state.upvotes)}
                               </span>
                             )}
@@ -399,14 +402,14 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
                             <Icon icon="arrow-down1" classes="icon-inline" />
                             {showScores() &&
                               this.state.upvotes !== this.state.score && (
-                                <span class="ml-1">
+                                <span className="ml-1">
                                   {numToSI(this.state.downvotes)}
                                 </span>
                               )}
                           </button>
                         )}
                         <button
-                          class="btn btn-link btn-animate text-muted"
+                          className="btn btn-link btn-animate text-muted"
                           onClick={linkEvent(this, this.handleReplyClick)}
                           data-tippy-content={i18n.t("reply")}
                           aria-label={i18n.t("reply")}
@@ -426,7 +429,7 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
                           <>
                             {!this.myComment && (
                               <>
-                                <button class="btn btn-link btn-animate">
+                                <button className="btn btn-link btn-animate">
                                   <Link
                                     className="text-muted"
                                     to={`/create_private_message/recipient/${cv.creator.id}`}
@@ -436,7 +439,7 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
                                   </Link>
                                 </button>
                                 <button
-                                  class="btn btn-link btn-animate text-muted"
+                                  className="btn btn-link btn-animate text-muted"
                                   onClick={linkEvent(
                                     this,
                                     this.handleShowReportDialog
@@ -449,7 +452,7 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
                                   <Icon icon="flag" />
                                 </button>
                                 <button
-                                  class="btn btn-link btn-animate text-muted"
+                                  className="btn btn-link btn-animate text-muted"
                                   onClick={linkEvent(
                                     this,
                                     this.handleBlockUserClick
@@ -462,7 +465,7 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
                               </>
                             )}
                             <button
-                              class="btn btn-link btn-animate text-muted"
+                              className="btn btn-link btn-animate text-muted"
                               onClick={linkEvent(
                                 this,
                                 this.handleSaveCommentClick
@@ -501,7 +504,7 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
                             {this.myComment && (
                               <>
                                 <button
-                                  class="btn btn-link btn-animate text-muted"
+                                  className="btn btn-link btn-animate text-muted"
                                   onClick={linkEvent(
                                     this,
                                     this.handleEditClick
@@ -512,7 +515,7 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
                                   <Icon icon="edit" classes="icon-inline" />
                                 </button>
                                 <button
-                                  class="btn btn-link btn-animate text-muted"
+                                  className="btn btn-link btn-animate text-muted"
                                   onClick={linkEvent(
                                     this,
                                     this.handleDeleteClick
@@ -538,7 +541,7 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
 
                                 {(canModOnSelf || canAdminOnSelf) && (
                                   <button
-                                    class="btn btn-link btn-animate text-muted"
+                                    className="btn btn-link btn-animate text-muted"
                                     onClick={linkEvent(
                                       this,
                                       this.handleDistinguishClick
@@ -570,7 +573,7 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
                               <>
                                 {!cv.comment.removed ? (
                                   <button
-                                    class="btn btn-link btn-animate text-muted"
+                                    className="btn btn-link btn-animate text-muted"
                                     onClick={linkEvent(
                                       this,
                                       this.handleModRemoveShow
@@ -581,7 +584,7 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
                                   </button>
                                 ) : (
                                   <button
-                                    class="btn btn-link btn-animate text-muted"
+                                    className="btn btn-link btn-animate text-muted"
                                     onClick={linkEvent(
                                       this,
                                       this.handleModRemoveSubmit
@@ -599,7 +602,7 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
                                 {!isMod_ &&
                                   (!cv.creator_banned_from_community ? (
                                     <button
-                                      class="btn btn-link btn-animate text-muted"
+                                      className="btn btn-link btn-animate text-muted"
                                       onClick={linkEvent(
                                         this,
                                         this.handleModBanFromCommunityShow
@@ -610,7 +613,7 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
                                     </button>
                                   ) : (
                                     <button
-                                      class="btn btn-link btn-animate text-muted"
+                                      className="btn btn-link btn-animate text-muted"
                                       onClick={linkEvent(
                                         this,
                                         this.handleModBanFromCommunitySubmit
@@ -623,7 +626,7 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
                                 {!cv.creator_banned_from_community &&
                                   (!this.state.showConfirmAppointAsMod ? (
                                     <button
-                                      class="btn btn-link btn-animate text-muted"
+                                      className="btn btn-link btn-animate text-muted"
                                       onClick={linkEvent(
                                         this,
                                         this.handleShowConfirmAppointAsMod
@@ -641,13 +644,13 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
                                   ) : (
                                     <>
                                       <button
-                                        class="btn btn-link btn-animate text-muted"
+                                        className="btn btn-link btn-animate text-muted"
                                         aria-label={i18n.t("are_you_sure")}
                                       >
                                         {i18n.t("are_you_sure")}
                                       </button>
                                       <button
-                                        class="btn btn-link btn-animate text-muted"
+                                        className="btn btn-link btn-animate text-muted"
                                         onClick={linkEvent(
                                           this,
                                           this.handleAddModToCommunity
@@ -657,7 +660,7 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
                                         {i18n.t("yes")}
                                       </button>
                                       <button
-                                        class="btn btn-link btn-animate text-muted"
+                                        className="btn btn-link btn-animate text-muted"
                                         onClick={linkEvent(
                                           this,
                                           this.handleCancelConfirmAppointAsMod
@@ -676,7 +679,7 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
                               cv.creator.local &&
                               (!this.state.showConfirmTransferCommunity ? (
                                 <button
-                                  class="btn btn-link btn-animate text-muted"
+                                  className="btn btn-link btn-animate text-muted"
                                   onClick={linkEvent(
                                     this,
                                     this.handleShowConfirmTransferCommunity
@@ -688,13 +691,13 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
                               ) : (
                                 <>
                                   <button
-                                    class="btn btn-link btn-animate text-muted"
+                                    className="btn btn-link btn-animate text-muted"
                                     aria-label={i18n.t("are_you_sure")}
                                   >
                                     {i18n.t("are_you_sure")}
                                   </button>
                                   <button
-                                    class="btn btn-link btn-animate text-muted"
+                                    className="btn btn-link btn-animate text-muted"
                                     onClick={linkEvent(
                                       this,
                                       this.handleTransferCommunity
@@ -704,7 +707,7 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
                                     {i18n.t("yes")}
                                   </button>
                                   <button
-                                    class="btn btn-link btn-animate text-muted"
+                                    className="btn btn-link btn-animate text-muted"
                                     onClick={linkEvent(
                                       this,
                                       this
@@ -722,7 +725,7 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
                                 {!isAdmin_ && (
                                   <>
                                     <button
-                                      class="btn btn-link btn-animate text-muted"
+                                      className="btn btn-link btn-animate text-muted"
                                       onClick={linkEvent(
                                         this,
                                         this.handlePurgePersonShow
@@ -732,7 +735,7 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
                                       {i18n.t("purge_user")}
                                     </button>
                                     <button
-                                      class="btn btn-link btn-animate text-muted"
+                                      className="btn btn-link btn-animate text-muted"
                                       onClick={linkEvent(
                                         this,
                                         this.handlePurgeCommentShow
@@ -744,7 +747,7 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
 
                                     {!isBanned(cv.creator) ? (
                                       <button
-                                        class="btn btn-link btn-animate text-muted"
+                                        className="btn btn-link btn-animate text-muted"
                                         onClick={linkEvent(
                                           this,
                                           this.handleModBanShow
@@ -755,7 +758,7 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
                                       </button>
                                     ) : (
                                       <button
-                                        class="btn btn-link btn-animate text-muted"
+                                        className="btn btn-link btn-animate text-muted"
                                         onClick={linkEvent(
                                           this,
                                           this.handleModBanSubmit
@@ -771,7 +774,7 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
                                   cv.creator.local &&
                                   (!this.state.showConfirmAppointAsAdmin ? (
                                     <button
-                                      class="btn btn-link btn-animate text-muted"
+                                      className="btn btn-link btn-animate text-muted"
                                       onClick={linkEvent(
                                         this,
                                         this.handleShowConfirmAppointAsAdmin
@@ -788,11 +791,11 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
                                     </button>
                                   ) : (
                                     <>
-                                      <button class="btn btn-link btn-animate text-muted">
+                                      <button className="btn btn-link btn-animate text-muted">
                                         {i18n.t("are_you_sure")}
                                       </button>
                                       <button
-                                        class="btn btn-link btn-animate text-muted"
+                                        className="btn btn-link btn-animate text-muted"
                                         onClick={linkEvent(
                                           this,
                                           this.handleAddAdmin
@@ -802,7 +805,7 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
                                         {i18n.t("yes")}
                                       </button>
                                       <button
-                                        class="btn btn-link btn-animate text-muted"
+                                        className="btn btn-link btn-animate text-muted"
                                         onClick={linkEvent(
                                           this,
                                           this.handleCancelConfirmAppointAsAdmin
@@ -833,7 +836,7 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
             style={`border-left: 2px ${moreRepliesBorderColor} solid !important`}
           >
             <button
-              class="btn btn-link text-muted"
+              className="btn btn-link text-muted"
               onClick={linkEvent(this, this.handleFetchChildren)}
             >
               {i18n.t("x_more_replies", {
@@ -847,11 +850,11 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
         {/* end of details */}
         {this.state.showRemoveDialog && (
           <form
-            class="form-inline"
+            className="form-inline"
             onSubmit={linkEvent(this, this.handleModRemoveSubmit)}
           >
             <label
-              class="sr-only"
+              className="sr-only"
               htmlFor={`mod-remove-reason-${cv.comment.id}`}
             >
               {i18n.t("reason")}
@@ -859,14 +862,14 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
             <input
               type="text"
               id={`mod-remove-reason-${cv.comment.id}`}
-              class="form-control mr-2"
+              className="form-control mr-2"
               placeholder={i18n.t("reason")}
               value={toUndefined(this.state.removeReason)}
               onInput={linkEvent(this, this.handleModRemoveReasonChange)}
             />
             <button
               type="submit"
-              class="btn btn-secondary"
+              className="btn btn-secondary"
               aria-label={i18n.t("remove_comment")}
             >
               {i18n.t("remove_comment")}
@@ -875,24 +878,27 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
         )}
         {this.state.showReportDialog && (
           <form
-            class="form-inline"
+            className="form-inline"
             onSubmit={linkEvent(this, this.handleReportSubmit)}
           >
-            <label class="sr-only" htmlFor={`report-reason-${cv.comment.id}`}>
+            <label
+              className="sr-only"
+              htmlFor={`report-reason-${cv.comment.id}`}
+            >
               {i18n.t("reason")}
             </label>
             <input
               type="text"
               required
               id={`report-reason-${cv.comment.id}`}
-              class="form-control mr-2"
+              className="form-control mr-2"
               placeholder={i18n.t("reason")}
               value={this.state.reportReason}
               onInput={linkEvent(this, this.handleReportReasonChange)}
             />
             <button
               type="submit"
-              class="btn btn-secondary"
+              className="btn btn-secondary"
               aria-label={i18n.t("create_report")}
             >
               {i18n.t("create_report")}
@@ -901,9 +907,9 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
         )}
         {this.state.showBanDialog && (
           <form onSubmit={linkEvent(this, this.handleModBanBothSubmit)}>
-            <div class="form-group row col-12">
+            <div className="form-group row col-12">
               <label
-                class="col-form-label"
+                className="col-form-label"
                 htmlFor={`mod-ban-reason-${cv.comment.id}`}
               >
                 {i18n.t("reason")}
@@ -911,13 +917,13 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
               <input
                 type="text"
                 id={`mod-ban-reason-${cv.comment.id}`}
-                class="form-control mr-2"
+                className="form-control mr-2"
                 placeholder={i18n.t("reason")}
                 value={toUndefined(this.state.banReason)}
                 onInput={linkEvent(this, this.handleModBanReasonChange)}
               />
               <label
-                class="col-form-label"
+                className="col-form-label"
                 htmlFor={`mod-ban-expires-${cv.comment.id}`}
               >
                 {i18n.t("expires")}
@@ -925,22 +931,22 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
               <input
                 type="number"
                 id={`mod-ban-expires-${cv.comment.id}`}
-                class="form-control mr-2"
+                className="form-control mr-2"
                 placeholder={i18n.t("number_of_days")}
                 value={toUndefined(this.state.banExpireDays)}
                 onInput={linkEvent(this, this.handleModBanExpireDaysChange)}
               />
-              <div class="form-group">
-                <div class="form-check">
+              <div className="form-group">
+                <div className="form-check">
                   <input
-                    class="form-check-input"
+                    className="form-check-input"
                     id="mod-ban-remove-data"
                     type="checkbox"
                     checked={this.state.removeData}
                     onChange={linkEvent(this, this.handleModRemoveDataChange)}
                   />
                   <label
-                    class="form-check-label"
+                    className="form-check-label"
                     htmlFor="mod-ban-remove-data"
                     title={i18n.t("remove_content_more")}
                   >
@@ -954,10 +960,10 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
             {/*   <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 class="form-group row">
+            <div className="form-group row">
               <button
                 type="submit"
-                class="btn btn-secondary"
+                className="btn btn-secondary"
                 aria-label={i18n.t("ban")}
               >
                 {i18n.t("ban")} {cv.creator.name}
@@ -969,24 +975,24 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
         {this.state.showPurgeDialog && (
           <form onSubmit={linkEvent(this, this.handlePurgeSubmit)}>
             <PurgeWarning />
-            <label class="sr-only" htmlFor="purge-reason">
+            <label className="sr-only" htmlFor="purge-reason">
               {i18n.t("reason")}
             </label>
             <input
               type="text"
               id="purge-reason"
-              class="form-control my-3"
+              className="form-control my-3"
               placeholder={i18n.t("reason")}
               value={toUndefined(this.state.purgeReason)}
               onInput={linkEvent(this, this.handlePurgeReasonChange)}
             />
-            <div class="form-group row col-12">
+            <div className="form-group row col-12">
               {this.state.purgeLoading ? (
                 <Spinner />
               ) : (
                 <button
                   type="submit"
-                  class="btn btn-secondary"
+                  className="btn btn-secondary"
                   aria-label={purgeTypeText}
                 >
                   {purgeTypeText}
@@ -1015,7 +1021,7 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
           />
         )}
         {/* A collapsed clearfix */}
-        {this.state.collapsed && <div class="row col-12"></div>}
+        {this.state.collapsed && <div className="row col-12"></div>}
       </div>
     );
   }
@@ -1090,13 +1096,11 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
   }
 
   handleReplyClick(i: CommentNode) {
-    i.state.showReply = true;
-    i.setState(i.state);
+    i.setState({ showReply: true });
   }
 
   handleEditClick(i: CommentNode) {
-    i.state.showEdit = true;
-    i.setState(i.state);
+    i.setState({ showEdit: true });
   }
 
   handleBlockUserClick(i: CommentNode) {
@@ -1129,14 +1133,11 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
 
     WebSocketService.Instance.send(wsClient.saveComment(form));
 
-    i.state.saveLoading = true;
-    i.setState(this.state);
+    i.setState({ saveLoading: true });
   }
 
   handleReplyCancel() {
-    this.state.showReply = false;
-    this.state.showEdit = false;
-    this.setState(this.state);
+    this.setState({ showReply: false, showEdit: false });
   }
 
   handleCommentUpvote(event: any) {
@@ -1145,18 +1146,24 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
     let newVote = myVote == 1 ? 0 : 1;
 
     if (myVote == 1) {
-      this.state.score--;
-      this.state.upvotes--;
+      this.setState({
+        score: this.state.score - 1,
+        upvotes: this.state.upvotes - 1,
+      });
     } else if (myVote == -1) {
-      this.state.downvotes--;
-      this.state.upvotes++;
-      this.state.score += 2;
+      this.setState({
+        downvotes: this.state.downvotes - 1,
+        upvotes: this.state.upvotes + 1,
+        score: this.state.score + 2,
+      });
     } else {
-      this.state.upvotes++;
-      this.state.score++;
+      this.setState({
+        score: this.state.score + 1,
+        upvotes: this.state.upvotes + 1,
+      });
     }
 
-    this.state.my_vote = Some(newVote);
+    this.setState({ my_vote: Some(newVote) });
 
     let form = new CreateCommentLike({
       comment_id: this.props.node.comment_view.comment.id,
@@ -1164,7 +1171,6 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
       auth: auth().unwrap(),
     });
     WebSocketService.Instance.send(wsClient.likeComment(form));
-    this.setState(this.state);
     setupTippy();
   }
 
@@ -1174,18 +1180,24 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
     let newVote = myVote == -1 ? 0 : -1;
 
     if (myVote == 1) {
-      this.state.score -= 2;
-      this.state.upvotes--;
-      this.state.downvotes++;
+      this.setState({
+        downvotes: this.state.downvotes + 1,
+        upvotes: this.state.upvotes - 1,
+        score: this.state.score - 2,
+      });
     } else if (myVote == -1) {
-      this.state.downvotes--;
-      this.state.score++;
+      this.setState({
+        downvotes: this.state.downvotes - 1,
+        score: this.state.score + 1,
+      });
     } else {
-      this.state.downvotes++;
-      this.state.score--;
+      this.setState({
+        downvotes: this.state.downvotes + 1,
+        score: this.state.score - 1,
+      });
     }
 
-    this.state.my_vote = Some(newVote);
+    this.setState({ my_vote: Some(newVote) });
 
     let form = new CreateCommentLike({
       comment_id: this.props.node.comment_view.comment.id,
@@ -1194,18 +1206,15 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
     });
 
     WebSocketService.Instance.send(wsClient.likeComment(form));
-    this.setState(this.state);
     setupTippy();
   }
 
   handleShowReportDialog(i: CommentNode) {
-    i.state.showReportDialog = !i.state.showReportDialog;
-    i.setState(i.state);
+    i.setState({ showReportDialog: !i.state.showReportDialog });
   }
 
   handleReportReasonChange(i: CommentNode, event: any) {
-    i.state.reportReason = event.target.value;
-    i.setState(i.state);
+    i.setState({ reportReason: event.target.value });
   }
 
   handleReportSubmit(i: CommentNode) {
@@ -1217,24 +1226,22 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
     });
     WebSocketService.Instance.send(wsClient.createCommentReport(form));
 
-    i.state.showReportDialog = false;
-    i.setState(i.state);
+    i.setState({ showReportDialog: false });
   }
 
   handleModRemoveShow(i: CommentNode) {
-    i.state.showRemoveDialog = !i.state.showRemoveDialog;
-    i.state.showBanDialog = false;
-    i.setState(i.state);
+    i.setState({
+      showRemoveDialog: !i.state.showRemoveDialog,
+      showBanDialog: false,
+    });
   }
 
   handleModRemoveReasonChange(i: CommentNode, event: any) {
-    i.state.removeReason = Some(event.target.value);
-    i.setState(i.state);
+    i.setState({ removeReason: Some(event.target.value) });
   }
 
   handleModRemoveDataChange(i: CommentNode, event: any) {
-    i.state.removeData = event.target.checked;
-    i.setState(i.state);
+    i.setState({ removeData: event.target.checked });
   }
 
   handleModRemoveSubmit(i: CommentNode) {
@@ -1247,8 +1254,7 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
     });
     WebSocketService.Instance.send(wsClient.removeComment(form));
 
-    i.state.showRemoveDialog = false;
-    i.setState(i.state);
+    i.setState({ showRemoveDialog: false });
   }
 
   handleDistinguishClick(i: CommentNode) {
@@ -1293,43 +1299,40 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
       WebSocketService.Instance.send(wsClient.markCommentReplyAsRead(form));
     }
 
-    i.state.readLoading = true;
-    i.setState(this.state);
+    i.setState({ readLoading: true });
   }
 
   handleModBanFromCommunityShow(i: CommentNode) {
-    i.state.showBanDialog = true;
-    i.state.banType = BanType.Community;
-    i.state.showRemoveDialog = false;
-    i.setState(i.state);
+    i.setState({
+      showBanDialog: true,
+      banType: BanType.Community,
+      showRemoveDialog: false,
+    });
   }
 
   handleModBanShow(i: CommentNode) {
-    i.state.showBanDialog = true;
-    i.state.banType = BanType.Site;
-    i.state.showRemoveDialog = false;
-    i.setState(i.state);
+    i.setState({
+      showBanDialog: true,
+      banType: BanType.Site,
+      showRemoveDialog: false,
+    });
   }
 
   handleModBanReasonChange(i: CommentNode, event: any) {
-    i.state.banReason = Some(event.target.value);
-    i.setState(i.state);
+    i.setState({ banReason: Some(event.target.value) });
   }
 
   handleModBanExpireDaysChange(i: CommentNode, event: any) {
-    i.state.banExpireDays = Some(event.target.value);
-    i.setState(i.state);
+    i.setState({ banExpireDays: Some(event.target.value) });
   }
 
   handleModBanFromCommunitySubmit(i: CommentNode) {
-    i.state.banType = BanType.Community;
-    i.setState(i.state);
+    i.setState({ banType: BanType.Community });
     i.handleModBanBothSubmit(i);
   }
 
   handleModBanSubmit(i: CommentNode) {
-    i.state.banType = BanType.Site;
-    i.setState(i.state);
+    i.setState({ banType: BanType.Site });
     i.handleModBanBothSubmit(i);
   }
 
@@ -1340,7 +1343,7 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
       // If its an unban, restore all their data
       let ban = !cv.creator_banned_from_community;
       if (ban == false) {
-        i.state.removeData = false;
+        i.setState({ removeData: false });
       }
       let form = new BanFromCommunity({
         person_id: cv.creator.id,
@@ -1356,7 +1359,7 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
       // If its an unban, restore all their data
       let ban = !cv.creator.banned;
       if (ban == false) {
-        i.state.removeData = false;
+        i.setState({ removeData: false });
       }
       let form = new BanPerson({
         person_id: cv.creator.id,
@@ -1369,27 +1372,27 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
       WebSocketService.Instance.send(wsClient.banPerson(form));
     }
 
-    i.state.showBanDialog = false;
-    i.setState(i.state);
+    i.setState({ showBanDialog: false });
   }
 
   handlePurgePersonShow(i: CommentNode) {
-    i.state.showPurgeDialog = true;
-    i.state.purgeType = PurgeType.Person;
-    i.state.showRemoveDialog = false;
-    i.setState(i.state);
+    i.setState({
+      showPurgeDialog: true,
+      purgeType: PurgeType.Person,
+      showRemoveDialog: false,
+    });
   }
 
   handlePurgeCommentShow(i: CommentNode) {
-    i.state.showPurgeDialog = true;
-    i.state.purgeType = PurgeType.Comment;
-    i.state.showRemoveDialog = false;
-    i.setState(i.state);
+    i.setState({
+      showPurgeDialog: true,
+      purgeType: PurgeType.Comment,
+      showRemoveDialog: false,
+    });
   }
 
   handlePurgeReasonChange(i: CommentNode, event: any) {
-    i.state.purgeReason = Some(event.target.value);
-    i.setState(i.state);
+    i.setState({ purgeReason: Some(event.target.value) });
   }
 
   handlePurgeSubmit(i: CommentNode, event: any) {
@@ -1411,18 +1414,15 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
       WebSocketService.Instance.send(wsClient.purgeComment(form));
     }
 
-    i.state.purgeLoading = true;
-    i.setState(i.state);
+    i.setState({ purgeLoading: true });
   }
 
   handleShowConfirmAppointAsMod(i: CommentNode) {
-    i.state.showConfirmAppointAsMod = true;
-    i.setState(i.state);
+    i.setState({ showConfirmAppointAsMod: true });
   }
 
   handleCancelConfirmAppointAsMod(i: CommentNode) {
-    i.state.showConfirmAppointAsMod = false;
-    i.setState(i.state);
+    i.setState({ showConfirmAppointAsMod: false });
   }
 
   handleAddModToCommunity(i: CommentNode) {
@@ -1434,18 +1434,15 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
       auth: auth().unwrap(),
     });
     WebSocketService.Instance.send(wsClient.addModToCommunity(form));
-    i.state.showConfirmAppointAsMod = false;
-    i.setState(i.state);
+    i.setState({ showConfirmAppointAsMod: false });
   }
 
   handleShowConfirmAppointAsAdmin(i: CommentNode) {
-    i.state.showConfirmAppointAsAdmin = true;
-    i.setState(i.state);
+    i.setState({ showConfirmAppointAsAdmin: true });
   }
 
   handleCancelConfirmAppointAsAdmin(i: CommentNode) {
-    i.state.showConfirmAppointAsAdmin = false;
-    i.setState(i.state);
+    i.setState({ showConfirmAppointAsAdmin: false });
   }
 
   handleAddAdmin(i: CommentNode) {
@@ -1456,18 +1453,15 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
       auth: auth().unwrap(),
     });
     WebSocketService.Instance.send(wsClient.addAdmin(form));
-    i.state.showConfirmAppointAsAdmin = false;
-    i.setState(i.state);
+    i.setState({ showConfirmAppointAsAdmin: false });
   }
 
   handleShowConfirmTransferCommunity(i: CommentNode) {
-    i.state.showConfirmTransferCommunity = true;
-    i.setState(i.state);
+    i.setState({ showConfirmTransferCommunity: true });
   }
 
   handleCancelShowConfirmTransferCommunity(i: CommentNode) {
-    i.state.showConfirmTransferCommunity = false;
-    i.setState(i.state);
+    i.setState({ showConfirmTransferCommunity: false });
   }
 
   handleTransferCommunity(i: CommentNode) {
@@ -1478,18 +1472,15 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
       auth: auth().unwrap(),
     });
     WebSocketService.Instance.send(wsClient.transferCommunity(form));
-    i.state.showConfirmTransferCommunity = false;
-    i.setState(i.state);
+    i.setState({ showConfirmTransferCommunity: false });
   }
 
   handleShowConfirmTransferSite(i: CommentNode) {
-    i.state.showConfirmTransferSite = true;
-    i.setState(i.state);
+    i.setState({ showConfirmTransferSite: true });
   }
 
   handleCancelShowConfirmTransferSite(i: CommentNode) {
-    i.state.showConfirmTransferSite = false;
-    i.setState(i.state);
+    i.setState({ showConfirmTransferSite: false });
   }
 
   get isCommentNew(): boolean {
@@ -1499,19 +1490,16 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
   }
 
   handleCommentCollapse(i: CommentNode) {
-    i.state.collapsed = !i.state.collapsed;
-    i.setState(i.state);
+    i.setState({ collapsed: !i.state.collapsed });
     setupTippy();
   }
 
   handleViewSource(i: CommentNode) {
-    i.state.viewSource = !i.state.viewSource;
-    i.setState(i.state);
+    i.setState({ viewSource: !i.state.viewSource });
   }
 
   handleShowAdvanced(i: CommentNode) {
-    i.state.showAdvanced = !i.state.showAdvanced;
-    i.setState(i.state);
+    i.setState({ showAdvanced: !i.state.showAdvanced });
     setupTippy();
   }
 
index d3383b5f6f83cf5bc399d4e67be5153f3c85bb1d..a72400a3dc824cd075ea39607d1bbb663a6350da 100644 (file)
@@ -14,7 +14,7 @@ export class BannerIconHeader extends Component<BannerIconHeaderProps, any> {
 
   render() {
     return (
-      <div class="position-relative mb-2">
+      <div className="position-relative mb-2">
         {this.props.banner.match({
           some: banner => <PictrsImage src={banner} banner alt="" />,
           none: <></>,
index b87f26664c4a8859855f7bfccd7be5a954303ac4..0e49d561ed9f0c631ff0c3bfdc2f8af6a5a418f2 100644 (file)
@@ -41,7 +41,7 @@ export class CommentSortSelect extends Component<
           name={this.id}
           value={this.state.sort}
           onChange={linkEvent(this, this.handleSortChange)}
-          class="custom-select w-auto mr-2 mb-2"
+          className="custom-select w-auto mr-2 mb-2"
           aria-label={i18n.t("sort_type")}
         >
           <option disabled aria-hidden="true">
index b9b6a6f580c07a04cac295e7f93c9203c39e9162..8a5613e85863553de3830bcc076c92060566fc5b 100644 (file)
@@ -32,7 +32,7 @@ export class DataTypeSelect extends Component<
 
   render() {
     return (
-      <div class="btn-group btn-group-toggle flex-wrap mb-2">
+      <div className="btn-group btn-group-toggle flex-wrap mb-2">
         <label
           className={`pointer btn btn-outline-secondary 
             ${this.state.type_ == DataType.Post && "active"}
index 1997b4f8cee16e62e191f15558d0444c262d4761..56cf3238ed0abf23407c449efd27bdda03655eae 100644 (file)
@@ -19,10 +19,10 @@ export class HtmlTags extends Component<HtmlTagsProps, any> {
     return (
       <Helmet title={this.props.title}>
         {["title", "og:title", "twitter:title"].map(t => (
-          <meta property={t} content={this.props.title} />
+          <meta key={t} property={t} content={this.props.title} />
         ))}
         {["og:url", "twitter:url"].map(u => (
-          <meta property={u} content={url} />
+          <meta key={u} property={u} content={url} />
         ))}
 
         {/* Open Graph / Facebook */}
@@ -35,6 +35,7 @@ export class HtmlTags extends Component<HtmlTagsProps, any> {
         {this.props.description.isSome() &&
           ["description", "og:description", "twitter:description"].map(n => (
             <meta
+              key={n}
               name={n}
               content={md.renderInline(this.props.description.unwrap())}
             />
@@ -42,7 +43,7 @@ export class HtmlTags extends Component<HtmlTagsProps, any> {
 
         {this.props.image.isSome() &&
           ["og:image", "twitter:image"].map(p => (
-            <meta property={p} content={this.props.image.unwrap()} />
+            <meta key={p} property={p} content={this.props.image.unwrap()} />
           ))}
       </Helmet>
     );
index 91271cfe539e45b889f38768ad5223c516851eae..f922e175e88cd4051b1e6625ee12d1da7d5bfdac 100644 (file)
@@ -17,13 +17,13 @@ export class Icon extends Component<IconProps, any> {
   render() {
     return (
       <svg
-        class={classNames("icon", this.props.classes, {
+        className={classNames("icon", this.props.classes, {
           "icon-inline": this.props.inline,
           small: this.props.small,
         })}
       >
         <use xlinkHref={`#icon-${this.props.icon}`}></use>
-        <div class="sr-only">
+        <div className="sr-only">
           <title>{this.props.icon}</title>
         </div>
       </svg>
@@ -57,7 +57,7 @@ export class PurgeWarning extends Component<any, any> {
 
   render() {
     return (
-      <div class="mt-2 alert alert-danger" role="alert">
+      <div className="mt-2 alert alert-danger" role="alert">
         <Icon icon="alert-triangle" classes="icon-inline mr-2" />
         {i18n.t("purge_warning")}
       </div>
index 5f7a8168f437518a01c78dfaa354ccb4d7547c29..432521efa26fac5f849e444ab6985b876d172ab2 100644 (file)
@@ -34,14 +34,14 @@ export class ImageUploadForm extends Component<
 
   render() {
     return (
-      <form class="d-inline">
+      <form className="d-inline">
         <label
           htmlFor={this.id}
-          class="pointer text-muted small font-weight-bold"
+          className="pointer text-muted small font-weight-bold"
         >
           {this.props.imageSrc.match({
             some: imageSrc => (
-              <span class="d-inline-block position-relative">
+              <span className="d-inline-block position-relative">
                 <img
                   src={imageSrc}
                   height={this.props.rounded ? 60 : ""}
@@ -59,7 +59,9 @@ export class ImageUploadForm extends Component<
               </span>
             ),
             none: (
-              <span class="btn btn-secondary">{this.props.uploadTitle}</span>
+              <span className="btn btn-secondary">
+                {this.props.uploadTitle}
+              </span>
             ),
           })}
         </label>
@@ -68,7 +70,7 @@ export class ImageUploadForm extends Component<
           type="file"
           accept="image/*,video/*"
           name={this.id}
-          class="d-none"
+          className="d-none"
           disabled={UserService.Instance.myUserInfo.isNone()}
           onChange={linkEvent(this, this.handleImageUpload)}
         />
@@ -82,8 +84,7 @@ export class ImageUploadForm extends Component<
     const formData = new FormData();
     formData.append("images[]", file);
 
-    i.state.loading = true;
-    i.setState(i.state);
+    i.setState({ loading: true });
 
     fetch(pictrsUri, {
       method: "POST",
@@ -96,18 +97,15 @@ export class ImageUploadForm extends Component<
         if (res.msg == "ok") {
           let hash = res.files[0].file;
           let url = `${pictrsUri}/${hash}`;
-          i.state.loading = false;
-          i.setState(i.state);
+          i.setState({ loading: false });
           i.props.onUpload(url);
         } else {
-          i.state.loading = false;
-          i.setState(i.state);
+          i.setState({ loading: false });
           toast(JSON.stringify(res), "danger");
         }
       })
       .catch(error => {
-        i.state.loading = false;
-        i.setState(i.state);
+        i.setState({ loading: false });
         console.error(error);
         toast(error, "danger");
       });
@@ -115,8 +113,7 @@ export class ImageUploadForm extends Component<
 
   handleRemoveImage(i: ImageUploadForm, event: any) {
     event.preventDefault();
-    i.state.loading = true;
-    i.setState(i.state);
+    i.setState({ loading: true });
     i.props.onRemove();
   }
 }
index 0ddd0e9abfca57ce1f2301ac092958be9c55c38d..38e6acddc639ee6f79b97d3e995cc330d5c5ad16 100644 (file)
@@ -40,7 +40,7 @@ export class ListingTypeSelect extends Component<
 
   render() {
     return (
-      <div class="btn-group btn-group-toggle flex-wrap mb-2">
+      <div className="btn-group btn-group-toggle flex-wrap mb-2">
         {this.props.showSubscribed && (
           <label
             title={i18n.t("subscribed_description")}
index e369f8fbb41ba842917bcb12dd5b12855cc4e735..b3374847feaa0ee1d7060f356124791b4e33f08f 100644 (file)
@@ -70,8 +70,7 @@ export class MarkdownTextArea extends Component<
       autosize(textarea);
       this.tribute.attach(textarea);
       textarea.addEventListener("tribute-replaced", () => {
-        this.state.content = Some(textarea.value);
-        this.setState(this.state);
+        this.setState({ content: Some(textarea.value) });
         autosize.update(textarea);
       });
 
@@ -96,10 +95,7 @@ export class MarkdownTextArea extends Component<
 
   componentWillReceiveProps(nextProps: MarkdownTextAreaProps) {
     if (nextProps.finished) {
-      this.state.previewMode = false;
-      this.state.loading = false;
-      this.state.content = None;
-      this.setState(this.state);
+      this.setState({ previewMode: false, loading: false, content: None });
       if (this.props.replyType) {
         this.props.onReplyCancel();
       }
@@ -108,7 +104,6 @@ export class MarkdownTextArea extends Component<
       let form: any = document.getElementById(this.formId);
       form.reset();
       setTimeout(() => autosize.update(textarea), 10);
-      this.setState(this.state);
     }
   }
 
@@ -125,7 +120,7 @@ export class MarkdownTextArea extends Component<
           }
           message={i18n.t("block_leaving")}
         />
-        <div class="form-group row">
+        <div className="form-group row">
           <div className={`col-sm-12`}>
             <textarea
               id={this.id}
@@ -150,17 +145,17 @@ export class MarkdownTextArea extends Component<
                 none: <></>,
               })}
           </div>
-          <label class="sr-only" htmlFor={this.id}>
+          <label className="sr-only" htmlFor={this.id}>
             {i18n.t("body")}
           </label>
         </div>
-        <div class="row">
-          <div class="col-sm-12 d-flex flex-wrap">
+        <div className="row">
+          <div className="col-sm-12 d-flex flex-wrap">
             {this.props.buttonTitle.match({
               some: buttonTitle => (
                 <button
                   type="submit"
-                  class="btn btn-sm btn-secondary mr-2"
+                  className="btn btn-sm btn-secondary mr-2"
                   disabled={this.props.disabled || this.state.loading}
                 >
                   {this.state.loading ? (
@@ -175,7 +170,7 @@ export class MarkdownTextArea extends Component<
             {this.props.replyType && (
               <button
                 type="button"
-                class="btn btn-sm btn-secondary mr-2"
+                className="btn btn-sm btn-secondary mr-2"
                 onClick={linkEvent(this, this.handleReplyCancel)}
               >
                 {i18n.t("cancel")}
@@ -192,9 +187,9 @@ export class MarkdownTextArea extends Component<
               </button>
             )}
             {/* A flex expander */}
-            <div class="flex-grow-1"></div>
+            <div className="flex-grow-1"></div>
             <button
-              class="btn btn-sm text-muted"
+              className="btn btn-sm text-muted"
               data-tippy-content={i18n.t("bold")}
               aria-label={i18n.t("bold")}
               onClick={linkEvent(this, this.handleInsertBold)}
@@ -202,7 +197,7 @@ export class MarkdownTextArea extends Component<
               <Icon icon="bold" classes="icon-inline" />
             </button>
             <button
-              class="btn btn-sm text-muted"
+              className="btn btn-sm text-muted"
               data-tippy-content={i18n.t("italic")}
               aria-label={i18n.t("italic")}
               onClick={linkEvent(this, this.handleInsertItalic)}
@@ -210,14 +205,14 @@ export class MarkdownTextArea extends Component<
               <Icon icon="italic" classes="icon-inline" />
             </button>
             <button
-              class="btn btn-sm text-muted"
+              className="btn btn-sm text-muted"
               data-tippy-content={i18n.t("link")}
               aria-label={i18n.t("link")}
               onClick={linkEvent(this, this.handleInsertLink)}
             >
               <Icon icon="link" classes="icon-inline" />
             </button>
-            <form class="btn btn-sm text-muted font-weight-bold">
+            <form className="btn btn-sm text-muted font-weight-bold">
               <label
                 htmlFor={`file-upload-${this.id}`}
                 className={`mb-0 ${
@@ -236,13 +231,13 @@ export class MarkdownTextArea extends Component<
                 type="file"
                 accept="image/*,video/*"
                 name="file"
-                class="d-none"
+                className="d-none"
                 disabled={UserService.Instance.myUserInfo.isNone()}
                 onChange={linkEvent(this, this.handleImageUpload)}
               />
             </form>
             <button
-              class="btn btn-sm text-muted"
+              className="btn btn-sm text-muted"
               data-tippy-content={i18n.t("header")}
               aria-label={i18n.t("header")}
               onClick={linkEvent(this, this.handleInsertHeader)}
@@ -250,7 +245,7 @@ export class MarkdownTextArea extends Component<
               <Icon icon="header" classes="icon-inline" />
             </button>
             <button
-              class="btn btn-sm text-muted"
+              className="btn btn-sm text-muted"
               data-tippy-content={i18n.t("strikethrough")}
               aria-label={i18n.t("strikethrough")}
               onClick={linkEvent(this, this.handleInsertStrikethrough)}
@@ -258,7 +253,7 @@ export class MarkdownTextArea extends Component<
               <Icon icon="strikethrough" classes="icon-inline" />
             </button>
             <button
-              class="btn btn-sm text-muted"
+              className="btn btn-sm text-muted"
               data-tippy-content={i18n.t("quote")}
               aria-label={i18n.t("quote")}
               onClick={linkEvent(this, this.handleInsertQuote)}
@@ -266,7 +261,7 @@ export class MarkdownTextArea extends Component<
               <Icon icon="format_quote" classes="icon-inline" />
             </button>
             <button
-              class="btn btn-sm text-muted"
+              className="btn btn-sm text-muted"
               data-tippy-content={i18n.t("list")}
               aria-label={i18n.t("list")}
               onClick={linkEvent(this, this.handleInsertList)}
@@ -274,7 +269,7 @@ export class MarkdownTextArea extends Component<
               <Icon icon="list" classes="icon-inline" />
             </button>
             <button
-              class="btn btn-sm text-muted"
+              className="btn btn-sm text-muted"
               data-tippy-content={i18n.t("code")}
               aria-label={i18n.t("code")}
               onClick={linkEvent(this, this.handleInsertCode)}
@@ -282,7 +277,7 @@ export class MarkdownTextArea extends Component<
               <Icon icon="code" classes="icon-inline" />
             </button>
             <button
-              class="btn btn-sm text-muted"
+              className="btn btn-sm text-muted"
               data-tippy-content={i18n.t("subscript")}
               aria-label={i18n.t("subscript")}
               onClick={linkEvent(this, this.handleInsertSubscript)}
@@ -290,7 +285,7 @@ export class MarkdownTextArea extends Component<
               <Icon icon="subscript" classes="icon-inline" />
             </button>
             <button
-              class="btn btn-sm text-muted"
+              className="btn btn-sm text-muted"
               data-tippy-content={i18n.t("superscript")}
               aria-label={i18n.t("superscript")}
               onClick={linkEvent(this, this.handleInsertSuperscript)}
@@ -298,7 +293,7 @@ export class MarkdownTextArea extends Component<
               <Icon icon="superscript" classes="icon-inline" />
             </button>
             <button
-              class="btn btn-sm text-muted"
+              className="btn btn-sm text-muted"
               data-tippy-content={i18n.t("spoiler")}
               aria-label={i18n.t("spoiler")}
               onClick={linkEvent(this, this.handleInsertSpoiler)}
@@ -307,7 +302,7 @@ export class MarkdownTextArea extends Component<
             </button>
             <a
               href={markdownHelpUrl}
-              class="btn btn-sm text-muted font-weight-bold"
+              className="btn btn-sm text-muted font-weight-bold"
               title={i18n.t("formatting_help")}
               rel={relTags}
             >
@@ -338,8 +333,7 @@ export class MarkdownTextArea extends Component<
     const formData = new FormData();
     formData.append("images[]", file);
 
-    i.state.imageLoading = true;
-    i.setState(i.state);
+    i.setState({ imageLoading: true });
 
     fetch(pictrsUri, {
       method: "POST",
@@ -355,15 +349,16 @@ export class MarkdownTextArea extends Component<
           let deleteToken = res.files[0].delete_token;
           let deleteUrl = `${pictrsUri}/delete/${deleteToken}/${hash}`;
           let imageMarkdown = `![](${url})`;
-          i.state.content = Some(
-            i.state.content.match({
-              some: content => `${content}\n${imageMarkdown}`,
-              none: imageMarkdown,
-            })
-          );
-          i.state.imageLoading = false;
+          i.setState({
+            content: Some(
+              i.state.content.match({
+                some: content => `${content}\n${imageMarkdown}`,
+                none: imageMarkdown,
+              })
+            ),
+            imageLoading: false,
+          });
           i.contentChange();
-          i.setState(i.state);
           let textarea: any = document.getElementById(i.id);
           autosize.update(textarea);
           pictrsDeleteToast(
@@ -372,14 +367,12 @@ export class MarkdownTextArea extends Component<
             deleteUrl
           );
         } else {
-          i.state.imageLoading = false;
-          i.setState(i.state);
+          i.setState({ imageLoading: false });
           toast(JSON.stringify(res), "danger");
         }
       })
       .catch(error => {
-        i.state.imageLoading = false;
-        i.setState(i.state);
+        i.setState({ imageLoading: false });
         console.error(error);
         toast(error, "danger");
       });
@@ -392,21 +385,18 @@ export class MarkdownTextArea extends Component<
   }
 
   handleContentChange(i: MarkdownTextArea, event: any) {
-    i.state.content = Some(event.target.value);
+    i.setState({ content: Some(event.target.value) });
     i.contentChange();
-    i.setState(i.state);
   }
 
   handlePreviewToggle(i: MarkdownTextArea, event: any) {
     event.preventDefault();
-    i.state.previewMode = !i.state.previewMode;
-    i.setState(i.state);
+    i.setState({ previewMode: !i.state.previewMode });
   }
 
   handleSubmit(i: MarkdownTextArea, event: any) {
     event.preventDefault();
-    i.state.loading = true;
-    i.setState(i.state);
+    i.setState({ loading: true });
     let msg = { val: toUndefined(i.state.content), formId: i.formId };
     i.props.onSubmit(msg);
   }
@@ -423,27 +413,28 @@ export class MarkdownTextArea extends Component<
     let end: number = textarea.selectionEnd;
 
     if (i.state.content.isNone()) {
-      i.state.content = Some("");
+      i.setState({ content: Some("") });
     }
 
     let content = i.state.content.unwrap();
 
     if (start !== end) {
       let selectedText = content.substring(start, end);
-      i.state.content = Some(
-        `${content.substring(0, start)}[${selectedText}]()${content.substring(
-          end
-        )}`
-      );
+      i.setState({
+        content: Some(
+          `${content.substring(0, start)}[${selectedText}]()${content.substring(
+            end
+          )}`
+        ),
+      });
       textarea.focus();
       setTimeout(() => (textarea.selectionEnd = end + 3), 10);
     } else {
-      i.state.content = Some(`${content} []()`);
+      i.setState({ content: Some(`${content} []()`) });
       textarea.focus();
       setTimeout(() => (textarea.selectionEnd -= 1), 10);
     }
     i.contentChange();
-    i.setState(i.state);
   }
 
   simpleSurround(chars: string) {
@@ -460,7 +451,7 @@ export class MarkdownTextArea extends Component<
     emptyChars = "___"
   ) {
     if (this.state.content.isNone()) {
-      this.state.content = Some("");
+      this.setState({ content: Some("") });
     }
     let textarea: any = document.getElementById(this.id);
     let start: number = textarea.selectionStart;
@@ -470,19 +461,20 @@ export class MarkdownTextArea extends Component<
 
     if (start !== end) {
       let selectedText = content.substring(start, end);
-      this.state.content = Some(
-        `${content.substring(
-          0,
-          start
-        )}${beforeChars}${selectedText}${afterChars}${content.substring(end)}`
-      );
+      this.setState({
+        content: Some(
+          `${content.substring(
+            0,
+            start
+          )}${beforeChars}${selectedText}${afterChars}${content.substring(end)}`
+        ),
+      });
     } else {
-      this.state.content = Some(
-        `${content}${beforeChars}${emptyChars}${afterChars}`
-      );
+      this.setState({
+        content: Some(`${content}${beforeChars}${emptyChars}${afterChars}`),
+      });
     }
     this.contentChange();
-    this.setState(this.state);
 
     textarea.focus();
 
@@ -554,9 +546,11 @@ export class MarkdownTextArea extends Component<
 
   simpleInsert(chars: string) {
     if (this.state.content.isNone()) {
-      this.state.content = Some(`${chars} `);
+      this.setState({ content: Some(`${chars} `) });
     } else {
-      this.state.content = Some(`${this.state.content.unwrap()}\n${chars} `);
+      this.setState({
+        content: Some(`${this.state.content.unwrap()}\n${chars} `),
+      });
     }
 
     let textarea: any = document.getElementById(this.id);
@@ -565,7 +559,6 @@ export class MarkdownTextArea extends Component<
       autosize.update(textarea);
     }, 10);
     this.contentChange();
-    this.setState(this.state);
   }
 
   handleInsertSpoiler(i: MarkdownTextArea, event: any) {
@@ -585,13 +578,14 @@ export class MarkdownTextArea extends Component<
           .map(t => `> ${t}`)
           .join("\n") + "\n\n";
       if (this.state.content.isNone()) {
-        this.state.content = Some("");
+        this.setState({ content: Some("") });
       } else {
-        this.state.content = Some(`${this.state.content.unwrap()}\n`);
+        this.setState({ content: Some(`${this.state.content.unwrap()}\n`) });
       }
-      this.state.content = Some(`${this.state.content.unwrap()}${quotedText}`);
+      this.setState({
+        content: Some(`${this.state.content.unwrap()}${quotedText}`),
+      });
       this.contentChange();
-      this.setState(this.state);
       // Not sure why this needs a delay
       setTimeout(() => autosize.update(textarea), 10);
     }
index 50094de48b839293667686106ad9f4ac32731fbd..75acde3e105ce59cc5a3510ee1f432392e269d6c 100644 (file)
@@ -12,16 +12,16 @@ export class Paginator extends Component<PaginatorProps, any> {
   }
   render() {
     return (
-      <div class="my-2">
+      <div className="my-2">
         <button
-          class="btn btn-secondary mr-2"
+          className="btn btn-secondary mr-2"
           disabled={this.props.page == 1}
           onClick={linkEvent(this, this.handlePrev)}
         >
           {i18n.t("prev")}
         </button>
         <button
-          class="btn btn-secondary"
+          className="btn btn-secondary"
           onClick={linkEvent(this, this.handleNext)}
         >
           {i18n.t("next")}
index f3c2cf523ddc142b414c6e2a05e8eb26ab49c478..cac317eef1e49e5554f99a8eb16f34a6d4e0e1c7 100644 (file)
@@ -88,11 +88,11 @@ export class RegistrationApplication extends Component<
         })}
 
         {this.state.denyExpanded && (
-          <div class="form-group row">
-            <label class="col-sm-2 col-form-label">
+          <div className="form-group row">
+            <label className="col-sm-2 col-form-label">
               {i18n.t("deny_reason")}
             </label>
-            <div class="col-sm-10">
+            <div className="col-sm-10">
               <MarkdownTextArea
                 initialContent={this.state.denyReason}
                 onContentChange={this.handleDenyReasonChange}
@@ -157,7 +157,6 @@ export class RegistrationApplication extends Component<
   }
 
   handleDenyReasonChange(val: string) {
-    this.state.denyReason = Some(val);
-    this.setState(this.state);
+    this.setState({ denyReason: Some(val) });
   }
 }
index 3f815f5b0eb6e728ff9fea9ad01544fd59fd4505..facc16f47bebf79d1be04e3653ebaf1613be65c8 100644 (file)
@@ -40,23 +40,27 @@ export class SortSelect extends Component<SortSelectProps, SortSelectState> {
           name={this.id}
           value={this.state.sort}
           onChange={linkEvent(this, this.handleSortChange)}
-          class="custom-select w-auto mr-2 mb-2"
+          className="custom-select w-auto mr-2 mb-2"
           aria-label={i18n.t("sort_type")}
         >
           <option disabled aria-hidden="true">
             {i18n.t("sort_type")}
           </option>
           {!this.props.hideHot && [
-            <option value={SortType.Hot}>{i18n.t("hot")}</option>,
-            <option value={SortType.Active}>{i18n.t("active")}</option>,
+            <option key={SortType.Hot} value={SortType.Hot}>
+              {i18n.t("hot")}
+            </option>,
+            <option key={SortType.Active} value={SortType.Active}>
+              {i18n.t("active")}
+            </option>,
           ]}
           <option value={SortType.New}>{i18n.t("new")}</option>
           <option value={SortType.Old}>{i18n.t("old")}</option>
           {!this.props.hideMostComments && [
-            <option value={SortType.MostComments}>
+            <option key={SortType.MostComments} value={SortType.MostComments}>
               {i18n.t("most_comments")}
             </option>,
-            <option value={SortType.NewComments}>
+            <option key={SortType.NewComments} value={SortType.NewComments}>
               {i18n.t("new_comments")}
             </option>,
           ]}
index 03791d1bec58cc9977f1e49890236f37a1b65f65..cb066cc2e3d4cf48f46bba1a4d40ceece156b2fa 100644 (file)
@@ -17,7 +17,7 @@ export const SYMBOLS = (
         viewBox="0 0 196.52 196.52"
         xmlns="http://www.w3.org/2000/svg"
       >
-        <g color="#000" font-weight="400" font-family="sans-serif">
+        <g color="#000" fontWeight="400" fontFamily="sans-serif">
           <path
             d="M47.924 72.797a18.228 18.228 0 0 1-7.796 7.76l42.799 42.965 10.318-5.23zm56.453 56.67l-10.319 5.23 21.686 21.77a18.228 18.228 0 0 1 7.798-7.76z"
             overflow="visible"
@@ -69,7 +69,7 @@ export const SYMBOLS = (
             fill="#dbb210"
           />
         </g>
-        <g transform="rotate(3.118 600.365 106.46)" fill-opacity=".996">
+        <g transform="rotate(3.118 600.365 106.46)" fillOpacity=".996">
           <circle cx="106.266" cy="51.536" r="16.571" fill="#ffca00" />
           <circle cx="171.428" cy="110.193" r="16.571" fill="#64ff00" />
           <circle cx="135.764" cy="190.277" r="16.571" fill="#00a3ff" />
index db17084a3e56ebd0e74f8cb41055d234124e358a..95e593efb7d5aac44462b2f42ce8c518b0870df3 100644 (file)
@@ -75,8 +75,11 @@ 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);
-      this.state.listCommunitiesResponse = listRes;
-      this.state.loading = false;
+      this.state = {
+        ...this.state,
+        listCommunitiesResponse: listRes,
+        loading: false,
+      };
     } else {
       this.refetch();
     }
@@ -114,7 +117,7 @@ export class Communities extends Component<any, CommunitiesState> {
 
   render() {
     return (
-      <div class="container">
+      <div className="container">
         <HtmlTags
           title={this.documentTitle}
           path={this.context.router.route.match.url}
@@ -127,10 +130,10 @@ export class Communities extends Component<any, CommunitiesState> {
           </h5>
         ) : (
           <div>
-            <div class="row">
-              <div class="col-md-6">
+            <div className="row">
+              <div className="col-md-6">
                 <h4>{i18n.t("list_of_communities")}</h4>
-                <span class="mb-2">
+                <span className="mb-2">
                   <ListingTypeSelect
                     type_={this.state.listingType}
                     showLocal={showLocal(this.isoData)}
@@ -139,24 +142,27 @@ export class Communities extends Component<any, CommunitiesState> {
                   />
                 </span>
               </div>
-              <div class="col-md-6">
-                <div class="float-md-right">{this.searchForm()}</div>
+              <div className="col-md-6">
+                <div className="float-md-right">{this.searchForm()}</div>
               </div>
             </div>
 
-            <div class="table-responsive">
-              <table id="community_table" class="table table-sm table-hover">
-                <thead class="pointer">
+            <div className="table-responsive">
+              <table
+                id="community_table"
+                className="table table-sm table-hover"
+              >
+                <thead className="pointer">
                   <tr>
                     <th>{i18n.t("name")}</th>
-                    <th class="text-right">{i18n.t("subscribers")}</th>
-                    <th class="text-right">
+                    <th className="text-right">{i18n.t("subscribers")}</th>
+                    <th className="text-right">
                       {i18n.t("users")} / {i18n.t("month")}
                     </th>
-                    <th class="text-right d-none d-lg-table-cell">
+                    <th className="text-right d-none d-lg-table-cell">
                       {i18n.t("posts")}
                     </th>
-                    <th class="text-right d-none d-lg-table-cell">
+                    <th className="text-right d-none d-lg-table-cell">
                       {i18n.t("comments")}
                     </th>
                     <th></th>
@@ -167,26 +173,26 @@ export class Communities extends Component<any, CommunitiesState> {
                     .map(l => l.communities)
                     .unwrapOr([])
                     .map(cv => (
-                      <tr>
+                      <tr key={cv.community.id}>
                         <td>
                           <CommunityLink community={cv.community} />
                         </td>
-                        <td class="text-right">
+                        <td className="text-right">
                           {numToSI(cv.counts.subscribers)}
                         </td>
-                        <td class="text-right">
+                        <td className="text-right">
                           {numToSI(cv.counts.users_active_month)}
                         </td>
-                        <td class="text-right d-none d-lg-table-cell">
+                        <td className="text-right d-none d-lg-table-cell">
                           {numToSI(cv.counts.posts)}
                         </td>
-                        <td class="text-right d-none d-lg-table-cell">
+                        <td className="text-right d-none d-lg-table-cell">
                           {numToSI(cv.counts.comments)}
                         </td>
-                        <td class="text-right">
+                        <td className="text-right">
                           {cv.subscribed == SubscribedType.Subscribed && (
                             <button
-                              class="btn btn-link d-inline-block"
+                              className="btn btn-link d-inline-block"
                               onClick={linkEvent(
                                 cv.community.id,
                                 this.handleUnsubscribe
@@ -197,7 +203,7 @@ export class Communities extends Component<any, CommunitiesState> {
                           )}
                           {cv.subscribed == SubscribedType.NotSubscribed && (
                             <button
-                              class="btn btn-link d-inline-block"
+                              className="btn btn-link d-inline-block"
                               onClick={linkEvent(
                                 cv.community.id,
                                 this.handleSubscribe
@@ -207,7 +213,7 @@ export class Communities extends Component<any, CommunitiesState> {
                             </button>
                           )}
                           {cv.subscribed == SubscribedType.Pending && (
-                            <div class="text-warning d-inline-block">
+                            <div className="text-warning d-inline-block">
                               {i18n.t("subscribe_pending")}
                             </div>
                           )}
@@ -230,23 +236,23 @@ export class Communities extends Component<any, CommunitiesState> {
   searchForm() {
     return (
       <form
-        class="form-inline"
+        className="form-inline"
         onSubmit={linkEvent(this, this.handleSearchSubmit)}
       >
         <input
           type="text"
           id="communities-search"
-          class="form-control mr-2 mb-2"
+          className="form-control mr-2 mb-2"
           value={this.state.searchText}
           placeholder={`${i18n.t("search")}...`}
           onInput={linkEvent(this, this.handleSearchChange)}
           required
           minLength={3}
         />
-        <label class="sr-only" htmlFor="communities-search">
+        <label className="sr-only" htmlFor="communities-search">
           {i18n.t("search")}
         </label>
-        <button type="submit" class="btn btn-secondary mr-2 mb-2">
+        <button type="submit" className="btn btn-secondary mr-2 mb-2">
           <span>{i18n.t("search")}</span>
         </button>
       </form>
@@ -343,10 +349,8 @@ export class Communities extends Component<any, CommunitiesState> {
         msg,
         ListCommunitiesResponse
       );
-      this.state.listCommunitiesResponse = Some(data);
-      this.state.loading = false;
+      this.setState({ listCommunitiesResponse: Some(data), loading: false });
       window.scrollTo(0, 0);
-      this.setState(this.state);
     } else if (op == UserOperation.FollowCommunity) {
       let data = wsJsonToRes<CommunityResponse>(msg, CommunityResponse);
       this.state.listCommunitiesResponse.match({
index cce144bf07b2de1b2f264cfe73c90af626d19a73..8f70f36f0f74d7d6eb85708e17dba3d83d317481 100644 (file)
@@ -73,9 +73,14 @@ export class CommunityForm extends Component<
     this.handleBannerUpload = this.handleBannerUpload.bind(this);
     this.handleBannerRemove = this.handleBannerRemove.bind(this);
 
-    this.props.community_view.match({
-      some: cv => {
-        this.state.communityForm = new CreateCommunity({
+    this.parseMessage = this.parseMessage.bind(this);
+    this.subscription = wsSubscribe(this.parseMessage);
+
+    if (this.props.community_view.isSome()) {
+      let cv = this.props.community_view.unwrap();
+      this.state = {
+        ...this.state,
+        communityForm: new CreateCommunity({
           name: cv.community.name,
           title: cv.community.title,
           description: cv.community.description,
@@ -86,13 +91,9 @@ export class CommunityForm extends Component<
             cv.community.posting_restricted_to_mods
           ),
           auth: undefined,
-        });
-      },
-      none: void 0,
-    });
-
-    this.parseMessage = this.parseMessage.bind(this);
-    this.subscription = wsSubscribe(this.parseMessage);
+        }),
+      };
+    }
   }
 
   componentDidUpdate() {
@@ -127,24 +128,24 @@ export class CommunityForm extends Component<
         />
         <form onSubmit={linkEvent(this, this.handleCreateCommunitySubmit)}>
           {this.props.community_view.isNone() && (
-            <div class="form-group row">
+            <div className="form-group row">
               <label
-                class="col-12 col-sm-2 col-form-label"
+                className="col-12 col-sm-2 col-form-label"
                 htmlFor="community-name"
               >
                 {i18n.t("name")}
                 <span
-                  class="position-absolute pointer unselectable ml-2 text-muted"
+                  className="position-absolute pointer unselectable ml-2 text-muted"
                   data-tippy-content={i18n.t("name_explain")}
                 >
                   <Icon icon="help-circle" classes="icon-inline" />
                 </span>
               </label>
-              <div class="col-12 col-sm-10">
+              <div className="col-12 col-sm-10">
                 <input
                   type="text"
                   id="community-name"
-                  class="form-control"
+                  className="form-control"
                   value={this.state.communityForm.name}
                   onInput={linkEvent(this, this.handleCommunityNameChange)}
                   required
@@ -155,35 +156,35 @@ export class CommunityForm extends Component<
               </div>
             </div>
           )}
-          <div class="form-group row">
+          <div className="form-group row">
             <label
-              class="col-12 col-sm-2 col-form-label"
+              className="col-12 col-sm-2 col-form-label"
               htmlFor="community-title"
             >
               {i18n.t("display_name")}
               <span
-                class="position-absolute pointer unselectable ml-2 text-muted"
+                className="position-absolute pointer unselectable ml-2 text-muted"
                 data-tippy-content={i18n.t("display_name_explain")}
               >
                 <Icon icon="help-circle" classes="icon-inline" />
               </span>
             </label>
-            <div class="col-12 col-sm-10">
+            <div className="col-12 col-sm-10">
               <input
                 type="text"
                 id="community-title"
                 value={this.state.communityForm.title}
                 onInput={linkEvent(this, this.handleCommunityTitleChange)}
-                class="form-control"
+                className="form-control"
                 required
                 minLength={3}
                 maxLength={100}
               />
             </div>
           </div>
-          <div class="form-group row">
-            <label class="col-12 col-sm-2">{i18n.t("icon")}</label>
-            <div class="col-12 col-sm-10">
+          <div className="form-group row">
+            <label className="col-12 col-sm-2">{i18n.t("icon")}</label>
+            <div className="col-12 col-sm-10">
               <ImageUploadForm
                 uploadTitle={i18n.t("upload_icon")}
                 imageSrc={this.state.communityForm.icon}
@@ -193,9 +194,9 @@ export class CommunityForm extends Component<
               />
             </div>
           </div>
-          <div class="form-group row">
-            <label class="col-12 col-sm-2">{i18n.t("banner")}</label>
-            <div class="col-12 col-sm-10">
+          <div className="form-group row">
+            <label className="col-12 col-sm-2">{i18n.t("banner")}</label>
+            <div className="col-12 col-sm-10">
               <ImageUploadForm
                 uploadTitle={i18n.t("upload_banner")}
                 imageSrc={this.state.communityForm.banner}
@@ -204,11 +205,11 @@ export class CommunityForm extends Component<
               />
             </div>
           </div>
-          <div class="form-group row">
-            <label class="col-12 col-sm-2 col-form-label" htmlFor={this.id}>
+          <div className="form-group row">
+            <label className="col-12 col-sm-2 col-form-label" htmlFor={this.id}>
               {i18n.t("sidebar")}
             </label>
-            <div class="col-12 col-sm-10">
+            <div className="col-12 col-sm-10">
               <MarkdownTextArea
                 initialContent={this.state.communityForm.description}
                 placeholder={Some("description")}
@@ -220,14 +221,14 @@ export class CommunityForm extends Component<
           </div>
 
           {this.props.enableNsfw && (
-            <div class="form-group row">
-              <legend class="col-form-label col-sm-2 pt-0">
+            <div className="form-group row">
+              <legend className="col-form-label col-sm-2 pt-0">
                 {i18n.t("nsfw")}
               </legend>
-              <div class="col-10">
-                <div class="form-check">
+              <div className="col-10">
+                <div className="form-check">
                   <input
-                    class="form-check-input position-static"
+                    className="form-check-input position-static"
                     id="community-nsfw"
                     type="checkbox"
                     checked={toUndefined(this.state.communityForm.nsfw)}
@@ -237,14 +238,14 @@ export class CommunityForm extends Component<
               </div>
             </div>
           )}
-          <div class="form-group row">
-            <legend class="col-form-label col-6 pt-0">
+          <div className="form-group row">
+            <legend className="col-form-label col-6 pt-0">
               {i18n.t("only_mods_can_post_in_community")}
             </legend>
-            <div class="col-6">
-              <div class="form-check">
+            <div className="col-6">
+              <div className="form-check">
                 <input
-                  class="form-check-input position-static"
+                  className="form-check-input position-static"
                   id="community-only-mods-can-post"
                   type="checkbox"
                   checked={toUndefined(
@@ -258,11 +259,11 @@ export class CommunityForm extends Component<
               </div>
             </div>
           </div>
-          <div class="form-group row">
-            <div class="col-12">
+          <div className="form-group row">
+            <div className="col-12">
               <button
                 type="submit"
-                class="btn btn-secondary mr-2"
+                className="btn btn-secondary mr-2"
                 disabled={this.state.loading}
               >
                 {this.state.loading ? (
@@ -276,7 +277,7 @@ export class CommunityForm extends Component<
               {this.props.community_view.isSome() && (
                 <button
                   type="button"
-                  class="btn btn-secondary"
+                  className="btn btn-secondary"
                   onClick={linkEvent(this, this.handleCancel)}
                 >
                   {i18n.t("cancel")}
@@ -291,7 +292,7 @@ export class CommunityForm extends Component<
 
   handleCreateCommunitySubmit(i: CommunityForm, event: any) {
     event.preventDefault();
-    i.state.loading = true;
+    i.setState({ loading: true });
     let cForm = i.state.communityForm;
     cForm.auth = auth().unwrap();
 
@@ -330,17 +331,18 @@ export class CommunityForm extends Component<
   }
 
   handleCommunityDescriptionChange(val: string) {
-    this.state.communityForm.description = Some(val);
-    this.setState(this.state);
+    this.setState(s => ((s.communityForm.description = Some(val)), s));
   }
 
   handleCommunityNsfwChange(i: CommunityForm, event: any) {
-    i.state.communityForm.nsfw = event.target.checked;
+    i.state.communityForm.nsfw = Some(event.target.checked);
     i.setState(i.state);
   }
 
   handleCommunityPostingRestrictedToMods(i: CommunityForm, event: any) {
-    i.state.communityForm.posting_restricted_to_mods = event.target.checked;
+    i.state.communityForm.posting_restricted_to_mods = Some(
+      event.target.checked
+    );
     i.setState(i.state);
   }
 
@@ -349,23 +351,19 @@ export class CommunityForm extends Component<
   }
 
   handleIconUpload(url: string) {
-    this.state.communityForm.icon = Some(url);
-    this.setState(this.state);
+    this.setState(s => ((s.communityForm.icon = Some(url)), s));
   }
 
   handleIconRemove() {
-    this.state.communityForm.icon = Some("");
-    this.setState(this.state);
+    this.setState(s => ((s.communityForm.icon = Some("")), s));
   }
 
   handleBannerUpload(url: string) {
-    this.state.communityForm.banner = Some(url);
-    this.setState(this.state);
+    this.setState(s => ((s.communityForm.banner = Some(url)), s));
   }
 
   handleBannerRemove() {
-    this.state.communityForm.banner = Some("");
-    this.setState(this.state);
+    this.setState(s => ((s.communityForm.banner = Some("")), s));
   }
 
   parseMessage(msg: any) {
@@ -374,12 +372,10 @@ export class CommunityForm extends Component<
     if (msg.error) {
       // Errors handled by top level pages
       // toast(i18n.t(msg.error), "danger");
-      this.state.loading = false;
-      this.setState(this.state);
+      this.setState({ loading: false });
       return;
     } else if (op == UserOperation.CreateCommunity) {
       let data = wsJsonToRes<CommunityResponse>(msg, CommunityResponse);
-      this.state.loading = false;
       this.props.onCreate(data.community_view);
 
       // Update myUserInfo
@@ -401,7 +397,7 @@ export class CommunityForm extends Component<
       });
     } else if (op == UserOperation.EditCommunity) {
       let data = wsJsonToRes<CommunityResponse>(msg, CommunityResponse);
-      this.state.loading = false;
+      this.setState({ loading: false });
       this.props.onEdit(data.community_view);
       let community = data.community_view.community;
 
index cc987053246c4a7ed2db183209ea80b5f5492ff3..b051ed8aac36d85e72eafa7a9b981c30301362e3 100644 (file)
@@ -64,7 +64,7 @@ export class CommunityLink extends Component<CommunityLinkProps, any> {
             some: icon => <PictrsImage src={icon} icon />,
             none: <></>,
           })}
-        <span class="overflow-wrap-anywhere">{displayName}</span>
+        <span className="overflow-wrap-anywhere">{displayName}</span>
       </>
     );
   }
index 4e8d949c4ca1b99f6119eff73d61dc1e97303445..5c3746e1b0bcc7cd6dffae792ed3e0bfa92e1f18 100644 (file)
@@ -139,24 +139,27 @@ export class Community extends Component<any, State> {
 
     // Only fetch the data if coming from another route
     if (this.isoData.path == this.context.router.route.match.url) {
-      this.state.communityRes = Some(
-        this.isoData.routeData[0] as GetCommunityResponse
-      );
+      this.state = {
+        ...this.state,
+        communityRes: Some(this.isoData.routeData[0] as GetCommunityResponse),
+      };
       let postsRes = Some(this.isoData.routeData[1] as GetPostsResponse);
       let commentsRes = Some(this.isoData.routeData[2] as GetCommentsResponse);
 
-      postsRes.match({
-        some: pvs => (this.state.posts = pvs.posts),
-        none: void 0,
-      });
-      commentsRes.match({
-        some: cvs => (this.state.comments = cvs.comments),
-        none: void 0,
-      });
+      if (postsRes.isSome()) {
+        this.state = { ...this.state, posts: postsRes.unwrap().posts };
+      }
 
-      this.state.communityLoading = false;
-      this.state.postsLoading = false;
-      this.state.commentsLoading = false;
+      if (commentsRes.isSome()) {
+        this.state = { ...this.state, comments: commentsRes.unwrap().comments };
+      }
+
+      this.state = {
+        ...this.state,
+        communityLoading: false,
+        postsLoading: false,
+        commentsLoading: false,
+      };
     } else {
       this.fetchCommunity();
       this.fetchData();
@@ -278,7 +281,7 @@ export class Community extends Component<any, State> {
 
   render() {
     return (
-      <div class="container">
+      <div className="container">
         {this.state.communityLoading ? (
           <h5>
             <Spinner large />
@@ -294,12 +297,12 @@ export class Community extends Component<any, State> {
                   image={res.community_view.community.icon}
                 />
 
-                <div class="row">
-                  <div class="col-12 col-md-8">
+                <div className="row">
+                  <div className="col-12 col-md-8">
                     {this.communityInfo()}
-                    <div class="d-block d-md-none">
+                    <div className="d-block d-md-none">
                       <button
-                        class="btn btn-secondary d-inline-block mb-2 mr-3"
+                        className="btn btn-secondary d-inline-block mb-2 mr-3"
                         onClick={linkEvent(this, this.handleShowSidebarMobile)}
                       >
                         {i18n.t("sidebar")}{" "}
@@ -344,7 +347,7 @@ export class Community extends Component<any, State> {
                       onChange={this.handlePageChange}
                     />
                   </div>
-                  <div class="d-none d-md-block col-md-4">
+                  <div className="d-none d-md-block col-md-4">
                     <Sidebar
                       community_view={res.community_view}
                       moderators={res.moderators}
@@ -413,9 +416,9 @@ export class Community extends Component<any, State> {
       .map(r => r.community_view.community)
       .match({
         some: community => (
-          <div class="mb-2">
+          <div className="mb-2">
             <BannerIconHeader banner={community.banner} icon={community.icon} />
-            <h5 class="mb-0 overflow-wrap-anywhere">{community.title}</h5>
+            <h5 className="mb-0 overflow-wrap-anywhere">{community.title}</h5>
             <CommunityLink
               community={community}
               realLink
@@ -434,14 +437,14 @@ export class Community extends Component<any, State> {
       communityRSSUrl(r.community_view.community.actor_id, this.state.sort)
     );
     return (
-      <div class="mb-3">
-        <span class="mr-3">
+      <div className="mb-3">
+        <span className="mr-3">
           <DataTypeSelect
             type_={this.state.dataType}
             onChange={this.handleDataTypeChange}
           />
         </span>
-        <span class="mr-2">
+        <span className="mr-2">
           <SortSelect sort={this.state.sort} onChange={this.handleSortChange} />
         </span>
         {communityRss.match({
@@ -475,8 +478,7 @@ export class Community extends Component<any, State> {
   }
 
   handleShowSidebarMobile(i: Community) {
-    i.state.showSidebarMobile = !i.state.showSidebarMobile;
-    i.setState(i.state);
+    i.setState({ showSidebarMobile: !i.state.showSidebarMobile });
   }
 
   updateUrl(paramUpdates: UrlParams) {
@@ -543,9 +545,7 @@ export class Community extends Component<any, State> {
       this.fetchData();
     } else if (op == UserOperation.GetCommunity) {
       let data = wsJsonToRes<GetCommunityResponse>(msg, GetCommunityResponse);
-      this.state.communityRes = Some(data);
-      this.state.communityLoading = false;
-      this.setState(this.state);
+      this.setState({ communityRes: Some(data), communityLoading: false });
       // TODO why is there no auth in this form?
       WebSocketService.Instance.send(
         wsClient.communityJoin({
@@ -576,9 +576,7 @@ export class Community extends Component<any, State> {
       this.setState(this.state);
     } else if (op == UserOperation.GetPosts) {
       let data = wsJsonToRes<GetPostsResponse>(msg, GetPostsResponse);
-      this.state.posts = data.posts;
-      this.state.postsLoading = false;
-      this.setState(this.state);
+      this.setState({ posts: data.posts, postsLoading: false });
       restoreScrollPosition(this.context);
       setupTippy();
     } else if (
@@ -631,9 +629,7 @@ export class Community extends Component<any, State> {
       this.setState(this.state);
     } else if (op == UserOperation.GetComments) {
       let data = wsJsonToRes<GetCommentsResponse>(msg, GetCommentsResponse);
-      this.state.comments = data.comments;
-      this.state.commentsLoading = false;
-      this.setState(this.state);
+      this.setState({ comments: data.comments, commentsLoading: false });
     } else if (
       op == UserOperation.EditComment ||
       op == UserOperation.DeleteComment ||
index 9a36678433b964492b394e4ea34223f88a2aa15a..8aed56b12eb61ef3dc8c6fe91d4e6bb78328ceea 100644 (file)
@@ -56,7 +56,7 @@ export class CreateCommunity extends Component<any, CreateCommunityState> {
 
   render() {
     return (
-      <div class="container">
+      <div className="container">
         <HtmlTags
           title={this.documentTitle}
           path={this.context.router.route.match.url}
@@ -68,8 +68,8 @@ export class CreateCommunity extends Component<any, CreateCommunityState> {
             <Spinner large />
           </h5>
         ) : (
-          <div class="row">
-            <div class="col-12 col-lg-6 offset-lg-3 mb-4">
+          <div className="row">
+            <div className="col-12 col-lg-6 offset-lg-3 mb-4">
               <h5>{i18n.t("create_community")}</h5>
               <CommunityForm
                 community_view={None}
index 84879d7b25474e50a9e2d574624e45212f2f6f7f..c723567789095b515368bf3a9241c63bc6ebe656 100644 (file)
@@ -91,8 +91,8 @@ export class Sidebar extends Component<SidebarProps, SidebarState> {
   sidebar() {
     return (
       <div>
-        <div class="card border-secondary mb-3">
-          <div class="card-body">
+        <div className="card border-secondary mb-3">
+          <div className="card-body">
             {this.communityTitle()}
             {this.adminButtons()}
             {this.subscribe()}
@@ -100,8 +100,8 @@ export class Sidebar extends Component<SidebarProps, SidebarState> {
             {this.blockCommunity()}
           </div>
         </div>
-        <div class="card border-secondary mb-3">
-          <div class="card-body">
+        <div className="card border-secondary mb-3">
+          <div className="card-body">
             {this.description()}
             {this.badges()}
             {this.mods()}
@@ -120,10 +120,10 @@ export class Sidebar extends Component<SidebarProps, SidebarState> {
           {this.props.showIcon && (
             <BannerIconHeader icon={community.icon} banner={community.banner} />
           )}
-          <span class="mr-2">{community.title}</span>
+          <span className="mr-2">{community.title}</span>
           {subscribed == SubscribedType.Subscribed && (
             <button
-              class="btn btn-secondary btn-sm mr-2"
+              className="btn btn-secondary btn-sm mr-2"
               onClick={linkEvent(this, this.handleUnsubscribe)}
             >
               <Icon icon="check" classes="icon-inline text-success mr-1" />
@@ -132,7 +132,7 @@ export class Sidebar extends Component<SidebarProps, SidebarState> {
           )}
           {subscribed == SubscribedType.Pending && (
             <button
-              class="btn btn-warning mr-2"
+              className="btn btn-warning mr-2"
               onClick={linkEvent(this, this.handleUnsubscribe)}
             >
               {i18n.t("subscribe_pending")}
@@ -169,7 +169,7 @@ export class Sidebar extends Component<SidebarProps, SidebarState> {
     let community_view = this.props.community_view;
     let counts = community_view.counts;
     return (
-      <ul class="my-1 list-inline">
+      <ul className="my-1 list-inline">
         <li className="list-inline-item badge badge-secondary">
           {i18n.t("number_online", {
             count: this.props.online,
@@ -260,10 +260,10 @@ export class Sidebar extends Component<SidebarProps, SidebarState> {
 
   mods() {
     return (
-      <ul class="list-inline small">
-        <li class="list-inline-item">{i18n.t("mods")}: </li>
+      <ul className="list-inline small">
+        <li className="list-inline-item">{i18n.t("mods")}: </li>
         {this.props.moderators.map(mod => (
-          <li class="list-inline-item">
+          <li key={mod.moderator.id} className="list-inline-item">
             <PersonListing person={mod.moderator} />
           </li>
         ))}
@@ -288,10 +288,10 @@ export class Sidebar extends Component<SidebarProps, SidebarState> {
   subscribe() {
     let community_view = this.props.community_view;
     return (
-      <div class="mb-2">
+      <div className="mb-2">
         {community_view.subscribed == SubscribedType.NotSubscribed && (
           <button
-            class="btn btn-secondary btn-block"
+            className="btn btn-secondary btn-block"
             onClick={linkEvent(this, this.handleSubscribe)}
           >
             {i18n.t("subscribe")}
@@ -306,18 +306,18 @@ export class Sidebar extends Component<SidebarProps, SidebarState> {
     let blocked = this.props.community_view.blocked;
 
     return (
-      <div class="mb-2">
+      <div className="mb-2">
         {community_view.subscribed == SubscribedType.NotSubscribed &&
           (blocked ? (
             <button
-              class="btn btn-danger btn-block"
+              className="btn btn-danger btn-block"
               onClick={linkEvent(this, this.handleUnblock)}
             >
               {i18n.t("unblock_community")}
             </button>
           ) : (
             <button
-              class="btn btn-danger btn-block"
+              className="btn btn-danger btn-block"
               onClick={linkEvent(this, this.handleBlock)}
             >
               {i18n.t("block_community")}
@@ -341,12 +341,12 @@ export class Sidebar extends Component<SidebarProps, SidebarState> {
     let community_view = this.props.community_view;
     return (
       <>
-        <ul class="list-inline mb-1 text-muted font-weight-bold">
+        <ul className="list-inline mb-1 text-muted font-weight-bold">
           {amMod(Some(this.props.moderators)) && (
             <>
               <li className="list-inline-item-action">
                 <button
-                  class="btn btn-link text-muted d-inline-block"
+                  className="btn btn-link text-muted d-inline-block"
                   onClick={linkEvent(this, this.handleEditClick)}
                   data-tippy-content={i18n.t("edit")}
                   aria-label={i18n.t("edit")}
@@ -358,7 +358,7 @@ export class Sidebar extends Component<SidebarProps, SidebarState> {
                 (!this.state.showConfirmLeaveModTeam ? (
                   <li className="list-inline-item-action">
                     <button
-                      class="btn btn-link text-muted d-inline-block"
+                      className="btn btn-link text-muted d-inline-block"
                       onClick={linkEvent(
                         this,
                         this.handleShowConfirmLeaveModTeamClick
@@ -374,7 +374,7 @@ export class Sidebar extends Component<SidebarProps, SidebarState> {
                     </li>
                     <li className="list-inline-item-action">
                       <button
-                        class="btn btn-link text-muted d-inline-block"
+                        className="btn btn-link text-muted d-inline-block"
                         onClick={linkEvent(this, this.handleLeaveModTeamClick)}
                       >
                         {i18n.t("yes")}
@@ -382,7 +382,7 @@ export class Sidebar extends Component<SidebarProps, SidebarState> {
                     </li>
                     <li className="list-inline-item-action">
                       <button
-                        class="btn btn-link text-muted d-inline-block"
+                        className="btn btn-link text-muted d-inline-block"
                         onClick={linkEvent(
                           this,
                           this.handleCancelLeaveModTeamClick
@@ -396,7 +396,7 @@ export class Sidebar extends Component<SidebarProps, SidebarState> {
               {amTopMod(Some(this.props.moderators)) && (
                 <li className="list-inline-item-action">
                   <button
-                    class="btn btn-link text-muted d-inline-block"
+                    className="btn btn-link text-muted d-inline-block"
                     onClick={linkEvent(this, this.handleDeleteClick)}
                     data-tippy-content={
                       !community_view.community.deleted
@@ -424,21 +424,21 @@ export class Sidebar extends Component<SidebarProps, SidebarState> {
             <li className="list-inline-item">
               {!this.props.community_view.community.removed ? (
                 <button
-                  class="btn btn-link text-muted d-inline-block"
+                  className="btn btn-link text-muted d-inline-block"
                   onClick={linkEvent(this, this.handleModRemoveShow)}
                 >
                   {i18n.t("remove")}
                 </button>
               ) : (
                 <button
-                  class="btn btn-link text-muted d-inline-block"
+                  className="btn btn-link text-muted d-inline-block"
                   onClick={linkEvent(this, this.handleModRemoveSubmit)}
                 >
                   {i18n.t("restore")}
                 </button>
               )}
               <button
-                class="btn btn-link text-muted d-inline-block"
+                className="btn btn-link text-muted d-inline-block"
                 onClick={linkEvent(this, this.handlePurgeCommunityShow)}
                 aria-label={i18n.t("purge_community")}
               >
@@ -449,14 +449,14 @@ export class Sidebar extends Component<SidebarProps, SidebarState> {
         </ul>
         {this.state.showRemoveDialog && (
           <form onSubmit={linkEvent(this, this.handleModRemoveSubmit)}>
-            <div class="form-group">
-              <label class="col-form-label" htmlFor="remove-reason">
+            <div className="form-group">
+              <label className="col-form-label" htmlFor="remove-reason">
                 {i18n.t("reason")}
               </label>
               <input
                 type="text"
                 id="remove-reason"
-                class="form-control mr-2"
+                className="form-control mr-2"
                 placeholder={i18n.t("optional")}
                 value={toUndefined(this.state.removeReason)}
                 onInput={linkEvent(this, this.handleModRemoveReasonChange)}
@@ -467,8 +467,8 @@ export class Sidebar extends Component<SidebarProps, SidebarState> {
             {/*   <label class="col-form-label">Expires</label> */}
             {/*   <input type="date" class="form-control mr-2" placeholder={i18n.t('expires')} value={this.state.removeExpires} onInput={linkEvent(this, this.handleModRemoveExpiresChange)} /> */}
             {/* </div> */}
-            <div class="form-group">
-              <button type="submit" class="btn btn-secondary">
+            <div className="form-group">
+              <button type="submit" className="btn btn-secondary">
                 {i18n.t("remove_community")}
               </button>
             </div>
@@ -476,29 +476,29 @@ export class Sidebar extends Component<SidebarProps, SidebarState> {
         )}
         {this.state.showPurgeDialog && (
           <form onSubmit={linkEvent(this, this.handlePurgeSubmit)}>
-            <div class="form-group">
+            <div className="form-group">
               <PurgeWarning />
             </div>
-            <div class="form-group">
-              <label class="sr-only" htmlFor="purge-reason">
+            <div className="form-group">
+              <label className="sr-only" htmlFor="purge-reason">
                 {i18n.t("reason")}
               </label>
               <input
                 type="text"
                 id="purge-reason"
-                class="form-control mr-2"
+                className="form-control mr-2"
                 placeholder={i18n.t("reason")}
                 value={toUndefined(this.state.purgeReason)}
                 onInput={linkEvent(this, this.handlePurgeReasonChange)}
               />
             </div>
-            <div class="form-group">
+            <div className="form-group">
               {this.state.purgeLoading ? (
                 <Spinner />
               ) : (
                 <button
                   type="submit"
-                  class="btn btn-secondary"
+                  className="btn btn-secondary"
                   aria-label={i18n.t("purge_community")}
                 >
                   {i18n.t("purge_community")}
@@ -512,18 +512,15 @@ export class Sidebar extends Component<SidebarProps, SidebarState> {
   }
 
   handleEditClick(i: Sidebar) {
-    i.state.showEdit = true;
-    i.setState(i.state);
+    i.setState({ showEdit: true });
   }
 
   handleEditCommunity() {
-    this.state.showEdit = false;
-    this.setState(this.state);
+    this.setState({ showEdit: false });
   }
 
   handleEditCancel() {
-    this.state.showEdit = false;
-    this.setState(this.state);
+    this.setState({ showEdit: false });
   }
 
   handleDeleteClick(i: Sidebar, event: any) {
@@ -537,8 +534,7 @@ export class Sidebar extends Component<SidebarProps, SidebarState> {
   }
 
   handleShowConfirmLeaveModTeamClick(i: Sidebar) {
-    i.state.showConfirmLeaveModTeam = true;
-    i.setState(i.state);
+    i.setState({ showConfirmLeaveModTeam: true });
   }
 
   handleLeaveModTeamClick(i: Sidebar) {
@@ -551,16 +547,14 @@ export class Sidebar extends Component<SidebarProps, SidebarState> {
           auth: auth().unwrap(),
         });
         WebSocketService.Instance.send(wsClient.addModToCommunity(form));
-        i.state.showConfirmLeaveModTeam = false;
-        i.setState(i.state);
+        i.setState({ showConfirmLeaveModTeam: false });
       },
       none: void 0,
     });
   }
 
   handleCancelLeaveModTeamClick(i: Sidebar) {
-    i.state.showConfirmLeaveModTeam = false;
-    i.setState(i.state);
+    i.setState({ showConfirmLeaveModTeam: false });
   }
 
   handleUnsubscribe(i: Sidebar, event: any) {
@@ -611,18 +605,15 @@ export class Sidebar extends Component<SidebarProps, SidebarState> {
   }
 
   handleModRemoveShow(i: Sidebar) {
-    i.state.showRemoveDialog = true;
-    i.setState(i.state);
+    i.setState({ showRemoveDialog: true });
   }
 
   handleModRemoveReasonChange(i: Sidebar, event: any) {
-    i.state.removeReason = Some(event.target.value);
-    i.setState(i.state);
+    i.setState({ removeReason: Some(event.target.value) });
   }
 
   handleModRemoveExpiresChange(i: Sidebar, event: any) {
-    i.state.removeExpires = Some(event.target.value);
-    i.setState(i.state);
+    i.setState({ removeExpires: Some(event.target.value) });
   }
 
   handleModRemoveSubmit(i: Sidebar, event: any) {
@@ -636,19 +627,15 @@ export class Sidebar extends Component<SidebarProps, SidebarState> {
     });
     WebSocketService.Instance.send(wsClient.removeCommunity(removeForm));
 
-    i.state.showRemoveDialog = false;
-    i.setState(i.state);
+    i.setState({ showRemoveDialog: false });
   }
 
   handlePurgeCommunityShow(i: Sidebar) {
-    i.state.showPurgeDialog = true;
-    i.state.showRemoveDialog = false;
-    i.setState(i.state);
+    i.setState({ showPurgeDialog: true, showRemoveDialog: false });
   }
 
   handlePurgeReasonChange(i: Sidebar, event: any) {
-    i.state.purgeReason = Some(event.target.value);
-    i.setState(i.state);
+    i.setState({ purgeReason: Some(event.target.value) });
   }
 
   handlePurgeSubmit(i: Sidebar, event: any) {
@@ -661,8 +648,7 @@ export class Sidebar extends Component<SidebarProps, SidebarState> {
     });
     WebSocketService.Instance.send(wsClient.purgeCommunity(form));
 
-    i.state.purgeLoading = true;
-    i.setState(i.state);
+    i.setState({ purgeLoading: true });
   }
 
   handleBlock(i: Sidebar, event: any) {
index e01a09d7ac612a04ac274d987fd0b4d6b257f785..011a5767ac9e164465382858de2c2efcf14a6ee7 100644 (file)
@@ -59,10 +59,11 @@ export class AdminSettings extends Component<any, AdminSettingsState> {
 
     // Only fetch the data if coming from another route
     if (this.isoData.path == this.context.router.route.match.url) {
-      this.state.banned = (
-        this.isoData.routeData[0] as BannedPersonsResponse
-      ).banned;
-      this.state.loading = false;
+      this.state = {
+        ...this.state,
+        banned: (this.isoData.routeData[0] as BannedPersonsResponse).banned,
+        loading: false,
+      };
     } else {
       WebSocketService.Instance.send(
         wsClient.getBannedPersons({
@@ -103,14 +104,14 @@ export class AdminSettings extends Component<any, AdminSettingsState> {
 
   render() {
     return (
-      <div class="container">
+      <div className="container">
         {this.state.loading ? (
           <h5>
             <Spinner large />
           </h5>
         ) : (
-          <div class="row">
-            <div class="col-12 col-md-6">
+          <div className="row">
+            <div className="col-12 col-md-6">
               <HtmlTags
                 title={this.documentTitle}
                 path={this.context.router.route.match.url}
@@ -127,7 +128,7 @@ export class AdminSettings extends Component<any, AdminSettingsState> {
                 none: <></>,
               })}
             </div>
-            <div class="col-12 col-md-6">
+            <div className="col-12 col-md-6">
               {this.admins()}
               {this.bannedUsers()}
             </div>
@@ -141,9 +142,9 @@ export class AdminSettings extends Component<any, AdminSettingsState> {
     return (
       <>
         <h5>{capitalizeFirstLetter(i18n.t("admins"))}</h5>
-        <ul class="list-unstyled">
+        <ul className="list-unstyled">
           {this.state.siteRes.admins.map(admin => (
-            <li class="list-inline-item">
+            <li key={admin.person.id} className="list-inline-item">
               <PersonListing person={admin.person} />
             </li>
           ))}
@@ -157,7 +158,7 @@ export class AdminSettings extends Component<any, AdminSettingsState> {
     return (
       <button
         onClick={linkEvent(this, this.handleLeaveAdminTeam)}
-        class="btn btn-danger mb-2"
+        className="btn btn-danger mb-2"
       >
         {this.state.leaveAdminTeamLoading ? (
           <Spinner />
@@ -172,9 +173,9 @@ export class AdminSettings extends Component<any, AdminSettingsState> {
     return (
       <>
         <h5>{i18n.t("banned_users")}</h5>
-        <ul class="list-unstyled">
+        <ul className="list-unstyled">
           {this.state.banned.map(banned => (
-            <li class="list-inline-item">
+            <li key={banned.person.id} className="list-inline-item">
               <PersonListing person={banned.person} />
             </li>
           ))}
@@ -184,11 +185,10 @@ export class AdminSettings extends Component<any, AdminSettingsState> {
   }
 
   handleLeaveAdminTeam(i: AdminSettings) {
-    i.state.leaveAdminTeamLoading = true;
+    i.setState({ leaveAdminTeamLoading: true });
     WebSocketService.Instance.send(
       wsClient.leaveAdmin({ auth: auth().unwrap() })
     );
-    i.setState(i.state);
   }
 
   parseMessage(msg: any) {
@@ -197,26 +197,21 @@ export class AdminSettings extends Component<any, AdminSettingsState> {
     if (msg.error) {
       toast(i18n.t(msg.error), "danger");
       this.context.router.history.push("/");
-      this.state.loading = false;
-      this.setState(this.state);
+      this.setState({ loading: false });
       return;
     } else if (op == UserOperation.EditSite) {
       let data = wsJsonToRes<SiteResponse>(msg, SiteResponse);
-      this.state.siteRes.site_view = Some(data.site_view);
-      this.setState(this.state);
+      this.setState(s => ((s.siteRes.site_view = Some(data.site_view)), s));
       toast(i18n.t("site_saved"));
     } else if (op == UserOperation.GetBannedPersons) {
       let data = wsJsonToRes<BannedPersonsResponse>(msg, BannedPersonsResponse);
-      this.state.banned = data.banned;
-      this.state.loading = false;
-      this.setState(this.state);
+      this.setState({ banned: data.banned, loading: false });
     } else if (op == UserOperation.LeaveAdmin) {
       let data = wsJsonToRes<GetSiteResponse>(msg, GetSiteResponse);
-      this.state.siteRes.site_view = data.site_view;
-      this.setState(this.state);
-      this.state.leaveAdminTeamLoading = false;
+      this.setState(s => ((s.siteRes.site_view = data.site_view), s));
+      this.setState({ leaveAdminTeamLoading: false });
+
       toast(i18n.t("left_admin_team"));
-      this.setState(this.state);
       this.context.router.history.push("/");
     }
   }
index 53559df9cbe5bc93defd43edc0f7d5a93cc888f2..69de7a8585171c0c019125ad6e7affd52fd2e5f1 100644 (file)
@@ -157,22 +157,24 @@ export class Home extends Component<any, HomeState> {
       let commentsRes = Some(this.isoData.routeData[1] as GetCommentsResponse);
       let trendingRes = this.isoData.routeData[2] as ListCommunitiesResponse;
 
-      postsRes.match({
-        some: pvs => (this.state.posts = pvs.posts),
-        none: void 0,
-      });
-      commentsRes.match({
-        some: cvs => (this.state.comments = cvs.comments),
-        none: void 0,
-      });
-      this.state.trendingCommunities = trendingRes.communities;
+      if (postsRes.isSome()) {
+        this.state = { ...this.state, posts: postsRes.unwrap().posts };
+      }
+
+      if (commentsRes.isSome()) {
+        this.state = { ...this.state, comments: commentsRes.unwrap().comments };
+      }
 
       if (isBrowser()) {
         WebSocketService.Instance.send(
           wsClient.communityJoin({ community_id: 0 })
         );
       }
-      this.state.loading = false;
+      this.state = {
+        ...this.state,
+        trendingCommunities: trendingRes.communities,
+        loading: false,
+      };
     } else {
       this.fetchTrendingCommunities();
       this.fetchData();
@@ -317,7 +319,7 @@ export class Home extends Component<any, HomeState> {
 
   render() {
     return (
-      <div class="container">
+      <div className="container">
         <HtmlTags
           title={this.documentTitle}
           path={this.context.router.route.match.url}
@@ -325,12 +327,14 @@ export class Home extends Component<any, HomeState> {
           image={None}
         />
         {this.state.siteRes.site_view.isSome() && (
-          <div class="row">
-            <main role="main" class="col-12 col-md-8">
-              <div class="d-block d-md-none">{this.mobileView()}</div>
+          <div className="row">
+            <main role="main" className="col-12 col-md-8">
+              <div className="d-block d-md-none">{this.mobileView()}</div>
               {this.posts()}
             </main>
-            <aside class="d-none d-md-block col-md-4">{this.mySidebar()}</aside>
+            <aside className="d-none d-md-block col-md-4">
+              {this.mySidebar()}
+            </aside>
           </div>
         )}
       </div>
@@ -347,11 +351,11 @@ export class Home extends Component<any, HomeState> {
   mobileView() {
     let siteRes = this.state.siteRes;
     return (
-      <div class="row">
-        <div class="col-12">
+      <div className="row">
+        <div className="col-12">
           {this.hasFollows && (
             <button
-              class="btn btn-secondary d-inline-block mb-2 mr-3"
+              className="btn btn-secondary d-inline-block mb-2 mr-3"
               onClick={linkEvent(this, this.handleShowSubscribedMobile)}
             >
               {i18n.t("subscribed")}{" "}
@@ -366,7 +370,7 @@ export class Home extends Component<any, HomeState> {
             </button>
           )}
           <button
-            class="btn btn-secondary d-inline-block mb-2 mr-3"
+            className="btn btn-secondary d-inline-block mb-2 mr-3"
             onClick={linkEvent(this, this.handleShowTrendingMobile)}
           >
             {i18n.t("trending")}{" "}
@@ -378,7 +382,7 @@ export class Home extends Component<any, HomeState> {
             />
           </button>
           <button
-            class="btn btn-secondary d-inline-block mb-2 mr-3"
+            className="btn btn-secondary d-inline-block mb-2 mr-3"
             onClick={linkEvent(this, this.handleShowSidebarMobile)}
           >
             {i18n.t("sidebar")}{" "}
@@ -403,13 +407,13 @@ export class Home extends Component<any, HomeState> {
               none: <></>,
             })}
           {this.state.showTrendingMobile && (
-            <div class="col-12 card border-secondary mb-3">
-              <div class="card-body">{this.trendingCommunities()}</div>
+            <div className="col-12 card border-secondary mb-3">
+              <div className="card-body">{this.trendingCommunities()}</div>
             </div>
           )}
           {this.state.showSubscribedMobile && (
-            <div class="col-12 card border-secondary mb-3">
-              <div class="card-body">{this.subscribedCommunities()}</div>
+            <div className="col-12 card border-secondary mb-3">
+              <div className="card-body">{this.subscribedCommunities()}</div>
             </div>
           )}
         </div>
@@ -423,8 +427,8 @@ export class Home extends Component<any, HomeState> {
       <div>
         {!this.state.loading && (
           <div>
-            <div class="card border-secondary mb-3">
-              <div class="card-body">
+            <div className="card border-secondary mb-3">
+              <div className="card-body">
                 {this.trendingCommunities()}
                 {this.createCommunityButton()}
                 {this.exploreCommunitiesButton()}
@@ -443,8 +447,8 @@ export class Home extends Component<any, HomeState> {
               none: <></>,
             })}
             {this.hasFollows && (
-              <div class="card border-secondary mb-3">
-                <div class="card-body">{this.subscribedCommunities()}</div>
+              <div className="card border-secondary mb-3">
+                <div className="card-body">{this.subscribedCommunities()}</div>
               </div>
             )}
           </div>
@@ -480,9 +484,12 @@ export class Home extends Component<any, HomeState> {
             </Link>
           </T>
         </h5>
-        <ul class="list-inline mb-0">
+        <ul className="list-inline mb-0">
           {this.state.trendingCommunities.map(cv => (
-            <li class="list-inline-item d-inline-block">
+            <li
+              key={cv.community.id}
+              className="list-inline-item d-inline-block"
+            >
               <CommunityLink community={cv.community} />
             </li>
           ))}
@@ -502,7 +509,7 @@ export class Home extends Component<any, HomeState> {
             </Link>
           </T>
           <button
-            class="btn btn-sm text-muted"
+            className="btn btn-sm text-muted"
             onClick={linkEvent(this, this.handleCollapseSubscribe)}
             aria-label={i18n.t("collapse")}
             data-tippy-content={i18n.t("collapse")}
@@ -515,12 +522,15 @@ export class Home extends Component<any, HomeState> {
           </button>
         </h5>
         {!this.state.subscribedCollapsed && (
-          <ul class="list-inline mb-0">
+          <ul className="list-inline mb-0">
             {UserService.Instance.myUserInfo
               .map(m => m.follows)
               .unwrapOr([])
               .map(cfv => (
-                <li class="list-inline-item d-inline-block">
+                <li
+                  key={cfv.community.id}
+                  className="list-inline-item d-inline-block"
+                >
                   <CommunityLink community={cfv.community} />
                 </li>
               ))}
@@ -542,7 +552,7 @@ export class Home extends Component<any, HomeState> {
 
   posts() {
     return (
-      <div class="main-content-wrapper">
+      <div className="main-content-wrapper">
         {this.state.loading ? (
           <h5>
             <Spinner large />
@@ -594,13 +604,13 @@ export class Home extends Component<any, HomeState> {
 
     return (
       <div className="mb-3">
-        <span class="mr-3">
+        <span className="mr-3">
           <DataTypeSelect
             type_={this.state.dataType}
             onChange={this.handleDataTypeChange}
           />
         </span>
-        <span class="mr-3">
+        <span className="mr-3">
           <ListingTypeSelect
             type_={this.state.listingType}
             showLocal={showLocal(this.isoData)}
@@ -608,7 +618,7 @@ export class Home extends Component<any, HomeState> {
             onChange={this.handleListingTypeChange}
           />
         </span>
-        <span class="mr-2">
+        <span className="mr-2">
           <SortSelect sort={this.state.sort} onChange={this.handleSortChange} />
         </span>
         {this.state.listingType == ListingType.All && (
@@ -644,23 +654,19 @@ export class Home extends Component<any, HomeState> {
   }
 
   handleShowSubscribedMobile(i: Home) {
-    i.state.showSubscribedMobile = !i.state.showSubscribedMobile;
-    i.setState(i.state);
+    i.setState({ showSubscribedMobile: !i.state.showSubscribedMobile });
   }
 
   handleShowTrendingMobile(i: Home) {
-    i.state.showTrendingMobile = !i.state.showTrendingMobile;
-    i.setState(i.state);
+    i.setState({ showTrendingMobile: !i.state.showTrendingMobile });
   }
 
   handleShowSidebarMobile(i: Home) {
-    i.state.showSidebarMobile = !i.state.showSidebarMobile;
-    i.setState(i.state);
+    i.setState({ showSidebarMobile: !i.state.showSidebarMobile });
   }
 
   handleCollapseSubscribe(i: Home) {
-    i.state.subscribedCollapsed = !i.state.subscribedCollapsed;
-    i.setState(i.state);
+    i.setState({ subscribedCollapsed: !i.state.subscribedCollapsed });
   }
 
   handlePageChange(page: number) {
@@ -731,18 +737,14 @@ export class Home extends Component<any, HomeState> {
         msg,
         ListCommunitiesResponse
       );
-      this.state.trendingCommunities = data.communities;
-      this.setState(this.state);
+      this.setState({ trendingCommunities: data.communities });
     } else if (op == UserOperation.EditSite) {
       let data = wsJsonToRes<SiteResponse>(msg, SiteResponse);
-      this.state.siteRes.site_view = Some(data.site_view);
-      this.setState(this.state);
+      this.setState(s => ((s.siteRes.site_view = Some(data.site_view)), s));
       toast(i18n.t("site_saved"));
     } else if (op == UserOperation.GetPosts) {
       let data = wsJsonToRes<GetPostsResponse>(msg, GetPostsResponse);
-      this.state.posts = data.posts;
-      this.state.loading = false;
-      this.setState(this.state);
+      this.setState({ posts: data.posts, loading: false });
       WebSocketService.Instance.send(
         wsClient.communityJoin({ community_id: 0 })
       );
@@ -812,8 +814,7 @@ export class Home extends Component<any, HomeState> {
       this.setState(this.state);
     } else if (op == UserOperation.AddAdmin) {
       let data = wsJsonToRes<AddAdminResponse>(msg, AddAdminResponse);
-      this.state.siteRes.admins = data.admins;
-      this.setState(this.state);
+      this.setState(s => ((s.siteRes.admins = data.admins), s));
     } else if (op == UserOperation.BanPerson) {
       let data = wsJsonToRes<BanPersonResponse>(msg, BanPersonResponse);
       this.state.posts
@@ -823,9 +824,7 @@ export class Home extends Component<any, HomeState> {
       this.setState(this.state);
     } else if (op == UserOperation.GetComments) {
       let data = wsJsonToRes<GetCommentsResponse>(msg, GetCommentsResponse);
-      this.state.comments = data.comments;
-      this.state.loading = false;
-      this.setState(this.state);
+      this.setState({ comments: data.comments, loading: false });
     } else if (
       op == UserOperation.EditComment ||
       op == UserOperation.DeleteComment ||
index fe4064dec6a46cb05f9c96c33713e488e91aa6a6..f473ffa17446027559d04a8efa827aec5c866b27 100644 (file)
@@ -30,22 +30,22 @@ export class Instances extends Component<any, InstancesState> {
   render() {
     return this.state.siteRes.federated_instances.match({
       some: federated_instances => (
-        <div class="container">
+        <div className="container">
           <HtmlTags
             title={this.documentTitle}
             path={this.context.router.route.match.url}
             description={None}
             image={None}
           />
-          <div class="row">
-            <div class="col-md-6">
+          <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 class="col-md-6">
+                  <div className="col-md-6">
                     <h5>{i18n.t("allowed_instances")}</h5>
                     {this.itemList(allowed)}
                   </div>
@@ -55,7 +55,7 @@ export class Instances extends Component<any, InstancesState> {
             {federated_instances.blocked.match({
               some: blocked =>
                 blocked.length > 0 && (
-                  <div class="col-md-6">
+                  <div className="col-md-6">
                     <h5>{i18n.t("blocked_instances")}</h5>
                     {this.itemList(blocked)}
                   </div>
@@ -74,7 +74,7 @@ export class Instances extends Component<any, InstancesState> {
     return items.length > 0 ? (
       <ul>
         {items.map(i => (
-          <li>
+          <li key={i}>
             <a href={`https://${i}`} rel={relTags}>
               {i}
             </a>
index 590d7d84619104077574666597f4c9802320f55c..78c9bbba8ddfe87dc974405594016c9098a453ef 100644 (file)
@@ -26,7 +26,7 @@ export class Legal extends Component<any, LegalState> {
 
   render() {
     return (
-      <div class="container">
+      <div className="container">
         <HtmlTags
           title={this.documentTitle}
           path={this.context.router.route.match.url}
index 753e9d8166f9ba03b4df9e4ddd87ccfd93aba2ee..001cfd4c99aec1f030aff5a4f86171b617e2ce87 100644 (file)
@@ -81,15 +81,15 @@ export class Login extends Component<any, State> {
 
   render() {
     return (
-      <div class="container">
+      <div className="container">
         <HtmlTags
           title={this.documentTitle}
           path={this.context.router.route.match.url}
           description={None}
           image={None}
         />
-        <div class="row">
-          <div class="col-12 col-lg-6 offset-lg-3">{this.loginForm()}</div>
+        <div className="row">
+          <div className="col-12 col-lg-6 offset-lg-3">{this.loginForm()}</div>
         </div>
       </div>
     );
@@ -100,17 +100,17 @@ export class Login extends Component<any, State> {
       <div>
         <form onSubmit={linkEvent(this, this.handleLoginSubmit)}>
           <h5>{i18n.t("login")}</h5>
-          <div class="form-group row">
+          <div className="form-group row">
             <label
-              class="col-sm-2 col-form-label"
+              className="col-sm-2 col-form-label"
               htmlFor="login-email-or-username"
             >
               {i18n.t("email_or_username")}
             </label>
-            <div class="col-sm-10">
+            <div className="col-sm-10">
               <input
                 type="text"
-                class="form-control"
+                className="form-control"
                 id="login-email-or-username"
                 value={this.state.loginForm.username_or_email}
                 onInput={linkEvent(this, this.handleLoginUsernameChange)}
@@ -120,17 +120,17 @@ export class Login extends Component<any, State> {
               />
             </div>
           </div>
-          <div class="form-group row">
-            <label class="col-sm-2 col-form-label" htmlFor="login-password">
+          <div className="form-group row">
+            <label className="col-sm-2 col-form-label" htmlFor="login-password">
               {i18n.t("password")}
             </label>
-            <div class="col-sm-10">
+            <div className="col-sm-10">
               <input
                 type="password"
                 id="login-password"
                 value={this.state.loginForm.password}
                 onInput={linkEvent(this, this.handleLoginPasswordChange)}
-                class="form-control"
+                className="form-control"
                 autoComplete="current-password"
                 required
                 maxLength={60}
@@ -146,9 +146,9 @@ export class Login extends Component<any, State> {
               </button>
             </div>
           </div>
-          <div class="form-group row">
-            <div class="col-sm-10">
-              <button type="submit" class="btn btn-secondary">
+          <div className="form-group row">
+            <div className="col-sm-10">
+              <button type="submit" className="btn btn-secondary">
                 {this.state.loginLoading ? <Spinner /> : i18n.t("login")}
               </button>
             </div>
@@ -160,8 +160,7 @@ export class Login extends Component<any, State> {
 
   handleLoginSubmit(i: Login, event: any) {
     event.preventDefault();
-    i.state.loginLoading = true;
-    i.setState(i.state);
+    i.setState({ loginLoading: true });
     WebSocketService.Instance.send(wsClient.login(i.state.loginForm));
   }
 
@@ -188,21 +187,18 @@ export class Login extends Component<any, State> {
     console.log(msg);
     if (msg.error) {
       toast(i18n.t(msg.error), "danger");
-      this.state = this.emptyState;
-      this.setState(this.state);
+      this.setState(this.emptyState);
       return;
     } else {
       if (op == UserOperation.Login) {
         let data = wsJsonToRes<LoginResponse>(msg, LoginResponse);
-        this.state = this.emptyState;
-        this.setState(this.state);
+        this.setState(this.emptyState);
         UserService.Instance.login(data);
       } else if (op == UserOperation.PasswordReset) {
         toast(i18n.t("reset_password_mail_sent"));
       } else if (op == UserOperation.GetSite) {
         let data = wsJsonToRes<GetSiteResponse>(msg, GetSiteResponse);
-        this.state.siteRes = data;
-        this.setState(this.state);
+        this.setState({ siteRes: data });
       }
     }
   }
index 46d225e24639db3b75077fcc73ce9c12f106f239..8e1c9fff06a214812a1e904797335fd6c1ee8a15 100644 (file)
@@ -67,10 +67,10 @@ export class Setup extends Component<any, State> {
 
   render() {
     return (
-      <div class="container">
+      <div className="container">
         <Helmet title={this.documentTitle} />
-        <div class="row">
-          <div class="col-12 offset-lg-3 col-lg-6">
+        <div className="row">
+          <div className="col-12 offset-lg-3 col-lg-6">
             <h3>{i18n.t("lemmy_instance_setup")}</h3>
             {!this.state.doneRegisteringUser ? (
               this.registerUser()
@@ -87,14 +87,14 @@ export class Setup extends Component<any, State> {
     return (
       <form onSubmit={linkEvent(this, this.handleRegisterSubmit)}>
         <h5>{i18n.t("setup_admin")}</h5>
-        <div class="form-group row">
-          <label class="col-sm-2 col-form-label" htmlFor="username">
+        <div className="form-group row">
+          <label className="col-sm-2 col-form-label" htmlFor="username">
             {i18n.t("username")}
           </label>
-          <div class="col-sm-10">
+          <div className="col-sm-10">
             <input
               type="text"
-              class="form-control"
+              className="form-control"
               id="username"
               value={this.state.userForm.username}
               onInput={linkEvent(this, this.handleRegisterUsernameChange)}
@@ -104,16 +104,16 @@ export class Setup extends Component<any, State> {
             />
           </div>
         </div>
-        <div class="form-group row">
-          <label class="col-sm-2 col-form-label" htmlFor="email">
+        <div className="form-group row">
+          <label className="col-sm-2 col-form-label" htmlFor="email">
             {i18n.t("email")}
           </label>
 
-          <div class="col-sm-10">
+          <div className="col-sm-10">
             <input
               type="email"
               id="email"
-              class="form-control"
+              className="form-control"
               placeholder={i18n.t("optional")}
               value={toUndefined(this.state.userForm.email)}
               onInput={linkEvent(this, this.handleRegisterEmailChange)}
@@ -121,17 +121,17 @@ export class Setup extends Component<any, State> {
             />
           </div>
         </div>
-        <div class="form-group row">
-          <label class="col-sm-2 col-form-label" htmlFor="password">
+        <div className="form-group row">
+          <label className="col-sm-2 col-form-label" htmlFor="password">
             {i18n.t("password")}
           </label>
-          <div class="col-sm-10">
+          <div className="col-sm-10">
             <input
               type="password"
               id="password"
               value={this.state.userForm.password}
               onInput={linkEvent(this, this.handleRegisterPasswordChange)}
-              class="form-control"
+              className="form-control"
               required
               autoComplete="new-password"
               minLength={10}
@@ -139,17 +139,17 @@ export class Setup extends Component<any, State> {
             />
           </div>
         </div>
-        <div class="form-group row">
-          <label class="col-sm-2 col-form-label" htmlFor="verify-password">
+        <div className="form-group row">
+          <label className="col-sm-2 col-form-label" htmlFor="verify-password">
             {i18n.t("verify_password")}
           </label>
-          <div class="col-sm-10">
+          <div className="col-sm-10">
             <input
               type="password"
               id="verify-password"
               value={this.state.userForm.password_verify}
               onInput={linkEvent(this, this.handleRegisterPasswordVerifyChange)}
-              class="form-control"
+              className="form-control"
               required
               autoComplete="new-password"
               minLength={10}
@@ -157,9 +157,9 @@ export class Setup extends Component<any, State> {
             />
           </div>
         </div>
-        <div class="form-group row">
-          <div class="col-sm-10">
-            <button type="submit" class="btn btn-secondary">
+        <div className="form-group row">
+          <div className="col-sm-10">
+            <button type="submit" className="btn btn-secondary">
               {this.state.userLoading ? <Spinner /> : i18n.t("sign_up")}
             </button>
           </div>
@@ -170,8 +170,7 @@ export class Setup extends Component<any, State> {
 
   handleRegisterSubmit(i: Setup, event: any) {
     event.preventDefault();
-    i.state.userLoading = true;
-    i.setState(i.state);
+    i.setState({ userLoading: true });
     event.preventDefault();
     WebSocketService.Instance.send(wsClient.register(i.state.userForm));
   }
@@ -200,14 +199,12 @@ export class Setup extends Component<any, State> {
     let op = wsUserOp(msg);
     if (msg.error) {
       toast(i18n.t(msg.error), "danger");
-      this.state.userLoading = false;
-      this.setState(this.state);
+      this.setState({ userLoading: false });
       return;
     } else if (op == UserOperation.Register) {
       let data = wsJsonToRes<LoginResponse>(msg, LoginResponse);
-      this.state.userLoading = false;
+      this.setState({ userLoading: false });
       UserService.Instance.login(data);
-      this.setState(this.state);
     } else if (op == UserOperation.CreateSite) {
       window.location.href = "/";
     }
index 9e6e3c7569e111843dec32d76f9821688e4c8c19..d7dc2a27627b19489fec1c5e6bf1bd6bc5af4592 100644 (file)
@@ -127,15 +127,17 @@ export class Signup extends Component<any, State> {
 
   render() {
     return (
-      <div class="container">
+      <div className="container">
         <HtmlTags
           title={this.documentTitle}
           path={this.context.router.route.match.url}
           description={None}
           image={None}
         />
-        <div class="row">
-          <div class="col-12 col-lg-6 offset-lg-3">{this.registerForm()}</div>
+        <div className="row">
+          <div className="col-12 col-lg-6 offset-lg-3">
+            {this.registerForm()}
+          </div>
         </div>
       </div>
     );
@@ -148,8 +150,8 @@ export class Signup extends Component<any, State> {
           <h5>{this.titleName(siteView)}</h5>
 
           {this.isLemmyMl && (
-            <div class="form-group row">
-              <div class="mt-2 mb-0 alert alert-warning" role="alert">
+            <div className="form-group row">
+              <div className="mt-2 mb-0 alert alert-warning" role="alert">
                 <T i18nKey="lemmy_ml_registration_message">
                   #<a href={joinLemmyUrl}>#</a>
                 </T>
@@ -157,16 +159,19 @@ export class Signup extends Component<any, State> {
             </div>
           )}
 
-          <div class="form-group row">
-            <label class="col-sm-2 col-form-label" htmlFor="register-username">
+          <div className="form-group row">
+            <label
+              className="col-sm-2 col-form-label"
+              htmlFor="register-username"
+            >
               {i18n.t("username")}
             </label>
 
-            <div class="col-sm-10">
+            <div className="col-sm-10">
               <input
                 type="text"
                 id="register-username"
-                class="form-control"
+                className="form-control"
                 value={this.state.registerForm.username}
                 onInput={linkEvent(this, this.handleRegisterUsernameChange)}
                 required
@@ -177,15 +182,15 @@ export class Signup extends Component<any, State> {
             </div>
           </div>
 
-          <div class="form-group row">
-            <label class="col-sm-2 col-form-label" htmlFor="register-email">
+          <div className="form-group row">
+            <label className="col-sm-2 col-form-label" htmlFor="register-email">
               {i18n.t("email")}
             </label>
-            <div class="col-sm-10">
+            <div className="col-sm-10">
               <input
                 type="email"
                 id="register-email"
-                class="form-control"
+                className="form-control"
                 placeholder={
                   siteView.site.require_email_verification
                     ? i18n.t("required")
@@ -201,7 +206,7 @@ export class Signup extends Component<any, State> {
                 !this.state.registerForm.email
                   .map(validEmail)
                   .unwrapOr(true) && (
-                  <div class="mt-2 mb-0 alert alert-warning" role="alert">
+                  <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")}
                   </div>
@@ -209,11 +214,14 @@ export class Signup extends Component<any, State> {
             </div>
           </div>
 
-          <div class="form-group row">
-            <label class="col-sm-2 col-form-label" htmlFor="register-password">
+          <div className="form-group row">
+            <label
+              className="col-sm-2 col-form-label"
+              htmlFor="register-password"
+            >
               {i18n.t("password")}
             </label>
-            <div class="col-sm-10">
+            <div className="col-sm-10">
               <input
                 type="password"
                 id="register-password"
@@ -222,25 +230,25 @@ export class Signup extends Component<any, State> {
                 onInput={linkEvent(this, this.handleRegisterPasswordChange)}
                 minLength={10}
                 maxLength={60}
-                class="form-control"
+                className="form-control"
                 required
               />
               {this.state.registerForm.password && (
-                <div class={this.passwordColorClass}>
+                <div className={this.passwordColorClass}>
                   {i18n.t(this.passwordStrength as I18nKeys)}
                 </div>
               )}
             </div>
           </div>
 
-          <div class="form-group row">
+          <div className="form-group row">
             <label
-              class="col-sm-2 col-form-label"
+              className="col-sm-2 col-form-label"
               htmlFor="register-verify-password"
             >
               {i18n.t("verify_password")}
             </label>
-            <div class="col-sm-10">
+            <div className="col-sm-10">
               <input
                 type="password"
                 id="register-verify-password"
@@ -251,7 +259,7 @@ export class Signup extends Component<any, State> {
                   this.handleRegisterPasswordVerifyChange
                 )}
                 maxLength={60}
-                class="form-control"
+                className="form-control"
                 required
               />
             </div>
@@ -259,9 +267,9 @@ export class Signup extends Component<any, State> {
 
           {siteView.site.require_application && (
             <>
-              <div class="form-group row">
-                <div class="offset-sm-2 col-sm-10">
-                  <div class="mt-2 alert alert-warning" role="alert">
+              <div className="form-group row">
+                <div className="offset-sm-2 col-sm-10">
+                  <div className="mt-2 alert alert-warning" role="alert">
                     <Icon icon="alert-triangle" classes="icon-inline mr-2" />
                     {i18n.t("fill_out_application")}
                   </div>
@@ -277,14 +285,14 @@ export class Signup extends Component<any, State> {
                 </div>
               </div>
 
-              <div class="form-group row">
+              <div className="form-group row">
                 <label
-                  class="col-sm-2 col-form-label"
+                  className="col-sm-2 col-form-label"
                   htmlFor="application_answer"
                 >
                   {i18n.t("answer")}
                 </label>
-                <div class="col-sm-10">
+                <div className="col-sm-10">
                   <MarkdownTextArea
                     initialContent={None}
                     placeholder={None}
@@ -299,12 +307,12 @@ export class Signup extends Component<any, State> {
           )}
 
           {this.state.captcha.isSome() && (
-            <div class="form-group row">
-              <label class="col-sm-2" htmlFor="register-captcha">
-                <span class="mr-2">{i18n.t("enter_code")}</span>
+            <div className="form-group row">
+              <label className="col-sm-2" htmlFor="register-captcha">
+                <span className="mr-2">{i18n.t("enter_code")}</span>
                 <button
                   type="button"
-                  class="btn btn-secondary"
+                  className="btn btn-secondary"
                   onClick={linkEvent(this, this.handleRegenCaptcha)}
                   aria-label={i18n.t("captcha")}
                 >
@@ -312,10 +320,10 @@ export class Signup extends Component<any, State> {
                 </button>
               </label>
               {this.showCaptcha()}
-              <div class="col-sm-6">
+              <div className="col-sm-6">
                 <input
                   type="text"
-                  class="form-control"
+                  className="form-control"
                   id="register-captcha"
                   value={toUndefined(this.state.registerForm.captcha_answer)}
                   onInput={linkEvent(
@@ -328,11 +336,11 @@ export class Signup extends Component<any, State> {
             </div>
           )}
           {siteView.site.enable_nsfw && (
-            <div class="form-group row">
-              <div class="col-sm-10">
-                <div class="form-check">
+            <div className="form-group row">
+              <div className="col-sm-10">
+                <div className="form-check">
                   <input
-                    class="form-check-input"
+                    className="form-check-input"
                     id="register-show-nsfw"
                     type="checkbox"
                     checked={this.state.registerForm.show_nsfw}
@@ -341,7 +349,10 @@ export class Signup extends Component<any, State> {
                       this.handleRegisterShowNsfwChange
                     )}
                   />
-                  <label class="form-check-label" htmlFor="register-show-nsfw">
+                  <label
+                    className="form-check-label"
+                    htmlFor="register-show-nsfw"
+                  >
                     {i18n.t("show_nsfw")}
                   </label>
                 </div>
@@ -353,14 +364,14 @@ export class Signup extends Component<any, State> {
             autoComplete="false"
             name="a_password"
             type="text"
-            class="form-control honeypot"
+            className="form-control honeypot"
             id="register-honey"
             value={toUndefined(this.state.registerForm.honeypot)}
             onInput={linkEvent(this, this.handleHoneyPotChange)}
           />
-          <div class="form-group row">
-            <div class="col-sm-10">
-              <button type="submit" class="btn btn-secondary">
+          <div className="form-group row">
+            <div className="col-sm-10">
+              <button type="submit" className="btn btn-secondary">
                 {this.state.registerLoading ? (
                   <Spinner />
                 ) : (
@@ -378,19 +389,19 @@ export class Signup extends Component<any, State> {
   showCaptcha() {
     return this.state.captcha.match({
       some: captcha => (
-        <div class="col-sm-4">
+        <div className="col-sm-4">
           {captcha.ok.match({
             some: res => (
               <>
                 <img
-                  class="rounded-top img-fluid"
+                  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
-                    class="rounded-bottom btn btn-sm btn-secondary btn-block"
+                    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)}
@@ -431,8 +442,7 @@ export class Signup extends Component<any, State> {
 
   handleRegisterSubmit(i: Signup, event: any) {
     event.preventDefault();
-    i.state.registerLoading = true;
-    i.setState(i.state);
+    i.setState({ registerLoading: true });
     WebSocketService.Instance.send(wsClient.register(i.state.registerForm));
   }
 
@@ -470,8 +480,7 @@ export class Signup extends Component<any, State> {
   }
 
   handleAnswerChange(val: string) {
-    this.state.registerForm.answer = Some(val);
-    this.setState(this.state);
+    this.setState(s => ((s.registerForm.answer = Some(val)), s));
   }
 
   handleHoneyPotChange(i: Signup, event: any) {
@@ -481,8 +490,7 @@ export class Signup extends Component<any, State> {
 
   handleRegenCaptcha(i: Signup) {
     i.audio = null;
-    i.state.captchaPlaying = false;
-    i.setState(i.state);
+    i.setState({ captchaPlaying: false });
     WebSocketService.Instance.send(wsClient.getCaptcha());
   }
 
@@ -500,13 +508,11 @@ export class Signup extends Component<any, State> {
 
             i.audio.play();
 
-            i.state.captchaPlaying = true;
-            i.setState(i.state);
+            i.setState({ captchaPlaying: true });
 
             i.audio.addEventListener("ended", () => {
               i.audio.currentTime = 0;
-              i.state.captchaPlaying = false;
-              i.setState(i.state);
+              i.setState({ captchaPlaying: false });
             });
           },
           none: void 0,
@@ -524,17 +530,15 @@ export class Signup extends Component<any, State> {
     console.log(msg);
     if (msg.error) {
       toast(i18n.t(msg.error), "danger");
-      this.state = this.emptyState;
-      this.state.registerForm.captcha_answer = undefined;
+      this.setState(this.emptyState);
+      this.setState(s => ((s.registerForm.captcha_answer = undefined), s));
       // Refetch another captcha
       // WebSocketService.Instance.send(wsClient.getCaptcha());
-      this.setState(this.state);
       return;
     } else {
       if (op == UserOperation.Register) {
         let data = wsJsonToRes<LoginResponse>(msg, LoginResponse);
-        this.state = this.emptyState;
-        this.setState(this.state);
+        this.setState(this.emptyState);
         // Only log them in if a jwt was set
         if (data.jwt.isSome()) {
           UserService.Instance.login(data);
@@ -552,9 +556,10 @@ export class Signup extends Component<any, State> {
         let data = wsJsonToRes<GetCaptchaResponse>(msg, GetCaptchaResponse);
         data.ok.match({
           some: res => {
-            this.state.captcha = Some(data);
-            this.state.registerForm.captcha_uuid = Some(res.uuid);
-            this.setState(this.state);
+            this.setState({ captcha: Some(data) });
+            this.setState(
+              s => ((s.registerForm.captcha_uuid = Some(res.uuid)), s)
+            );
           },
           none: void 0,
         });
@@ -562,8 +567,7 @@ export class Signup extends Component<any, State> {
         toast(i18n.t("reset_password_mail_sent"));
       } else if (op == UserOperation.GetSite) {
         let data = wsJsonToRes<GetSiteResponse>(msg, GetSiteResponse);
-        this.state.siteRes = data;
-        this.setState(this.state);
+        this.setState({ siteRes: data });
       }
     }
   }
index 5602d9bf13a4b37881b5cba31456ba50b6e14d5a..4f275cfd4fa032c994b012bab5f4d20b6e5e4fbc 100644 (file)
@@ -78,9 +78,11 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
     this.handleDefaultPostListingTypeChange =
       this.handleDefaultPostListingTypeChange.bind(this);
 
-    this.props.site.match({
-      some: site => {
-        this.state.siteForm = new EditSite({
+    if (this.props.site.isSome()) {
+      let site = this.props.site.unwrap();
+      this.state = {
+        ...this.state,
+        siteForm: new EditSite({
           name: Some(site.name),
           sidebar: site.sidebar,
           description: site.description,
@@ -101,21 +103,18 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
           legal_information: site.legal_information,
           hide_modlog_mod_names: site.hide_modlog_mod_names,
           auth: undefined,
-        });
-      },
-      none: void 0,
-    });
+        }),
+      };
+    }
   }
 
   async componentDidMount() {
-    this.state.themeList = Some(await fetchThemeList());
-    this.setState(this.state);
+    this.setState({ themeList: Some(await fetchThemeList()) });
   }
 
   // Necessary to stop the loading
   componentWillReceiveProps() {
-    this.state.loading = false;
-    this.setState(this.state);
+    this.setState({ loading: false });
   }
 
   componentDidUpdate() {
@@ -157,15 +156,15 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
               ? capitalizeFirstLetter(i18n.t("save"))
               : capitalizeFirstLetter(i18n.t("name"))
           } ${i18n.t("your_site")}`}</h5>
-          <div class="form-group row">
-            <label class="col-12 col-form-label" htmlFor="create-site-name">
+          <div className="form-group row">
+            <label className="col-12 col-form-label" htmlFor="create-site-name">
               {i18n.t("name")}
             </label>
-            <div class="col-12">
+            <div className="col-12">
               <input
                 type="text"
                 id="create-site-name"
-                class="form-control"
+                className="form-control"
                 value={toUndefined(this.state.siteForm.name)}
                 onInput={linkEvent(this, this.handleSiteNameChange)}
                 required
@@ -174,7 +173,7 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
               />
             </div>
           </div>
-          <div class="form-group">
+          <div className="form-group">
             <label>{i18n.t("icon")}</label>
             <ImageUploadForm
               uploadTitle={i18n.t("upload_icon")}
@@ -184,7 +183,7 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
               rounded
             />
           </div>
-          <div class="form-group">
+          <div className="form-group">
             <label>{i18n.t("banner")}</label>
             <ImageUploadForm
               uploadTitle={i18n.t("upload_banner")}
@@ -193,14 +192,14 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
               onRemove={this.handleBannerRemove}
             />
           </div>
-          <div class="form-group row">
-            <label class="col-12 col-form-label" htmlFor="site-desc">
+          <div className="form-group row">
+            <label className="col-12 col-form-label" htmlFor="site-desc">
               {i18n.t("description")}
             </label>
-            <div class="col-12">
+            <div className="col-12">
               <input
                 type="text"
-                class="form-control"
+                className="form-control"
                 id="site-desc"
                 value={toUndefined(this.state.siteForm.description)}
                 onInput={linkEvent(this, this.handleSiteDescChange)}
@@ -208,9 +207,9 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
               />
             </div>
           </div>
-          <div class="form-group row">
-            <label class="col-12 col-form-label">{i18n.t("sidebar")}</label>
-            <div class="col-12">
+          <div className="form-group row">
+            <label className="col-12 col-form-label">{i18n.t("sidebar")}</label>
+            <div className="col-12">
               <MarkdownTextArea
                 initialContent={this.state.siteForm.sidebar}
                 placeholder={None}
@@ -221,11 +220,11 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
               />
             </div>
           </div>
-          <div class="form-group row">
-            <label class="col-12 col-form-label">
+          <div className="form-group row">
+            <label className="col-12 col-form-label">
               {i18n.t("legal_information")}
             </label>
-            <div class="col-12">
+            <div className="col-12">
               <MarkdownTextArea
                 initialContent={this.state.siteForm.legal_information}
                 placeholder={None}
@@ -237,11 +236,11 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
             </div>
           </div>
           {this.state.siteForm.require_application.unwrapOr(false) && (
-            <div class="form-group row">
-              <label class="col-12 col-form-label">
+            <div className="form-group row">
+              <label className="col-12 col-form-label">
                 {i18n.t("application_questionnaire")}
               </label>
-              <div class="col-12">
+              <div className="col-12">
                 <MarkdownTextArea
                   initialContent={this.state.siteForm.application_question}
                   placeholder={None}
@@ -253,11 +252,11 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
               </div>
             </div>
           )}
-          <div class="form-group row">
-            <div class="col-12">
-              <div class="form-check">
+          <div className="form-group row">
+            <div className="col-12">
+              <div className="form-check">
                 <input
-                  class="form-check-input"
+                  className="form-check-input"
                   id="create-site-downvotes"
                   type="checkbox"
                   checked={toUndefined(this.state.siteForm.enable_downvotes)}
@@ -266,24 +265,27 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
                     this.handleSiteEnableDownvotesChange
                   )}
                 />
-                <label class="form-check-label" htmlFor="create-site-downvotes">
+                <label
+                  className="form-check-label"
+                  htmlFor="create-site-downvotes"
+                >
                   {i18n.t("enable_downvotes")}
                 </label>
               </div>
             </div>
           </div>
-          <div class="form-group row">
-            <div class="col-12">
-              <div class="form-check">
+          <div className="form-group row">
+            <div className="col-12">
+              <div className="form-check">
                 <input
-                  class="form-check-input"
+                  className="form-check-input"
                   id="create-site-enable-nsfw"
                   type="checkbox"
                   checked={toUndefined(this.state.siteForm.enable_nsfw)}
                   onChange={linkEvent(this, this.handleSiteEnableNsfwChange)}
                 />
                 <label
-                  class="form-check-label"
+                  className="form-check-label"
                   htmlFor="create-site-enable-nsfw"
                 >
                   {i18n.t("enable_nsfw")}
@@ -291,11 +293,11 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
               </div>
             </div>
           </div>
-          <div class="form-group row">
-            <div class="col-12">
-              <div class="form-check">
+          <div className="form-group row">
+            <div className="col-12">
+              <div className="form-check">
                 <input
-                  class="form-check-input"
+                  className="form-check-input"
                   id="create-site-open-registration"
                   type="checkbox"
                   checked={toUndefined(this.state.siteForm.open_registration)}
@@ -305,7 +307,7 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
                   )}
                 />
                 <label
-                  class="form-check-label"
+                  className="form-check-label"
                   htmlFor="create-site-open-registration"
                 >
                   {i18n.t("open_registration")}
@@ -313,11 +315,11 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
               </div>
             </div>
           </div>
-          <div class="form-group row">
-            <div class="col-12">
-              <div class="form-check">
+          <div className="form-group row">
+            <div className="col-12">
+              <div className="form-check">
                 <input
-                  class="form-check-input"
+                  className="form-check-input"
                   id="create-site-community-creation-admin-only"
                   type="checkbox"
                   checked={toUndefined(
@@ -329,7 +331,7 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
                   )}
                 />
                 <label
-                  class="form-check-label"
+                  className="form-check-label"
                   htmlFor="create-site-community-creation-admin-only"
                 >
                   {i18n.t("community_creation_admin_only")}
@@ -337,11 +339,11 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
               </div>
             </div>
           </div>
-          <div class="form-group row">
-            <div class="col-12">
-              <div class="form-check">
+          <div className="form-group row">
+            <div className="col-12">
+              <div className="form-check">
                 <input
-                  class="form-check-input"
+                  className="form-check-input"
                   id="create-site-require-email-verification"
                   type="checkbox"
                   checked={toUndefined(
@@ -353,7 +355,7 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
                   )}
                 />
                 <label
-                  class="form-check-label"
+                  className="form-check-label"
                   htmlFor="create-site-require-email-verification"
                 >
                   {i18n.t("require_email_verification")}
@@ -361,18 +363,18 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
               </div>
             </div>
           </div>
-          <div class="form-group row">
-            <div class="col-12">
-              <div class="form-check">
+          <div className="form-group row">
+            <div className="col-12">
+              <div className="form-check">
                 <input
-                  class="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
-                  class="form-check-label"
+                  className="form-check-label"
                   htmlFor="create-site-require-application"
                 >
                   {i18n.t("require_registration_application")}
@@ -380,10 +382,10 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
               </div>
             </div>
           </div>
-          <div class="form-group row">
-            <div class="col-12">
+          <div className="form-group row">
+            <div className="col-12">
               <label
-                class="form-check-label mr-2"
+                className="form-check-label mr-2"
                 htmlFor="create-site-default-theme"
               >
                 {i18n.t("theme")}
@@ -392,19 +394,21 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
                 id="create-site-default-theme"
                 value={toUndefined(this.state.siteForm.default_theme)}
                 onChange={linkEvent(this, this.handleSiteDefaultTheme)}
-                class="custom-select w-auto"
+                className="custom-select w-auto"
               >
                 <option value="browser">{i18n.t("browser_default")}</option>
                 {this.state.themeList.unwrapOr([]).map(theme => (
-                  <option value={theme}>{theme}</option>
+                  <option key={theme} value={theme}>
+                    {theme}
+                  </option>
                 ))}
               </select>
             </div>
           </div>
           {this.props.showLocal && (
             <form className="form-group row">
-              <label class="col-sm-3">{i18n.t("listing_type")}</label>
-              <div class="col-sm-9">
+              <label className="col-sm-3">{i18n.t("listing_type")}</label>
+              <div className="col-sm-9">
                 <ListingTypeSelect
                   type_={
                     ListingType[
@@ -420,18 +424,18 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
               </div>
             </form>
           )}
-          <div class="form-group row">
-            <div class="col-12">
-              <div class="form-check">
+          <div className="form-group row">
+            <div className="col-12">
+              <div className="form-check">
                 <input
-                  class="form-check-input"
+                  className="form-check-input"
                   id="create-site-private-instance"
                   type="checkbox"
                   checked={toUndefined(this.state.siteForm.private_instance)}
                   onChange={linkEvent(this, this.handleSitePrivateInstance)}
                 />
                 <label
-                  class="form-check-label"
+                  className="form-check-label"
                   htmlFor="create-site-private-instance"
                 >
                   {i18n.t("private_instance")}
@@ -439,11 +443,11 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
               </div>
             </div>
           </div>
-          <div class="form-group row">
-            <div class="col-12">
-              <div class="form-check">
+          <div className="form-group row">
+            <div className="col-12">
+              <div className="form-check">
                 <input
-                  class="form-check-input"
+                  className="form-check-input"
                   id="create-site-hide-modlog-mod-names"
                   type="checkbox"
                   checked={toUndefined(
@@ -452,7 +456,7 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
                   onChange={linkEvent(this, this.handleSiteHideModlogModNames)}
                 />
                 <label
-                  class="form-check-label"
+                  className="form-check-label"
                   htmlFor="create-site-hide-modlog-mod-names"
                 >
                   {i18n.t("hide_modlog_mod_names")}
@@ -460,11 +464,11 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
               </div>
             </div>
           </div>
-          <div class="form-group row">
-            <div class="col-12">
+          <div className="form-group row">
+            <div className="col-12">
               <button
                 type="submit"
-                class="btn btn-secondary mr-2"
+                className="btn btn-secondary mr-2"
                 disabled={this.state.loading}
               >
                 {this.state.loading ? (
@@ -478,7 +482,7 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
               {this.props.site.isSome() && (
                 <button
                   type="button"
-                  class="btn btn-secondary"
+                  className="btn btn-secondary"
                   onClick={linkEvent(this, this.handleCancel)}
                 >
                   {i18n.t("cancel")}
@@ -493,8 +497,8 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
 
   handleCreateSiteSubmit(i: SiteForm, event: any) {
     event.preventDefault();
-    i.state.loading = true;
-    i.state.siteForm.auth = auth().unwrap();
+    i.setState({ loading: true });
+    i.setState(s => ((s.siteForm.auth = auth().unwrap()), s));
 
     if (i.props.site.isSome()) {
       WebSocketService.Instance.send(wsClient.editSite(i.state.siteForm));
@@ -531,18 +535,15 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
   }
 
   handleSiteSidebarChange(val: string) {
-    this.state.siteForm.sidebar = Some(val);
-    this.setState(this.state);
+    this.setState(s => ((s.siteForm.sidebar = Some(val)), s));
   }
 
   handleSiteLegalInfoChange(val: string) {
-    this.state.siteForm.legal_information = Some(val);
-    this.setState(this.state);
+    this.setState(s => ((s.siteForm.legal_information = Some(val)), s));
   }
 
   handleSiteApplicationQuestionChange(val: string) {
-    this.state.siteForm.application_question = Some(val);
-    this.setState(this.state);
+    this.setState(s => ((s.siteForm.application_question = Some(val)), s));
   }
 
   handleSiteDescChange(i: SiteForm, event: any) {
@@ -600,29 +601,29 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
   }
 
   handleIconUpload(url: string) {
-    this.state.siteForm.icon = Some(url);
-    this.setState(this.state);
+    this.setState(s => ((s.siteForm.icon = Some(url)), s));
   }
 
   handleIconRemove() {
-    this.state.siteForm.icon = Some("");
-    this.setState(this.state);
+    this.setState(s => ((s.siteForm.icon = Some("")), s));
   }
 
   handleBannerUpload(url: string) {
-    this.state.siteForm.banner = Some(url);
-    this.setState(this.state);
+    this.setState(s => ((s.siteForm.banner = Some(url)), s));
   }
 
   handleBannerRemove() {
-    this.state.siteForm.banner = Some("");
-    this.setState(this.state);
+    this.setState(s => ((s.siteForm.banner = Some("")), s));
   }
 
   handleDefaultPostListingTypeChange(val: ListingType) {
-    this.state.siteForm.default_post_listing_type = Some(
-      ListingType[ListingType[val]]
+    this.setState(
+      s => (
+        (s.siteForm.default_post_listing_type = Some(
+          ListingType[ListingType[val]]
+        )),
+        s
+      )
     );
-    this.setState(this.state);
   }
 }
index 79a8a4306c410eaae00c68520b1799dbabff1762..d867c8d56913b1f60148ef1f1d0770dcc18d47bd 100644 (file)
@@ -38,11 +38,11 @@ export class SiteSidebar extends Component<SiteSidebarProps, SiteSidebarState> {
   render() {
     let site = this.props.site;
     return (
-      <div class="card border-secondary mb-3">
-        <div class="card-body">
+      <div className="card border-secondary mb-3">
+        <div className="card-body">
           {!this.state.showEdit ? (
             <div>
-              <div class="mb-2">
+              <div className="mb-2">
                 {this.siteName()}
                 {this.props.admins.isSome() && this.adminButtons()}
               </div>
@@ -69,10 +69,10 @@ export class SiteSidebar extends Component<SiteSidebarProps, SiteSidebarState> {
   siteName() {
     let site = this.props.site;
     return (
-      <h5 class="mb-0 d-inline">
+      <h5 className="mb-0 d-inline">
         {site.name}
         <button
-          class="btn btn-sm text-muted"
+          className="btn btn-sm text-muted"
           onClick={linkEvent(this, this.handleCollapseSidebar)}
           aria-label={i18n.t("collapse")}
           data-tippy-content={i18n.t("collapse")}
@@ -114,10 +114,10 @@ export class SiteSidebar extends Component<SiteSidebarProps, SiteSidebarState> {
   adminButtons() {
     return (
       amAdmin(this.props.admins) && (
-        <ul class="list-inline mb-1 text-muted font-weight-bold">
+        <ul className="list-inline mb-1 text-muted font-weight-bold">
           <li className="list-inline-item-action">
             <button
-              class="btn btn-link d-inline-block text-muted"
+              className="btn btn-link d-inline-block text-muted"
               onClick={linkEvent(this, this.handleEditClick)}
               aria-label={i18n.t("edit")}
               data-tippy-content={i18n.t("edit")}
@@ -138,10 +138,10 @@ export class SiteSidebar extends Component<SiteSidebarProps, SiteSidebarState> {
 
   admins(admins: PersonViewSafe[]) {
     return (
-      <ul class="mt-1 list-inline small mb-0">
-        <li class="list-inline-item">{i18n.t("admins")}:</li>
+      <ul className="mt-1 list-inline small mb-0">
+        <li className="list-inline-item">{i18n.t("admins")}:</li>
         {admins.map(av => (
-          <li class="list-inline-item">
+          <li key={av.person.id} className="list-inline-item">
             <PersonListing person={av.person} />
           </li>
         ))}
@@ -153,7 +153,7 @@ export class SiteSidebar extends Component<SiteSidebarProps, SiteSidebarState> {
     let counts = siteAggregates;
     let online = this.props.online.unwrapOr(1);
     return (
-      <ul class="my-2 list-inline">
+      <ul className="my-2 list-inline">
         <li className="list-inline-item badge badge-secondary">
           {i18n.t("number_online", {
             count: online,
@@ -246,22 +246,18 @@ export class SiteSidebar extends Component<SiteSidebarProps, SiteSidebarState> {
   }
 
   handleCollapseSidebar(i: SiteSidebar) {
-    i.state.collapsed = !i.state.collapsed;
-    i.setState(i.state);
+    i.setState({ collapsed: !i.state.collapsed });
   }
 
   handleEditClick(i: SiteSidebar) {
-    i.state.showEdit = true;
-    i.setState(i.state);
+    i.setState({ showEdit: true });
   }
 
   handleEditSite() {
-    this.state.showEdit = false;
-    this.setState(this.state);
+    this.setState({ showEdit: false });
   }
 
   handleEditCancel() {
-    this.state.showEdit = false;
-    this.setState(this.state);
+    this.setState({ showEdit: false });
   }
 }
index e8192d27caa21963f3203d428313e7839d726ab5..9eed9c9a9d2812cdf2c8a188c8eb1331e58c2eff 100644 (file)
@@ -114,31 +114,41 @@ export class Modlog extends Component<any, ModlogState> {
     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.state.communityId = this.props.match.params.community_id
-      ? Some(Number(this.props.match.params.community_id))
-      : None;
-
     this.parseMessage = this.parseMessage.bind(this);
     this.subscription = wsSubscribe(this.parseMessage);
 
+    this.state = {
+      ...this.state,
+      communityId: this.props.match.params.community_id
+        ? Some(Number(this.props.match.params.community_id))
+        : None,
+    };
+
     // Only fetch the data if coming from another route
     if (this.isoData.path == this.context.router.route.match.url) {
-      this.state.res = Some(this.isoData.routeData[0] as GetModlogResponse);
+      this.state = {
+        ...this.state,
+        res: Some(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.communityMods = communityRes.map(c => c.moderators);
+        this.state = {
+          ...this.state,
+          communityMods: communityRes.map(c => c.moderators),
+        };
       }
 
-      this.state.loading = false;
+      this.state = { ...this.state, loading: false };
     } else {
       this.refetch();
     }
@@ -300,219 +310,283 @@ export class Modlog extends Component<any, ModlogState> {
     switch (i.type_) {
       case ModlogActionType.ModRemovePost: {
         let mrpv = i.view as ModRemovePostView;
-        return [
-          mrpv.mod_remove_post.removed.unwrapOr(false)
-            ? "Removed "
-            : "Restored ",
-          <span>
-            Post <Link to={`/post/${mrpv.post.id}`}>{mrpv.post.name}</Link>
-          </span>,
-          mrpv.mod_remove_post.reason.match({
-            some: reason => <div>reason: {reason}</div>,
-            none: <></>,
-          }),
-        ];
+        return (
+          <>
+            <span>
+              {mrpv.mod_remove_post.removed.unwrapOr(false)
+                ? "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>
+          </>
+        );
       }
       case ModlogActionType.ModLockPost: {
         let mlpv = i.view as ModLockPostView;
-        return [
-          mlpv.mod_lock_post.locked.unwrapOr(false) ? "Locked " : "Unlocked ",
-          <span>
-            Post <Link to={`/post/${mlpv.post.id}`}>{mlpv.post.name}</Link>
-          </span>,
-        ];
+        return (
+          <>
+            <span>
+              {mlpv.mod_lock_post.locked.unwrapOr(false)
+                ? "Locked "
+                : "Unlocked "}
+            </span>
+            <span>
+              Post <Link to={`/post/${mlpv.post.id}`}>{mlpv.post.name}</Link>
+            </span>
+          </>
+        );
       }
       case ModlogActionType.ModStickyPost: {
         let mspv = i.view as ModStickyPostView;
-        return [
-          mspv.mod_sticky_post.stickied.unwrapOr(false)
-            ? "Stickied "
-            : "Unstickied ",
-          <span>
-            Post <Link to={`/post/${mspv.post.id}`}>{mspv.post.name}</Link>
-          </span>,
-        ];
+        return (
+          <>
+            <span>
+              {mspv.mod_sticky_post.stickied.unwrapOr(false)
+                ? "Stickied "
+                : "Unstickied "}
+            </span>
+            <span>
+              Post <Link to={`/post/${mspv.post.id}`}>{mspv.post.name}</Link>
+            </span>
+          </>
+        );
       }
       case ModlogActionType.ModRemoveComment: {
         let mrc = i.view as ModRemoveCommentView;
-        return [
-          mrc.mod_remove_comment.removed.unwrapOr(false)
-            ? "Removed "
-            : "Restored ",
-          <span>
-            Comment{" "}
-            <Link to={`/post/${mrc.post.id}/comment/${mrc.comment.id}`}>
-              {mrc.comment.content}
-            </Link>
-          </span>,
-          <span>
-            {" "}
-            by <PersonListing person={mrc.commenter} />
-          </span>,
-          mrc.mod_remove_comment.reason.match({
-            some: reason => <div>reason: {reason}</div>,
-            none: <></>,
-          }),
-        ];
+        return (
+          <>
+            <span>
+              {mrc.mod_remove_comment.removed.unwrapOr(false)
+                ? "Removed "
+                : "Restored "}
+            </span>
+            <span>
+              Comment{" "}
+              <Link to={`/post/${mrc.post.id}/comment/${mrc.comment.id}`}>
+                {mrc.comment.content}
+              </Link>
+            </span>
+            <span>
+              {" "}
+              by <PersonListing person={mrc.commenter} />
+            </span>
+            <span>
+              {mrc.mod_remove_comment.reason.match({
+                some: reason => <div>reason: {reason}</div>,
+                none: <></>,
+              })}
+            </span>
+          </>
+        );
       }
       case ModlogActionType.ModRemoveCommunity: {
         let mrco = i.view as ModRemoveCommunityView;
-        return [
-          mrco.mod_remove_community.removed.unwrapOr(false)
-            ? "Removed "
-            : "Restored ",
-          <span>
-            Community <CommunityLink community={mrco.community} />
-          </span>,
-          mrco.mod_remove_community.reason.match({
-            some: reason => <div>reason: {reason}</div>,
-            none: <></>,
-          }),
-          mrco.mod_remove_community.expires.match({
-            some: expires => (
-              <div>expires: {moment.utc(expires).fromNow()}</div>
-            ),
-            none: <></>,
-          }),
-        ];
+        return (
+          <>
+            <span>
+              {mrco.mod_remove_community.removed.unwrapOr(false)
+                ? "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>
+          </>
+        );
       }
       case ModlogActionType.ModBanFromCommunity: {
         let mbfc = i.view as ModBanFromCommunityView;
-        return [
-          <span>
-            {mbfc.mod_ban_from_community.banned.unwrapOr(false)
-              ? "Banned "
-              : "Unbanned "}{" "}
-          </span>,
-          <span>
-            <PersonListing person={mbfc.banned_person} />
-          </span>,
-          <span> from the community </span>,
-          <span>
-            <CommunityLink community={mbfc.community} />
-          </span>,
-          mbfc.mod_ban_from_community.reason.match({
-            some: reason => <div>reason: {reason}</div>,
-            none: <></>,
-          }),
-          mbfc.mod_ban_from_community.expires.match({
-            some: expires => (
-              <div>expires: {moment.utc(expires).fromNow()}</div>
-            ),
-            none: <></>,
-          }),
-        ];
+        return (
+          <>
+            <span>
+              {mbfc.mod_ban_from_community.banned.unwrapOr(false)
+                ? "Banned "
+                : "Unbanned "}{" "}
+            </span>
+            <span>
+              <PersonListing person={mbfc.banned_person} />
+            </span>
+            <span> from the community </span>
+            <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>
+          </>
+        );
       }
       case ModlogActionType.ModAddCommunity: {
         let mac = i.view as ModAddCommunityView;
-        return [
-          <span>
-            {mac.mod_add_community.removed.unwrapOr(false)
-              ? "Removed "
-              : "Appointed "}{" "}
-          </span>,
-          <span>
-            <PersonListing person={mac.modded_person} />
-          </span>,
-          <span> as a mod to the community </span>,
-          <span>
-            <CommunityLink community={mac.community} />
-          </span>,
-        ];
+        return (
+          <>
+            <span>
+              {mac.mod_add_community.removed.unwrapOr(false)
+                ? "Removed "
+                : "Appointed "}{" "}
+            </span>
+            <span>
+              <PersonListing person={mac.modded_person} />
+            </span>
+            <span> as a mod to the community </span>
+            <span>
+              <CommunityLink community={mac.community} />
+            </span>
+          </>
+        );
       }
       case ModlogActionType.ModTransferCommunity: {
         let mtc = i.view as ModTransferCommunityView;
-        return [
-          <span>
-            {mtc.mod_transfer_community.removed.unwrapOr(false)
-              ? "Removed "
-              : "Transferred "}{" "}
-          </span>,
-          <span>
-            <CommunityLink community={mtc.community} />
-          </span>,
-          <span> to </span>,
-          <span>
-            <PersonListing person={mtc.modded_person} />
-          </span>,
-        ];
+        return (
+          <>
+            <span>
+              {mtc.mod_transfer_community.removed.unwrapOr(false)
+                ? "Removed "
+                : "Transferred "}{" "}
+            </span>
+            <span>
+              <CommunityLink community={mtc.community} />
+            </span>
+            <span> to </span>
+            <span>
+              <PersonListing person={mtc.modded_person} />
+            </span>
+          </>
+        );
       }
       case ModlogActionType.ModBan: {
         let mb = i.view as ModBanView;
-        return [
-          <span>
-            {mb.mod_ban.banned.unwrapOr(false) ? "Banned " : "Unbanned "}{" "}
-          </span>,
-          <span>
-            <PersonListing person={mb.banned_person} />
-          </span>,
-          mb.mod_ban.reason.match({
-            some: reason => <div>reason: {reason}</div>,
-            none: <></>,
-          }),
-          mb.mod_ban.expires.match({
-            some: expires => (
-              <div>expires: {moment.utc(expires).fromNow()}</div>
-            ),
-            none: <></>,
-          }),
-        ];
+        return (
+          <>
+            <span>
+              {mb.mod_ban.banned.unwrapOr(false) ? "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>
+          </>
+        );
       }
       case ModlogActionType.ModAdd: {
         let ma = i.view as ModAddView;
-        return [
-          <span>
-            {ma.mod_add.removed.unwrapOr(false) ? "Removed " : "Appointed "}{" "}
-          </span>,
-          <span>
-            <PersonListing person={ma.modded_person} />
-          </span>,
-          <span> as an admin </span>,
-        ];
+        return (
+          <>
+            <span>
+              {ma.mod_add.removed.unwrapOr(false) ? "Removed " : "Appointed "}{" "}
+            </span>
+            <span>
+              <PersonListing person={ma.modded_person} />
+            </span>
+            <span> as an admin </span>
+          </>
+        );
       }
       case ModlogActionType.AdminPurgePerson: {
         let ap = i.view as AdminPurgePersonView;
-        return [
-          <span>Purged a Person</span>,
-          ap.admin_purge_person.reason.match({
-            some: reason => <div>reason: {reason}</div>,
-            none: <></>,
-          }),
-        ];
+        return (
+          <>
+            <span>Purged a Person</span>
+            <span>
+              {ap.admin_purge_person.reason.match({
+                some: reason => <div>reason: {reason}</div>,
+                none: <></>,
+              })}
+            </span>
+          </>
+        );
       }
       case ModlogActionType.AdminPurgeCommunity: {
         let ap = i.view as AdminPurgeCommunityView;
-        return [
-          <span>Purged a Community</span>,
-          ap.admin_purge_community.reason.match({
-            some: reason => <div>reason: {reason}</div>,
-            none: <></>,
-          }),
-        ];
+        return (
+          <>
+            <span>Purged a Community</span>
+            <span>
+              {ap.admin_purge_community.reason.match({
+                some: reason => <div>reason: {reason}</div>,
+                none: <></>,
+              })}
+            </span>
+          </>
+        );
       }
       case ModlogActionType.AdminPurgePost: {
         let ap = i.view as AdminPurgePostView;
-        return [
-          <span>Purged a Post from from </span>,
-          <CommunityLink community={ap.community} />,
-          ap.admin_purge_post.reason.match({
-            some: reason => <div>reason: {reason}</div>,
-            none: <></>,
-          }),
-        ];
+        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>
+          </>
+        );
       }
       case ModlogActionType.AdminPurgeComment: {
         let ap = i.view as AdminPurgeCommentView;
-        return [
-          <span>
-            Purged a Comment from{" "}
-            <Link to={`/post/${ap.post.id}`}>{ap.post.name}</Link>
-          </span>,
-          ap.admin_purge_comment.reason.match({
-            some: reason => <div>reason: {reason}</div>,
-            none: <></>,
-          }),
-        ];
+        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>
+          </>
+        );
       }
       default:
         return <div />;
@@ -525,7 +599,7 @@ export class Modlog extends Component<any, ModlogState> {
     return (
       <tbody>
         {combined.map(i => (
-          <tr>
+          <tr key={i.id}>
             <td>
               <MomentTime published={i.when_} updated={None} />
             </td>
@@ -593,67 +667,75 @@ export class Modlog extends Component<any, ModlogState> {
               })}
               <span>{i18n.t("modlog")}</span>
             </h5>
-            <form className="form-inline mr-2">
-              <select
-                value={this.state.filter_action}
-                onChange={linkEvent(this, this.handleFilterActionChange)}
-                className="custom-select col-4 mb-2"
-                aria-label="action"
-              >
-                <option disabled aria-hidden="true">
-                  {i18n.t("filter_by_action")}
-                </option>
-                <option value={ModlogActionType.All}>{i18n.t("all")}</option>
-                <option value={ModlogActionType.ModRemovePost}>
-                  Removing Posts
-                </option>
-                <option value={ModlogActionType.ModLockPost}>
-                  Locking Posts
-                </option>
-                <option value={ModlogActionType.ModStickyPost}>
-                  Stickying Posts
-                </option>
-                <option value={ModlogActionType.ModRemoveComment}>
-                  Removing Comments
-                </option>
-                <option value={ModlogActionType.ModRemoveCommunity}>
-                  Removing Communities
-                </option>
-                <option value={ModlogActionType.ModBanFromCommunity}>
-                  Banning From Communities
-                </option>
-                <option value={ModlogActionType.ModAddCommunity}>
-                  Adding Mod to Community
-                </option>
-                <option value={ModlogActionType.ModTransferCommunity}>
-                  Transfering Communities
-                </option>
-                <option value={ModlogActionType.ModAdd}>
-                  Adding Mod to Site
-                </option>
-                <option value={ModlogActionType.ModBan}>
-                  Banning From Site
-                </option>
-              </select>
+            <div className="form-row">
+              <div className="form-group col-sm-6">
+                <select
+                  value={this.state.filter_action}
+                  onChange={linkEvent(this, this.handleFilterActionChange)}
+                  className="custom-select mb-2"
+                  aria-label="action"
+                >
+                  <option disabled aria-hidden="true">
+                    {i18n.t("filter_by_action")}
+                  </option>
+                  <option value={ModlogActionType.All}>{i18n.t("all")}</option>
+                  <option value={ModlogActionType.ModRemovePost}>
+                    Removing Posts
+                  </option>
+                  <option value={ModlogActionType.ModLockPost}>
+                    Locking Posts
+                  </option>
+                  <option value={ModlogActionType.ModStickyPost}>
+                    Stickying Posts
+                  </option>
+                  <option value={ModlogActionType.ModRemoveComment}>
+                    Removing Comments
+                  </option>
+                  <option value={ModlogActionType.ModRemoveCommunity}>
+                    Removing Communities
+                  </option>
+                  <option value={ModlogActionType.ModBanFromCommunity}>
+                    Banning From Communities
+                  </option>
+                  <option value={ModlogActionType.ModAddCommunity}>
+                    Adding Mod to Community
+                  </option>
+                  <option value={ModlogActionType.ModTransferCommunity}>
+                    Transfering Communities
+                  </option>
+                  <option value={ModlogActionType.ModAdd}>
+                    Adding Mod to Site
+                  </option>
+                  <option value={ModlogActionType.ModBan}>
+                    Banning From Site
+                  </option>
+                </select>
+              </div>
               {this.state.siteRes.site_view.match({
                 some: site_view =>
                   !site_view.site.hide_modlog_mod_names.unwrapOr(false) && (
-                    <select
-                      id="filter-mod"
-                      value={toUndefined(this.state.filter_mod)}
-                    >
-                      <option>{i18n.t("filter_by_mod")}</option>
-                    </select>
+                    <div className="form-group col-sm-6">
+                      <select
+                        id="filter-mod"
+                        className="form-control"
+                        value={toUndefined(this.state.filter_mod)}
+                      >
+                        <option>{i18n.t("filter_by_mod")}</option>
+                      </select>
+                    </div>
                   ),
                 none: <></>,
               })}
-              <select
-                id="filter-user"
-                value={toUndefined(this.state.filter_user)}
-              >
-                <option>{i18n.t("filter_by_user")}</option>
-              </select>
-            </form>
+              <div className="form-group col-sm-6">
+                <select
+                  id="filter-user"
+                  className="form-control"
+                  value={toUndefined(this.state.filter_user)}
+                >
+                  <option>{i18n.t("filter_by_user")}</option>
+                </select>
+              </div>
+            </div>
             <div className="table-responsive">
               <table id="modlog_table" className="table table-sm table-hover">
                 <thead className="pointer">
@@ -719,8 +801,7 @@ export class Modlog extends Component<any, ModlogState> {
         this.userChoices.passedElement.element.addEventListener(
           "choice",
           (e: any) => {
-            this.state.filter_user = Some(Number(e.detail.choice.value));
-            this.setState(this.state);
+            this.setState({ filter_user: Some(Number(e.detail.choice.value)) });
             this.refetch();
           },
           false
@@ -759,8 +840,7 @@ export class Modlog extends Component<any, ModlogState> {
         this.modChoices.passedElement.element.addEventListener(
           "choice",
           (e: any) => {
-            this.state.filter_mod = Some(Number(e.detail.choice.value));
-            this.setState(this.state);
+            this.setState({ filter_mod: Some(Number(e.detail.choice.value)) });
             this.refetch();
           },
           false
@@ -829,14 +909,16 @@ export class Modlog extends Component<any, ModlogState> {
       return;
     } else if (op == UserOperation.GetModlog) {
       let data = wsJsonToRes<GetModlogResponse>(msg, GetModlogResponse);
-      this.state.loading = false;
       window.scrollTo(0, 0);
-      this.state.res = Some(data);
-      this.setState(this.state);
+      this.setState({ res: Some(data), loading: false });
+      this.setupUserFilter();
+      this.setupModFilter();
     } else if (op == UserOperation.GetCommunity) {
       let data = wsJsonToRes<GetCommunityResponse>(msg, GetCommunityResponse);
-      this.state.communityMods = Some(data.moderators);
-      this.state.communityName = Some(data.community_view.community.name);
+      this.setState({
+        communityMods: Some(data.moderators),
+        communityName: Some(data.community_view.community.name),
+      });
     }
   }
 }
index 2a8a7b2984640cade3ebe22dc365fface8dc794f..858364f945f90ac8cdc22f229148f9d602cd23fa 100644 (file)
@@ -127,15 +127,19 @@ export class Inbox extends Component<any, InboxState> {
 
     // Only fetch the data if coming from another route
     if (this.isoData.path == this.context.router.route.match.url) {
-      this.state.replies =
-        (this.isoData.routeData[0] as GetRepliesResponse).replies || [];
-      this.state.mentions =
-        (this.isoData.routeData[1] as GetPersonMentionsResponse).mentions || [];
-      this.state.messages =
-        (this.isoData.routeData[2] as PrivateMessagesResponse)
-          .private_messages || [];
-      this.state.combined = this.buildCombined();
-      this.state.loading = false;
+      this.state = {
+        ...this.state,
+        replies:
+          (this.isoData.routeData[0] as GetRepliesResponse).replies || [],
+        mentions:
+          (this.isoData.routeData[1] as GetPersonMentionsResponse).mentions ||
+          [],
+        messages:
+          (this.isoData.routeData[2] as PrivateMessagesResponse)
+            .private_messages || [],
+        loading: false,
+      };
+      this.state = { ...this.state, combined: this.buildCombined() };
     } else {
       this.refetch();
     }
@@ -166,21 +170,21 @@ export class Inbox extends Component<any, InboxState> {
       .ok()
       .map(a => `/feeds/inbox/${a}.xml`);
     return (
-      <div class="container">
+      <div className="container">
         {this.state.loading ? (
           <h5>
             <Spinner large />
           </h5>
         ) : (
-          <div class="row">
-            <div class="col-12">
+          <div className="row">
+            <div className="col-12">
               <HtmlTags
                 title={this.documentTitle}
                 path={this.context.router.route.match.url}
                 description={None}
                 image={None}
               />
-              <h5 class="mb-2">
+              <h5 className="mb-2">
                 {i18n.t("inbox")}
                 {inboxRss.match({
                   some: rss => (
@@ -204,7 +208,7 @@ export class Inbox extends Component<any, InboxState> {
                 0 &&
                 this.state.unreadOrAll == UnreadOrAll.Unread && (
                   <button
-                    class="btn btn-secondary mb-2"
+                    className="btn btn-secondary mb-2"
                     onClick={linkEvent(this, this.markAllAsRead)}
                   >
                     {i18n.t("mark_all_as_read")}
@@ -230,7 +234,7 @@ export class Inbox extends Component<any, InboxState> {
 
   unreadOrAllRadios() {
     return (
-      <div class="btn-group btn-group-toggle flex-wrap mb-2">
+      <div className="btn-group btn-group-toggle flex-wrap mb-2">
         <label
           className={`btn btn-outline-secondary pointer
             ${this.state.unreadOrAll == UnreadOrAll.Unread && "active"}
@@ -263,7 +267,7 @@ export class Inbox extends Component<any, InboxState> {
 
   messageTypeRadios() {
     return (
-      <div class="btn-group btn-group-toggle flex-wrap mb-2">
+      <div className="btn-group btn-group-toggle flex-wrap mb-2">
         <label
           className={`btn btn-outline-secondary pointer
             ${this.state.messageType == MessageType.All && "active"}
@@ -323,8 +327,8 @@ export class Inbox extends Component<any, InboxState> {
   selects() {
     return (
       <div className="mb-2">
-        <span class="mr-3">{this.unreadOrAllRadios()}</span>
-        <span class="mr-3">{this.messageTypeRadios()}</span>
+        <span className="mr-3">{this.unreadOrAllRadios()}</span>
+        <span className="mr-3">{this.messageTypeRadios()}</span>
         <CommentSortSelect
           sort={this.state.sort}
           onChange={this.handleSortChange}
@@ -494,16 +498,12 @@ export class Inbox extends Component<any, InboxState> {
   }
 
   handleUnreadOrAllChange(i: Inbox, event: any) {
-    i.state.unreadOrAll = Number(event.target.value);
-    i.state.page = 1;
-    i.setState(i.state);
+    i.setState({ unreadOrAll: Number(event.target.value), page: 1 });
     i.refetch();
   }
 
   handleMessageTypeChange(i: Inbox, event: any) {
-    i.state.messageType = Number(event.target.value);
-    i.state.page = 1;
-    i.setState(i.state);
+    i.setState({ messageType: Number(event.target.value), page: 1 });
     i.refetch();
   }
 
@@ -580,9 +580,7 @@ export class Inbox extends Component<any, InboxState> {
   }
 
   handleSortChange(val: CommentSortType) {
-    this.state.sort = val;
-    this.state.page = 1;
-    this.setState(this.state);
+    this.setState({ sort: val, page: 1 });
     this.refetch();
   }
 
@@ -592,10 +590,8 @@ export class Inbox extends Component<any, InboxState> {
         auth: auth().unwrap(),
       })
     );
-    i.state.replies = [];
-    i.state.mentions = [];
-    i.state.messages = [];
-    i.state.combined = i.buildCombined();
+    i.setState({ replies: [], mentions: [], messages: [] });
+    i.setState({ combined: i.buildCombined() });
     UserService.Instance.unreadInboxCountSub.next(0);
     window.scrollTo(0, 0);
     i.setState(i.state);
@@ -620,31 +616,27 @@ export class Inbox extends Component<any, InboxState> {
       this.refetch();
     } else if (op == UserOperation.GetReplies) {
       let data = wsJsonToRes<GetRepliesResponse>(msg, GetRepliesResponse);
-      this.state.replies = data.replies;
-      this.state.combined = this.buildCombined();
-      this.state.loading = false;
+      this.setState({ replies: data.replies });
+      this.setState({ combined: this.buildCombined(), loading: false });
       window.scrollTo(0, 0);
-      this.setState(this.state);
       setupTippy();
     } else if (op == UserOperation.GetPersonMentions) {
       let data = wsJsonToRes<GetPersonMentionsResponse>(
         msg,
         GetPersonMentionsResponse
       );
-      this.state.mentions = data.mentions;
-      this.state.combined = this.buildCombined();
+      this.setState({ mentions: data.mentions });
+      this.setState({ combined: this.buildCombined() });
       window.scrollTo(0, 0);
-      this.setState(this.state);
       setupTippy();
     } else if (op == UserOperation.GetPrivateMessages) {
       let data = wsJsonToRes<PrivateMessagesResponse>(
         msg,
         PrivateMessagesResponse
       );
-      this.state.messages = data.private_messages;
-      this.state.combined = this.buildCombined();
+      this.setState({ messages: data.private_messages });
+      this.setState({ combined: this.buildCombined() });
       window.scrollTo(0, 0);
-      this.setState(this.state);
       setupTippy();
     } else if (op == UserOperation.EditPrivateMessage) {
       let data = wsJsonToRes<PrivateMessageResponse>(
@@ -696,7 +688,9 @@ export class Inbox extends Component<any, InboxState> {
 
       if (found) {
         let combinedView = this.state.combined.find(
-          i => i.id == data.private_message_view.private_message.id
+          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;
@@ -706,14 +700,18 @@ export class Inbox extends Component<any, InboxState> {
           this.state.unreadOrAll == UnreadOrAll.Unread &&
           data.private_message_view.private_message.read
         ) {
-          this.state.messages = this.state.messages.filter(
-            r =>
-              r.private_message.id !==
-              data.private_message_view.private_message.id
-          );
-          this.state.combined = this.state.combined.filter(
-            r => r.id !== data.private_message_view.private_message.id
-          );
+          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;
@@ -733,7 +731,6 @@ export class Inbox extends Component<any, InboxState> {
       this.setState(this.state);
     } else if (op == UserOperation.MarkCommentReplyAsRead) {
       let data = wsJsonToRes<CommentReplyResponse>(msg, CommentReplyResponse);
-      console.log(data);
 
       let found = this.state.replies.find(
         c => c.comment_reply.id == data.comment_reply_view.comment_reply.id
@@ -741,7 +738,9 @@ export class Inbox extends Component<any, InboxState> {
 
       if (found) {
         let combinedView = this.state.combined.find(
-          i => i.id == data.comment_reply_view.comment_reply.id
+          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;
@@ -763,12 +762,17 @@ export class Inbox extends Component<any, InboxState> {
           this.state.unreadOrAll == UnreadOrAll.Unread &&
           data.comment_reply_view.comment_reply.read
         ) {
-          this.state.replies = this.state.replies.filter(
-            r => r.comment_reply.id !== data.comment_reply_view.comment_reply.id
-          );
-          this.state.combined = this.state.combined.filter(
-            r => r.id !== data.comment_reply_view.comment_reply.id
-          );
+          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;
@@ -786,7 +790,9 @@ export class Inbox extends Component<any, InboxState> {
 
       if (found) {
         let combinedView = this.state.combined.find(
-          i => i.id == data.person_mention_view.person_mention.id
+          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;
@@ -808,13 +814,18 @@ export class Inbox extends Component<any, InboxState> {
           this.state.unreadOrAll == UnreadOrAll.Unread &&
           data.person_mention_view.person_mention.read
         ) {
-          this.state.mentions = this.state.mentions.filter(
-            r =>
-              r.person_mention.id !== data.person_mention_view.person_mention.id
-          );
-          this.state.combined = this.state.combined.filter(
-            r => r.id !== data.person_mention_view.person_mention.id
-          );
+          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 =
index 8120fe97630e67d60a972b9103fdeb4d5071a7e7..42a287e6d591c43facf3d780fe48ee5c52d5bdbf 100644 (file)
@@ -66,15 +66,15 @@ export class PasswordChange extends Component<any, State> {
 
   render() {
     return (
-      <div class="container">
+      <div className="container">
         <HtmlTags
           title={this.documentTitle}
           path={this.context.router.route.match.url}
           description={None}
           image={None}
         />
-        <div class="row">
-          <div class="col-12 col-lg-6 offset-lg-3 mb-4">
+        <div className="row">
+          <div className="col-12 col-lg-6 offset-lg-3 mb-4">
             <h5>{i18n.t("password_change")}</h5>
             {this.passwordChangeForm()}
           </div>
@@ -86,41 +86,41 @@ export class PasswordChange extends Component<any, State> {
   passwordChangeForm() {
     return (
       <form onSubmit={linkEvent(this, this.handlePasswordChangeSubmit)}>
-        <div class="form-group row">
-          <label class="col-sm-2 col-form-label" htmlFor="new-password">
+        <div className="form-group row">
+          <label className="col-sm-2 col-form-label" htmlFor="new-password">
             {i18n.t("new_password")}
           </label>
-          <div class="col-sm-10">
+          <div className="col-sm-10">
             <input
               id="new-password"
               type="password"
               value={this.state.passwordChangeForm.password}
               onInput={linkEvent(this, this.handlePasswordChange)}
-              class="form-control"
+              className="form-control"
               required
               maxLength={60}
             />
           </div>
         </div>
-        <div class="form-group row">
-          <label class="col-sm-2 col-form-label" htmlFor="verify-password">
+        <div className="form-group row">
+          <label className="col-sm-2 col-form-label" htmlFor="verify-password">
             {i18n.t("verify_password")}
           </label>
-          <div class="col-sm-10">
+          <div className="col-sm-10">
             <input
               id="verify-password"
               type="password"
               value={this.state.passwordChangeForm.password_verify}
               onInput={linkEvent(this, this.handleVerifyPasswordChange)}
-              class="form-control"
+              className="form-control"
               required
               maxLength={60}
             />
           </div>
         </div>
-        <div class="form-group row">
-          <div class="col-sm-10">
-            <button type="submit" class="btn btn-secondary">
+        <div className="form-group row">
+          <div className="col-sm-10">
+            <button type="submit" className="btn btn-secondary">
               {this.state.loading ? (
                 <Spinner />
               ) : (
@@ -145,8 +145,7 @@ export class PasswordChange extends Component<any, State> {
 
   handlePasswordChangeSubmit(i: PasswordChange, event: any) {
     event.preventDefault();
-    i.state.loading = true;
-    i.setState(i.state);
+    i.setState({ loading: true });
 
     WebSocketService.Instance.send(
       wsClient.passwordChange(i.state.passwordChangeForm)
@@ -158,13 +157,11 @@ export class PasswordChange extends Component<any, State> {
     console.log(msg);
     if (msg.error) {
       toast(i18n.t(msg.error), "danger");
-      this.state.loading = false;
-      this.setState(this.state);
+      this.setState({ loading: false });
       return;
     } else if (op == UserOperation.PasswordChange) {
       let data = wsJsonToRes<LoginResponse>(msg, LoginResponse);
-      this.state = this.emptyState;
-      this.setState(this.state);
+      this.setState(this.emptyState);
       UserService.Instance.login(data);
       this.props.history.push("/");
     }
index 6ce7a8b8d0b02c3070866adfa9983b15ed58de12..8eaa5f0ac00b57c74a4b21bc1b737a8dbc19e858 100644 (file)
@@ -150,7 +150,10 @@ export class PersonDetails extends Component<PersonDetailsProps, any> {
 
     return (
       <div>
-        {combined.map(i => [this.renderItemType(i), <hr class="my-3" />])}
+        {combined.map(i => [
+          this.renderItemType(i),
+          <hr key={i.type_} className="my-3" />,
+        ])}
       </div>
     );
   }
@@ -187,7 +190,7 @@ export class PersonDetails extends Component<PersonDetailsProps, any> {
               enableDownvotes={this.props.enableDownvotes}
               enableNsfw={this.props.enableNsfw}
             />
-            <hr class="my-3" />
+            <hr className="my-3" />
           </>
         ))}
       </div>
index 9de6d0b967abe06c9380d90eb3f3ef81697d5b8b..0a8e4c09ffe138b370c062df1cc6217ceedd6895 100644 (file)
@@ -121,10 +121,11 @@ export class Profile extends Component<any, ProfileState> {
 
     // Only fetch the data if coming from another route
     if (this.isoData.path == this.context.router.route.match.url) {
-      this.state.personRes = Some(
-        this.isoData.routeData[0] as GetPersonDetailsResponse
-      );
-      this.state.loading = false;
+      this.state = {
+        ...this.state,
+        personRes: Some(this.isoData.routeData[0] as GetPersonDetailsResponse),
+        loading: false,
+      };
     } else {
       this.fetchUserData();
     }
@@ -162,11 +163,12 @@ export class Profile extends Component<any, ProfileState> {
     UserService.Instance.myUserInfo.match({
       some: mui =>
         this.state.personRes.match({
-          some: res => {
-            this.state.personBlocked = mui.person_blocks
-              .map(a => a.target.id)
-              .includes(res.person_view.person.id);
-          },
+          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,
@@ -250,7 +252,7 @@ export class Profile extends Component<any, ProfileState> {
 
   render() {
     return (
-      <div class="container">
+      <div className="container">
         {this.state.loading ? (
           <h5>
             <Spinner large />
@@ -258,8 +260,8 @@ export class Profile extends Component<any, ProfileState> {
         ) : (
           this.state.personRes.match({
             some: res => (
-              <div class="row">
-                <div class="col-12 col-md-8">
+              <div className="row">
+                <div className="col-12 col-md-8">
                   <>
                     <HtmlTags
                       title={this.documentTitle}
@@ -285,7 +287,7 @@ export class Profile extends Component<any, ProfileState> {
                 </div>
 
                 {!this.state.loading && (
-                  <div class="col-12 col-md-4">
+                  <div className="col-12 col-md-4">
                     {this.moderates()}
                     {this.amCurrentUser && this.follows()}
                   </div>
@@ -301,7 +303,7 @@ export class Profile extends Component<any, ProfileState> {
 
   viewRadios() {
     return (
-      <div class="btn-group btn-group-toggle flex-wrap mb-2">
+      <div className="btn-group btn-group-toggle flex-wrap mb-2">
         <label
           className={`btn btn-outline-secondary pointer
             ${this.state.view == PersonDetailsView.Overview && "active"}
@@ -363,7 +365,7 @@ export class Profile extends Component<any, ProfileState> {
 
     return (
       <div className="mb-2">
-        <span class="mr-3">{this.viewRadios()}</span>
+        <span className="mr-3">{this.viewRadios()}</span>
         <SortSelect
           sort={this.state.sort}
           onChange={this.handleSortChange}
@@ -406,14 +408,14 @@ export class Profile extends Component<any, ProfileState> {
               banner={pv.person.banner}
               icon={pv.person.avatar}
             />
-            <div class="mb-3">
-              <div class="">
-                <div class="mb-0 d-flex flex-wrap">
+            <div className="mb-3">
+              <div className="">
+                <div className="mb-0 d-flex flex-wrap">
                   <div>
                     {pv.person.display_name && (
-                      <h5 class="mb-0">{pv.person.display_name}</h5>
+                      <h5 className="mb-0">{pv.person.display_name}</h5>
                     )}
-                    <ul class="list-inline mb-2">
+                    <ul className="list-inline mb-2">
                       <li className="list-inline-item">
                         <PersonListing
                           person={pv.person}
@@ -531,7 +533,7 @@ export class Profile extends Component<any, ProfileState> {
                   none: <></>,
                 })}
                 <div>
-                  <ul class="list-inline mb-2">
+                  <ul className="list-inline mb-2">
                     <li className="list-inline-item badge badge-light">
                       {i18n.t("number_of_posts", {
                         count: pv.counts.post_count,
@@ -546,7 +548,7 @@ export class Profile extends Component<any, ProfileState> {
                     </li>
                   </ul>
                 </div>
-                <div class="text-muted">
+                <div className="text-muted">
                   {i18n.t("joined")}{" "}
                   <MomentTime
                     published={pv.person.published}
@@ -581,33 +583,36 @@ export class Profile extends Component<any, ProfileState> {
           <>
             {this.state.showBanDialog && (
               <form onSubmit={linkEvent(this, this.handleModBanSubmit)}>
-                <div class="form-group row col-12">
-                  <label class="col-form-label" htmlFor="profile-ban-reason">
+                <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"
-                    class="form-control mr-2"
+                    className="form-control mr-2"
                     placeholder={i18n.t("reason")}
                     value={toUndefined(this.state.banReason)}
                     onInput={linkEvent(this, this.handleModBanReasonChange)}
                   />
-                  <label class="col-form-label" htmlFor={`mod-ban-expires`}>
+                  <label className="col-form-label" htmlFor={`mod-ban-expires`}>
                     {i18n.t("expires")}
                   </label>
                   <input
                     type="number"
                     id={`mod-ban-expires`}
-                    class="form-control mr-2"
+                    className="form-control mr-2"
                     placeholder={i18n.t("number_of_days")}
                     value={toUndefined(this.state.banExpireDays)}
                     onInput={linkEvent(this, this.handleModBanExpireDaysChange)}
                   />
-                  <div class="form-group">
-                    <div class="form-check">
+                  <div className="form-group">
+                    <div className="form-check">
                       <input
-                        class="form-check-input"
+                        className="form-check-input"
                         id="mod-ban-remove-data"
                         type="checkbox"
                         checked={this.state.removeData}
@@ -617,7 +622,7 @@ export class Profile extends Component<any, ProfileState> {
                         )}
                       />
                       <label
-                        class="form-check-label"
+                        className="form-check-label"
                         htmlFor="mod-ban-remove-data"
                         title={i18n.t("remove_content_more")}
                       >
@@ -631,10 +636,10 @@ export class Profile extends Component<any, ProfileState> {
                 {/*   <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 class="form-group row">
+                <div className="form-group row">
                   <button
-                    type="cancel"
-                    class="btn btn-secondary mr-2"
+                    type="reset"
+                    className="btn btn-secondary mr-2"
                     aria-label={i18n.t("cancel")}
                     onClick={linkEvent(this, this.handleModBanSubmitCancel)}
                   >
@@ -642,7 +647,7 @@ export class Profile extends Component<any, ProfileState> {
                   </button>
                   <button
                     type="submit"
-                    class="btn btn-secondary"
+                    className="btn btn-secondary"
                     aria-label={i18n.t("ban")}
                   >
                     {i18n.t("ban")} {pv.person.name}
@@ -663,12 +668,12 @@ export class Profile extends Component<any, ProfileState> {
       .match({
         some: moderates => {
           if (moderates.length > 0) {
-            <div class="card border-secondary mb-3">
-              <div class="card-body">
+            <div className="card border-secondary mb-3">
+              <div className="card-body">
                 <h5>{i18n.t("moderates")}</h5>
-                <ul class="list-unstyled mb-0">
+                <ul className="list-unstyled mb-0">
                   {moderates.map(cmv => (
-                    <li>
+                    <li key={cmv.community.id}>
                       <CommunityLink community={cmv.community} />
                     </li>
                   ))}
@@ -687,12 +692,12 @@ export class Profile extends Component<any, ProfileState> {
       .match({
         some: follows => {
           if (follows.length > 0) {
-            <div class="card border-secondary mb-3">
-              <div class="card-body">
+            <div className="card border-secondary mb-3">
+              <div className="card-body">
                 <h5>{i18n.t("subscribed")}</h5>
-                <ul class="list-unstyled mb-0">
+                <ul className="list-unstyled mb-0">
                   {follows.map(cfv => (
-                    <li>
+                    <li key={cfv.community.id}>
                       <CommunityLink community={cfv.community} />
                     </li>
                   ))}
@@ -715,8 +720,7 @@ export class Profile extends Component<any, ProfileState> {
     this.props.history.push(
       `${typeView}/view/${viewStr}/sort/${sortStr}/page/${page}`
     );
-    this.state.loading = true;
-    this.setState(this.state);
+    this.setState({ loading: true });
     this.fetchUserData();
   }
 
@@ -736,29 +740,24 @@ export class Profile extends Component<any, ProfileState> {
   }
 
   handleModBanShow(i: Profile) {
-    i.state.showBanDialog = true;
-    i.setState(i.state);
+    i.setState({ showBanDialog: true });
   }
 
   handleModBanReasonChange(i: Profile, event: any) {
-    i.state.banReason = event.target.value;
-    i.setState(i.state);
+    i.setState({ banReason: event.target.value });
   }
 
   handleModBanExpireDaysChange(i: Profile, event: any) {
-    i.state.banExpireDays = event.target.value;
-    i.setState(i.state);
+    i.setState({ banExpireDays: event.target.value });
   }
 
   handleModRemoveDataChange(i: Profile, event: any) {
-    i.state.removeData = event.target.checked;
-    i.setState(i.state);
+    i.setState({ removeData: event.target.checked });
   }
 
   handleModBanSubmitCancel(i: Profile, event?: any) {
     event.preventDefault();
-    i.state.showBanDialog = false;
-    i.setState(i.state);
+    i.setState({ showBanDialog: false });
   }
 
   handleModBanSubmit(i: Profile, event?: any) {
@@ -771,7 +770,7 @@ export class Profile extends Component<any, ProfileState> {
           // If its an unban, restore all their data
           let ban = !person.banned;
           if (ban == false) {
-            i.state.removeData = false;
+            i.setState({ removeData: false });
           }
           let form = new BanPerson({
             person_id: person.id,
@@ -783,8 +782,7 @@ export class Profile extends Component<any, ProfileState> {
           });
           WebSocketService.Instance.send(wsClient.banPerson(form));
 
-          i.state.showBanDialog = false;
-          i.setState(i.state);
+          i.setState({ showBanDialog: false });
         },
         none: void 0,
       });
@@ -809,15 +807,12 @@ export class Profile extends Component<any, ProfileState> {
         msg,
         GetPersonDetailsResponse
       );
-      this.state.personRes = Some(data);
-      this.state.loading = false;
+      this.setState({ personRes: Some(data), loading: false });
       this.setPersonBlock();
-      this.setState(this.state);
       restoreScrollPosition(this.context);
     } else if (op == UserOperation.AddAdmin) {
       let data = wsJsonToRes<AddAdminResponse>(msg, AddAdminResponse);
-      this.state.siteRes.admins = data.admins;
-      this.setState(this.state);
+      this.setState(s => ((s.siteRes.admins = data.admins), s));
     } else if (op == UserOperation.CreateCommentLike) {
       let data = wsJsonToRes<CommentResponse>(msg, CommentResponse);
       createCommentLikeRes(
index eec90319ad8cfb1f7b3cf4e2bf47b1b7e4209678..e2ef43467d1831a64d5150358eb2492275f3414e 100644 (file)
@@ -75,10 +75,13 @@ export class RegistrationApplications extends Component<
 
     // Only fetch the data if coming from another route
     if (this.isoData.path == this.context.router.route.match.url) {
-      this.state.listRegistrationApplicationsResponse = Some(
-        this.isoData.routeData[0] as ListRegistrationApplicationsResponse
-      );
-      this.state.loading = false;
+      this.state = {
+        ...this.state,
+        listRegistrationApplicationsResponse: Some(
+          this.isoData.routeData[0] as ListRegistrationApplicationsResponse
+        ),
+        loading: false,
+      };
     } else {
       this.refetch();
     }
@@ -110,21 +113,21 @@ export class RegistrationApplications extends Component<
 
   render() {
     return (
-      <div class="container">
+      <div className="container">
         {this.state.loading ? (
           <h5>
             <Spinner large />
           </h5>
         ) : (
-          <div class="row">
-            <div class="col-12">
+          <div className="row">
+            <div className="col-12">
               <HtmlTags
                 title={this.documentTitle}
                 path={this.context.router.route.match.url}
                 description={None}
                 image={None}
               />
-              <h5 class="mb-2">{i18n.t("registration_applications")}</h5>
+              <h5 className="mb-2">{i18n.t("registration_applications")}</h5>
               {this.selects()}
               {this.applicationList()}
               <Paginator
@@ -140,7 +143,7 @@ export class RegistrationApplications extends Component<
 
   unreadOrAllRadios() {
     return (
-      <div class="btn-group btn-group-toggle flex-wrap mb-2">
+      <div className="btn-group btn-group-toggle flex-wrap mb-2">
         <label
           className={`btn btn-outline-secondary pointer
             ${this.state.unreadOrAll == UnreadOrAll.Unread && "active"}
@@ -174,7 +177,7 @@ export class RegistrationApplications extends Component<
   selects() {
     return (
       <div className="mb-2">
-        <span class="mr-3">{this.unreadOrAllRadios()}</span>
+        <span className="mr-3">{this.unreadOrAllRadios()}</span>
       </div>
     );
   }
@@ -199,9 +202,7 @@ export class RegistrationApplications extends Component<
   }
 
   handleUnreadOrAllChange(i: RegistrationApplications, event: any) {
-    i.state.unreadOrAll = Number(event.target.value);
-    i.state.page = 1;
-    i.setState(i.state);
+    i.setState({ unreadOrAll: Number(event.target.value), page: 1 });
     i.refetch();
   }
 
@@ -248,10 +249,11 @@ export class RegistrationApplications extends Component<
         msg,
         ListRegistrationApplicationsResponse
       );
-      this.state.listRegistrationApplicationsResponse = Some(data);
-      this.state.loading = false;
+      this.setState({
+        listRegistrationApplicationsResponse: Some(data),
+        loading: false,
+      });
       window.scrollTo(0, 0);
-      this.setState(this.state);
     } else if (op == UserOperation.ApproveRegistrationApplication) {
       let data = wsJsonToRes<RegistrationApplicationResponse>(
         msg,
index f8a641b556032f24166efcda16f337448220b9bd..ed1182fe2c1526727e4a5c9a5fa19ec55ced6cfa 100644 (file)
@@ -104,14 +104,20 @@ export class Reports extends Component<any, ReportsState> {
 
     // Only fetch the data if coming from another route
     if (this.isoData.path == this.context.router.route.match.url) {
-      this.state.listCommentReportsResponse = Some(
-        this.isoData.routeData[0] as ListCommentReportsResponse
-      );
-      this.state.listPostReportsResponse = Some(
-        this.isoData.routeData[1] as ListPostReportsResponse
-      );
-      this.state.combined = this.buildCombined();
-      this.state.loading = false;
+      this.state = {
+        ...this.state,
+        listCommentReportsResponse: Some(
+          this.isoData.routeData[0] as ListCommentReportsResponse
+        ),
+        listPostReportsResponse: Some(
+          this.isoData.routeData[1] as ListPostReportsResponse
+        ),
+      };
+      this.state = {
+        ...this.state,
+        combined: this.buildCombined(),
+        loading: false,
+      };
     } else {
       this.refetch();
     }
@@ -139,21 +145,21 @@ export class Reports extends Component<any, ReportsState> {
 
   render() {
     return (
-      <div class="container">
+      <div className="container">
         {this.state.loading ? (
           <h5>
             <Spinner large />
           </h5>
         ) : (
-          <div class="row">
-            <div class="col-12">
+          <div className="row">
+            <div className="col-12">
               <HtmlTags
                 title={this.documentTitle}
                 path={this.context.router.route.match.url}
                 description={None}
                 image={None}
               />
-              <h5 class="mb-2">{i18n.t("reports")}</h5>
+              <h5 className="mb-2">{i18n.t("reports")}</h5>
               {this.selects()}
               {this.state.messageType == MessageType.All && this.all()}
               {this.state.messageType == MessageType.CommentReport &&
@@ -173,7 +179,7 @@ export class Reports extends Component<any, ReportsState> {
 
   unreadOrAllRadios() {
     return (
-      <div class="btn-group btn-group-toggle flex-wrap mb-2">
+      <div className="btn-group btn-group-toggle flex-wrap mb-2">
         <label
           className={`btn btn-outline-secondary pointer
             ${this.state.unreadOrAll == UnreadOrAll.Unread && "active"}
@@ -206,7 +212,7 @@ export class Reports extends Component<any, ReportsState> {
 
   messageTypeRadios() {
     return (
-      <div class="btn-group btn-group-toggle flex-wrap mb-2">
+      <div className="btn-group btn-group-toggle flex-wrap mb-2">
         <label
           className={`btn btn-outline-secondary pointer
             ${this.state.messageType == MessageType.All && "active"}
@@ -253,8 +259,8 @@ export class Reports extends Component<any, ReportsState> {
   selects() {
     return (
       <div className="mb-2">
-        <span class="mr-3">{this.unreadOrAllRadios()}</span>
-        <span class="mr-3">{this.messageTypeRadios()}</span>
+        <span className="mr-3">{this.unreadOrAllRadios()}</span>
+        <span className="mr-3">{this.messageTypeRadios()}</span>
       </div>
     );
   }
@@ -356,16 +362,12 @@ export class Reports extends Component<any, ReportsState> {
   }
 
   handleUnreadOrAllChange(i: Reports, event: any) {
-    i.state.unreadOrAll = Number(event.target.value);
-    i.state.page = 1;
-    i.setState(i.state);
+    i.setState({ unreadOrAll: Number(event.target.value), page: 1 });
     i.refetch();
   }
 
   handleMessageTypeChange(i: Reports, event: any) {
-    i.state.messageType = Number(event.target.value);
-    i.state.page = 1;
-    i.setState(i.state);
+    i.setState({ messageType: Number(event.target.value), page: 1 });
     i.refetch();
   }
 
@@ -443,24 +445,20 @@ export class Reports extends Component<any, ReportsState> {
         msg,
         ListCommentReportsResponse
       );
-      this.state.listCommentReportsResponse = Some(data);
-      this.state.combined = this.buildCombined();
-      this.state.loading = false;
+      this.setState({ listCommentReportsResponse: Some(data) });
+      this.setState({ combined: this.buildCombined(), loading: false });
       // this.sendUnreadCount();
       window.scrollTo(0, 0);
-      this.setState(this.state);
       setupTippy();
     } else if (op == UserOperation.ListPostReports) {
       let data = wsJsonToRes<ListPostReportsResponse>(
         msg,
         ListPostReportsResponse
       );
-      this.state.listPostReportsResponse = Some(data);
-      this.state.combined = this.buildCombined();
-      this.state.loading = false;
+      this.setState({ listPostReportsResponse: Some(data) });
+      this.setState({ combined: this.buildCombined(), loading: false });
       // this.sendUnreadCount();
       window.scrollTo(0, 0);
-      this.setState(this.state);
       setupTippy();
     } else if (op == UserOperation.ResolvePostReport) {
       let data = wsJsonToRes<PostReportResponse>(msg, PostReportResponse);
index 532d92204ab9ae624a24e586b7d1a35a5cff1bc2..838c1ec1081b415d26d49f12a54009d914f7535e 100644 (file)
@@ -150,13 +150,43 @@ export class Settings extends Component<any, SettingsState> {
     this.parseMessage = this.parseMessage.bind(this);
     this.subscription = wsSubscribe(this.parseMessage);
 
-    this.setUserInfo();
+    if (UserService.Instance.myUserInfo.isSome()) {
+      let mui = UserService.Instance.myUserInfo.unwrap();
+      let luv = mui.local_user_view;
+      this.state = {
+        ...this.state,
+        personBlocks: mui.person_blocks,
+        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),
+          lang: Some(luv.local_user.lang),
+          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),
+          email: luv.local_user.email,
+          bio: luv.person.bio,
+          send_notifications_to_email: Some(
+            luv.local_user.send_notifications_to_email
+          ),
+          matrix_user_id: luv.person.matrix_user_id,
+        },
+      };
+    }
   }
 
   async componentDidMount() {
     setupTippy();
-    this.state.themeList = await fetchThemeList();
-    this.setState(this.state);
+    this.setState({ themeList: await fetchThemeList() });
   }
 
   componentWillUnmount() {
@@ -169,7 +199,7 @@ export class Settings extends Component<any, SettingsState> {
 
   render() {
     return (
-      <div class="container">
+      <div className="container">
         <>
           <HtmlTags
             title={this.documentTitle}
@@ -177,10 +207,10 @@ export class Settings extends Component<any, SettingsState> {
             description={Some(this.documentTitle)}
             image={this.state.saveUserSettingsForm.avatar}
           />
-          <ul class="nav nav-tabs mb-2">
-            <li class="nav-item">
+          <ul className="nav nav-tabs mb-2">
+            <li className="nav-item">
               <button
-                class={`nav-link btn ${
+                className={`nav-link btn ${
                   this.state.currentTab == "settings" && "active"
                 }`}
                 onClick={linkEvent(
@@ -191,9 +221,9 @@ export class Settings extends Component<any, SettingsState> {
                 {i18n.t("settings")}
               </button>
             </li>
-            <li class="nav-item">
+            <li className="nav-item">
               <button
-                class={`nav-link btn ${
+                className={`nav-link btn ${
                   this.state.currentTab == "blocks" && "active"
                 }`}
                 onClick={linkEvent(
@@ -214,15 +244,15 @@ export class Settings extends Component<any, SettingsState> {
 
   userSettings() {
     return (
-      <div class="row">
-        <div class="col-12 col-md-6">
-          <div class="card border-secondary mb-3">
-            <div class="card-body">{this.saveUserSettingsHtmlForm()}</div>
+      <div className="row">
+        <div className="col-12 col-md-6">
+          <div className="card border-secondary mb-3">
+            <div className="card-body">{this.saveUserSettingsHtmlForm()}</div>
           </div>
         </div>
-        <div class="col-12 col-md-6">
-          <div class="card border-secondary mb-3">
-            <div class="card-body">{this.changePasswordHtmlForm()}</div>
+        <div className="col-12 col-md-6">
+          <div className="card border-secondary mb-3">
+            <div className="card-body">{this.changePasswordHtmlForm()}</div>
           </div>
         </div>
       </div>
@@ -231,15 +261,15 @@ export class Settings extends Component<any, SettingsState> {
 
   blockCards() {
     return (
-      <div class="row">
-        <div class="col-12 col-md-6">
-          <div class="card border-secondary mb-3">
-            <div class="card-body">{this.blockUserCard()}</div>
+      <div className="row">
+        <div className="col-12 col-md-6">
+          <div className="card border-secondary mb-3">
+            <div className="card-body">{this.blockUserCard()}</div>
           </div>
         </div>
-        <div class="col-12 col-md-6">
-          <div class="card border-secondary mb-3">
-            <div class="card-body">{this.blockCommunityCard()}</div>
+        <div className="col-12 col-md-6">
+          <div className="card border-secondary mb-3">
+            <div className="card-body">{this.blockCommunityCard()}</div>
           </div>
         </div>
       </div>
@@ -251,15 +281,15 @@ export class Settings extends Component<any, SettingsState> {
       <>
         <h5>{i18n.t("change_password")}</h5>
         <form onSubmit={linkEvent(this, this.handleChangePasswordSubmit)}>
-          <div class="form-group row">
-            <label class="col-sm-5 col-form-label" htmlFor="user-password">
+          <div className="form-group row">
+            <label className="col-sm-5 col-form-label" htmlFor="user-password">
               {i18n.t("new_password")}
             </label>
-            <div class="col-sm-7">
+            <div className="col-sm-7">
               <input
                 type="password"
                 id="user-password"
-                class="form-control"
+                className="form-control"
                 value={this.state.changePasswordForm.new_password}
                 autoComplete="new-password"
                 maxLength={60}
@@ -267,18 +297,18 @@ export class Settings extends Component<any, SettingsState> {
               />
             </div>
           </div>
-          <div class="form-group row">
+          <div className="form-group row">
             <label
-              class="col-sm-5 col-form-label"
+              className="col-sm-5 col-form-label"
               htmlFor="user-verify-password"
             >
               {i18n.t("verify_password")}
             </label>
-            <div class="col-sm-7">
+            <div className="col-sm-7">
               <input
                 type="password"
                 id="user-verify-password"
-                class="form-control"
+                className="form-control"
                 value={this.state.changePasswordForm.new_password_verify}
                 autoComplete="new-password"
                 maxLength={60}
@@ -286,15 +316,18 @@ export class Settings extends Component<any, SettingsState> {
               />
             </div>
           </div>
-          <div class="form-group row">
-            <label class="col-sm-5 col-form-label" htmlFor="user-old-password">
+          <div className="form-group row">
+            <label
+              className="col-sm-5 col-form-label"
+              htmlFor="user-old-password"
+            >
               {i18n.t("old_password")}
             </label>
-            <div class="col-sm-7">
+            <div className="col-sm-7">
               <input
                 type="password"
                 id="user-old-password"
-                class="form-control"
+                className="form-control"
                 value={this.state.changePasswordForm.old_password}
                 autoComplete="new-password"
                 maxLength={60}
@@ -302,8 +335,8 @@ export class Settings extends Component<any, SettingsState> {
               />
             </div>
           </div>
-          <div class="form-group">
-            <button type="submit" class="btn btn-block btn-secondary mr-4">
+          <div className="form-group">
+            <button type="submit" className="btn btn-block btn-secondary mr-4">
               {this.state.changePasswordLoading ? (
                 <Spinner />
               ) : (
@@ -329,9 +362,9 @@ export class Settings extends Component<any, SettingsState> {
     return (
       <>
         <h5>{i18n.t("blocked_users")}</h5>
-        <ul class="list-unstyled mb-0">
+        <ul className="list-unstyled mb-0">
           {this.state.personBlocks.map(pb => (
-            <li>
+            <li key={pb.target.id}>
               <span>
                 <PersonListing person={pb.target} />
                 <button
@@ -354,13 +387,16 @@ export class Settings extends Component<any, SettingsState> {
 
   blockUserForm() {
     return (
-      <div class="form-group row">
-        <label class="col-md-4 col-form-label" htmlFor="block-person-filter">
+      <div className="form-group row">
+        <label
+          className="col-md-4 col-form-label"
+          htmlFor="block-person-filter"
+        >
           {i18n.t("block_user")}
         </label>
-        <div class="col-md-8">
+        <div className="col-md-8">
           <select
-            class="form-control"
+            className="form-control"
             id="block-person-filter"
             value={this.state.blockPerson.map(p => p.person.id).unwrapOr(0)}
           >
@@ -392,9 +428,9 @@ export class Settings extends Component<any, SettingsState> {
     return (
       <>
         <h5>{i18n.t("blocked_communities")}</h5>
-        <ul class="list-unstyled mb-0">
+        <ul className="list-unstyled mb-0">
           {this.state.communityBlocks.map(cb => (
-            <li>
+            <li key={cb.community.id}>
               <span>
                 <CommunityLink community={cb.community} />
                 <button
@@ -417,13 +453,16 @@ export class Settings extends Component<any, SettingsState> {
 
   blockCommunityForm() {
     return (
-      <div class="form-group row">
-        <label class="col-md-4 col-form-label" htmlFor="block-community-filter">
+      <div className="form-group row">
+        <label
+          className="col-md-4 col-form-label"
+          htmlFor="block-community-filter"
+        >
           {i18n.t("block_community")}
         </label>
-        <div class="col-md-8">
+        <div className="col-md-8">
           <select
-            class="form-control"
+            className="form-control"
             id="block-community-filter"
             value={this.state.blockCommunityId}
           >
@@ -444,15 +483,15 @@ export class Settings extends Component<any, SettingsState> {
       <>
         <h5>{i18n.t("settings")}</h5>
         <form onSubmit={linkEvent(this, this.handleSaveSettingsSubmit)}>
-          <div class="form-group row">
-            <label class="col-sm-5 col-form-label" htmlFor="display-name">
+          <div className="form-group row">
+            <label className="col-sm-5 col-form-label" htmlFor="display-name">
               {i18n.t("display_name")}
             </label>
-            <div class="col-sm-7">
+            <div className="col-sm-7">
               <input
                 id="display-name"
                 type="text"
-                class="form-control"
+                className="form-control"
                 placeholder={i18n.t("optional")}
                 value={toUndefined(
                   this.state.saveUserSettingsForm.display_name
@@ -463,11 +502,11 @@ export class Settings extends Component<any, SettingsState> {
               />
             </div>
           </div>
-          <div class="form-group row">
-            <label class="col-sm-3 col-form-label" htmlFor="user-bio">
+          <div className="form-group row">
+            <label className="col-sm-3 col-form-label" htmlFor="user-bio">
               {i18n.t("bio")}
             </label>
-            <div class="col-sm-9">
+            <div className="col-sm-9">
               <MarkdownTextArea
                 initialContent={this.state.saveUserSettingsForm.bio}
                 onContentChange={this.handleBioChange}
@@ -478,15 +517,15 @@ export class Settings extends Component<any, SettingsState> {
               />
             </div>
           </div>
-          <div class="form-group row">
-            <label class="col-sm-3 col-form-label" htmlFor="user-email">
+          <div className="form-group row">
+            <label className="col-sm-3 col-form-label" htmlFor="user-email">
               {i18n.t("email")}
             </label>
-            <div class="col-sm-9">
+            <div className="col-sm-9">
               <input
                 type="email"
                 id="user-email"
-                class="form-control"
+                className="form-control"
                 placeholder={i18n.t("optional")}
                 value={toUndefined(this.state.saveUserSettingsForm.email)}
                 onInput={linkEvent(this, this.handleEmailChange)}
@@ -494,17 +533,17 @@ export class Settings extends Component<any, SettingsState> {
               />
             </div>
           </div>
-          <div class="form-group row">
-            <label class="col-sm-5 col-form-label" htmlFor="matrix-user-id">
+          <div className="form-group row">
+            <label className="col-sm-5 col-form-label" htmlFor="matrix-user-id">
               <a href={elementUrl} rel={relTags}>
                 {i18n.t("matrix_user_id")}
               </a>
             </label>
-            <div class="col-sm-7">
+            <div className="col-sm-7">
               <input
                 id="matrix-user-id"
                 type="text"
-                class="form-control"
+                className="form-control"
                 placeholder="@user:example.com"
                 value={toUndefined(
                   this.state.saveUserSettingsForm.matrix_user_id
@@ -514,9 +553,9 @@ export class Settings extends Component<any, SettingsState> {
               />
             </div>
           </div>
-          <div class="form-group row">
-            <label class="col-sm-3">{i18n.t("avatar")}</label>
-            <div class="col-sm-9">
+          <div className="form-group row">
+            <label className="col-sm-3">{i18n.t("avatar")}</label>
+            <div className="col-sm-9">
               <ImageUploadForm
                 uploadTitle={i18n.t("upload_avatar")}
                 imageSrc={this.state.saveUserSettingsForm.avatar}
@@ -526,9 +565,9 @@ export class Settings extends Component<any, SettingsState> {
               />
             </div>
           </div>
-          <div class="form-group row">
-            <label class="col-sm-3">{i18n.t("banner")}</label>
-            <div class="col-sm-9">
+          <div className="form-group row">
+            <label className="col-sm-3">{i18n.t("banner")}</label>
+            <div className="col-sm-9">
               <ImageUploadForm
                 uploadTitle={i18n.t("upload_banner")}
                 imageSrc={this.state.saveUserSettingsForm.banner}
@@ -537,16 +576,16 @@ export class Settings extends Component<any, SettingsState> {
               />
             </div>
           </div>
-          <div class="form-group row">
-            <label class="col-sm-3" htmlFor="user-language">
+          <div className="form-group row">
+            <label className="col-sm-3" htmlFor="user-language">
               {i18n.t("language")}
             </label>
-            <div class="col-sm-9">
+            <div className="col-sm-9">
               <select
                 id="user-language"
                 value={toUndefined(this.state.saveUserSettingsForm.lang)}
                 onChange={linkEvent(this, this.handleLangChange)}
-                class="custom-select w-auto"
+                className="custom-select w-auto"
               >
                 <option disabled aria-hidden="true">
                   {i18n.t("language")}
@@ -558,35 +597,39 @@ export class Settings extends Component<any, SettingsState> {
                 {languages
                   .sort((a, b) => a.code.localeCompare(b.code))
                   .map(lang => (
-                    <option value={lang.code}>{lang.name}</option>
+                    <option key={lang.code} value={lang.code}>
+                      {lang.name}
+                    </option>
                   ))}
               </select>
             </div>
           </div>
-          <div class="form-group row">
-            <label class="col-sm-3" htmlFor="user-theme">
+          <div className="form-group row">
+            <label className="col-sm-3" htmlFor="user-theme">
               {i18n.t("theme")}
             </label>
-            <div class="col-sm-9">
+            <div className="col-sm-9">
               <select
                 id="user-theme"
                 value={toUndefined(this.state.saveUserSettingsForm.theme)}
                 onChange={linkEvent(this, this.handleThemeChange)}
-                class="custom-select w-auto"
+                className="custom-select w-auto"
               >
                 <option disabled aria-hidden="true">
                   {i18n.t("theme")}
                 </option>
                 <option value="browser">{i18n.t("browser_default")}</option>
                 {this.state.themeList.map(theme => (
-                  <option value={theme}>{theme}</option>
+                  <option key={theme} value={theme}>
+                    {theme}
+                  </option>
                 ))}
               </select>
             </div>
           </div>
           <form className="form-group row">
-            <label class="col-sm-3">{i18n.t("type")}</label>
-            <div class="col-sm-9">
+            <label className="col-sm-3">{i18n.t("type")}</label>
+            <div className="col-sm-9">
               <ListingTypeSelect
                 type_={
                   Object.values(ListingType)[
@@ -602,8 +645,8 @@ export class Settings extends Component<any, SettingsState> {
             </div>
           </form>
           <form className="form-group row">
-            <label class="col-sm-3">{i18n.t("sort_type")}</label>
-            <div class="col-sm-9">
+            <label className="col-sm-3">{i18n.t("sort_type")}</label>
+            <div className="col-sm-9">
               <SortSelect
                 sort={
                   Object.values(SortType)[
@@ -617,10 +660,10 @@ export class Settings extends Component<any, SettingsState> {
             </div>
           </form>
           {enableNsfw(this.state.siteRes) && (
-            <div class="form-group">
-              <div class="form-check">
+            <div className="form-group">
+              <div className="form-check">
                 <input
-                  class="form-check-input"
+                  className="form-check-input"
                   id="user-show-nsfw"
                   type="checkbox"
                   checked={toUndefined(
@@ -628,16 +671,16 @@ export class Settings extends Component<any, SettingsState> {
                   )}
                   onChange={linkEvent(this, this.handleShowNsfwChange)}
                 />
-                <label class="form-check-label" htmlFor="user-show-nsfw">
+                <label className="form-check-label" htmlFor="user-show-nsfw">
                   {i18n.t("show_nsfw")}
                 </label>
               </div>
             </div>
           )}
-          <div class="form-group">
-            <div class="form-check">
+          <div className="form-group">
+            <div className="form-check">
               <input
-                class="form-check-input"
+                className="form-check-input"
                 id="user-show-scores"
                 type="checkbox"
                 checked={toUndefined(
@@ -645,15 +688,15 @@ export class Settings extends Component<any, SettingsState> {
                 )}
                 onChange={linkEvent(this, this.handleShowScoresChange)}
               />
-              <label class="form-check-label" htmlFor="user-show-scores">
+              <label className="form-check-label" htmlFor="user-show-scores">
                 {i18n.t("show_scores")}
               </label>
             </div>
           </div>
-          <div class="form-group">
-            <div class="form-check">
+          <div className="form-group">
+            <div className="form-check">
               <input
-                class="form-check-input"
+                className="form-check-input"
                 id="user-show-avatars"
                 type="checkbox"
                 checked={toUndefined(
@@ -661,15 +704,15 @@ export class Settings extends Component<any, SettingsState> {
                 )}
                 onChange={linkEvent(this, this.handleShowAvatarsChange)}
               />
-              <label class="form-check-label" htmlFor="user-show-avatars">
+              <label className="form-check-label" htmlFor="user-show-avatars">
                 {i18n.t("show_avatars")}
               </label>
             </div>
           </div>
-          <div class="form-group">
-            <div class="form-check">
+          <div className="form-group">
+            <div className="form-check">
               <input
-                class="form-check-input"
+                className="form-check-input"
                 id="user-bot-account"
                 type="checkbox"
                 checked={toUndefined(
@@ -677,15 +720,15 @@ export class Settings extends Component<any, SettingsState> {
                 )}
                 onChange={linkEvent(this, this.handleBotAccount)}
               />
-              <label class="form-check-label" htmlFor="user-bot-account">
+              <label className="form-check-label" htmlFor="user-bot-account">
                 {i18n.t("bot_account")}
               </label>
             </div>
           </div>
-          <div class="form-group">
-            <div class="form-check">
+          <div className="form-group">
+            <div className="form-check">
               <input
-                class="form-check-input"
+                className="form-check-input"
                 id="user-show-bot-accounts"
                 type="checkbox"
                 checked={toUndefined(
@@ -693,15 +736,18 @@ export class Settings extends Component<any, SettingsState> {
                 )}
                 onChange={linkEvent(this, this.handleShowBotAccounts)}
               />
-              <label class="form-check-label" htmlFor="user-show-bot-accounts">
+              <label
+                className="form-check-label"
+                htmlFor="user-show-bot-accounts"
+              >
                 {i18n.t("show_bot_accounts")}
               </label>
             </div>
           </div>
-          <div class="form-group">
-            <div class="form-check">
+          <div className="form-group">
+            <div className="form-check">
               <input
-                class="form-check-input"
+                className="form-check-input"
                 id="user-show-read-posts"
                 type="checkbox"
                 checked={toUndefined(
@@ -709,15 +755,18 @@ export class Settings extends Component<any, SettingsState> {
                 )}
                 onChange={linkEvent(this, this.handleReadPosts)}
               />
-              <label class="form-check-label" htmlFor="user-show-read-posts">
+              <label
+                className="form-check-label"
+                htmlFor="user-show-read-posts"
+              >
                 {i18n.t("show_read_posts")}
               </label>
             </div>
           </div>
-          <div class="form-group">
-            <div class="form-check">
+          <div className="form-group">
+            <div className="form-check">
               <input
-                class="form-check-input"
+                className="form-check-input"
                 id="user-show-new-post-notifs"
                 type="checkbox"
                 checked={toUndefined(
@@ -726,17 +775,17 @@ export class Settings extends Component<any, SettingsState> {
                 onChange={linkEvent(this, this.handleShowNewPostNotifs)}
               />
               <label
-                class="form-check-label"
+                className="form-check-label"
                 htmlFor="user-show-new-post-notifs"
               >
                 {i18n.t("show_new_post_notifs")}
               </label>
             </div>
           </div>
-          <div class="form-group">
-            <div class="form-check">
+          <div className="form-group">
+            <div className="form-check">
               <input
-                class="form-check-input"
+                className="form-check-input"
                 id="user-send-notifications-to-email"
                 type="checkbox"
                 disabled={!this.state.saveUserSettingsForm.email}
@@ -749,15 +798,15 @@ export class Settings extends Component<any, SettingsState> {
                 )}
               />
               <label
-                class="form-check-label"
+                className="form-check-label"
                 htmlFor="user-send-notifications-to-email"
               >
                 {i18n.t("send_notifications_to_email")}
               </label>
             </div>
           </div>
-          <div class="form-group">
-            <button type="submit" class="btn btn-block btn-secondary mr-4">
+          <div className="form-group">
+            <button type="submit" className="btn btn-block btn-secondary mr-4">
               {this.state.saveUserSettingsLoading ? (
                 <Spinner />
               ) : (
@@ -766,9 +815,9 @@ export class Settings extends Component<any, SettingsState> {
             </button>
           </div>
           <hr />
-          <div class="form-group">
+          <div className="form-group">
             <button
-              class="btn btn-block btn-danger"
+              className="btn btn-block btn-danger"
               onClick={linkEvent(
                 this,
                 this.handleDeleteAccountShowConfirmToggle
@@ -778,7 +827,7 @@ export class Settings extends Component<any, SettingsState> {
             </button>
             {this.state.deleteAccountShowConfirm && (
               <>
-                <div class="my-2 alert alert-danger" role="alert">
+                <div className="my-2 alert alert-danger" role="alert">
                   {i18n.t("delete_account_confirm")}
                 </div>
                 <input
@@ -790,10 +839,10 @@ export class Settings extends Component<any, SettingsState> {
                     this,
                     this.handleDeleteAccountPasswordChange
                   )}
-                  class="form-control my-2"
+                  className="form-control my-2"
                 />
                 <button
-                  class="btn btn-danger mr-4"
+                  className="btn btn-danger mr-4"
                   disabled={!this.state.deleteAccountForm.password}
                   onClick={linkEvent(this, this.handleDeleteAccount)}
                 >
@@ -804,7 +853,7 @@ export class Settings extends Component<any, SettingsState> {
                   )}
                 </button>
                 <button
-                  class="btn btn-secondary"
+                  className="btn btn-secondary"
                   onClick={linkEvent(
                     this,
                     this.handleDeleteAccountShowConfirmToggle
@@ -1000,17 +1049,25 @@ export class Settings extends Component<any, SettingsState> {
   }
 
   handleSortTypeChange(val: SortType) {
-    this.state.saveUserSettingsForm.default_sort_type = Some(
-      Object.keys(SortType).indexOf(val)
+    this.setState(
+      s => (
+        (s.saveUserSettingsForm.default_sort_type = Some(
+          Object.keys(SortType).indexOf(val)
+        )),
+        s
+      )
     );
-    this.setState(this.state);
   }
 
   handleListingTypeChange(val: ListingType) {
-    this.state.saveUserSettingsForm.default_listing_type = Some(
-      Object.keys(ListingType).indexOf(val)
+    this.setState(
+      s => (
+        (s.saveUserSettingsForm.default_listing_type = Some(
+          Object.keys(ListingType).indexOf(val)
+        )),
+        s
+      )
     );
-    this.setState(this.state);
   }
 
   handleEmailChange(i: Settings, event: any) {
@@ -1019,28 +1076,23 @@ export class Settings extends Component<any, SettingsState> {
   }
 
   handleBioChange(val: string) {
-    this.state.saveUserSettingsForm.bio = Some(val);
-    this.setState(this.state);
+    this.setState(s => ((s.saveUserSettingsForm.bio = Some(val)), s));
   }
 
   handleAvatarUpload(url: string) {
-    this.state.saveUserSettingsForm.avatar = Some(url);
-    this.setState(this.state);
+    this.setState(s => ((s.saveUserSettingsForm.avatar = Some(url)), s));
   }
 
   handleAvatarRemove() {
-    this.state.saveUserSettingsForm.avatar = Some("");
-    this.setState(this.state);
+    this.setState(s => ((s.saveUserSettingsForm.avatar = Some("")), s));
   }
 
   handleBannerUpload(url: string) {
-    this.state.saveUserSettingsForm.banner = Some(url);
-    this.setState(this.state);
+    this.setState(s => ((s.saveUserSettingsForm.banner = Some(url)), s));
   }
 
   handleBannerRemove() {
-    this.state.saveUserSettingsForm.banner = Some("");
-    this.setState(this.state);
+    this.setState(s => ((s.saveUserSettingsForm.banner = Some("")), s));
   }
 
   handleDisplayNameChange(i: Settings, event: any) {
@@ -1079,30 +1131,26 @@ export class Settings extends Component<any, SettingsState> {
 
   handleSaveSettingsSubmit(i: Settings, event: any) {
     event.preventDefault();
-    i.state.saveUserSettingsLoading = true;
-    i.state.saveUserSettingsForm.auth = auth().unwrap();
-    i.setState(i.state);
+    i.setState({ saveUserSettingsLoading: true });
+    i.setState(s => ((s.saveUserSettingsForm.auth = auth().unwrap()), s));
 
-    WebSocketService.Instance.send(
-      wsClient.saveUserSettings(i.state.saveUserSettingsForm)
-    );
+    let form = new SaveUserSettings({ ...i.state.saveUserSettingsForm });
+    WebSocketService.Instance.send(wsClient.saveUserSettings(form));
   }
 
   handleChangePasswordSubmit(i: Settings, event: any) {
     event.preventDefault();
-    i.state.changePasswordLoading = true;
-    i.state.changePasswordForm.auth = auth().unwrap();
-    i.setState(i.state);
+    i.setState({ changePasswordLoading: true });
+    i.setState(s => ((s.changePasswordForm.auth = auth().unwrap()), s));
 
-    WebSocketService.Instance.send(
-      wsClient.changePassword(i.state.changePasswordForm)
-    );
+    let form = new ChangePassword({ ...i.state.changePasswordForm });
+
+    WebSocketService.Instance.send(wsClient.changePassword(form));
   }
 
   handleDeleteAccountShowConfirmToggle(i: Settings, event: any) {
     event.preventDefault();
-    i.state.deleteAccountShowConfirm = !i.state.deleteAccountShowConfirm;
-    i.setState(i.state);
+    i.setState({ deleteAccountShowConfirm: !i.state.deleteAccountShowConfirm });
   }
 
   handleDeleteAccountPasswordChange(i: Settings, event: any) {
@@ -1112,13 +1160,12 @@ export class Settings extends Component<any, SettingsState> {
 
   handleDeleteAccount(i: Settings, event: any) {
     event.preventDefault();
-    i.state.deleteAccountLoading = true;
-    i.state.deleteAccountForm.auth = auth().unwrap();
-    i.setState(i.state);
+    i.setState({ deleteAccountLoading: true });
+    i.setState(s => ((s.deleteAccountForm.auth = auth().unwrap()), s));
 
-    WebSocketService.Instance.send(
-      wsClient.deleteAccount(i.state.deleteAccountForm)
-    );
+    let form = new DeleteAccount({ ...i.state.deleteAccountForm });
+
+    WebSocketService.Instance.send(wsClient.deleteAccount(form));
   }
 
   handleSwitchTab(i: { ctx: Settings; tab: string }) {
@@ -1130,58 +1177,6 @@ export class Settings extends Component<any, SettingsState> {
     }
   }
 
-  setUserInfo() {
-    UserService.Instance.myUserInfo.match({
-      some: mui => {
-        let luv = mui.local_user_view;
-        this.state.saveUserSettingsForm.show_nsfw = Some(
-          luv.local_user.show_nsfw
-        );
-        this.state.saveUserSettingsForm.theme = Some(
-          luv.local_user.theme ? luv.local_user.theme : "browser"
-        );
-        this.state.saveUserSettingsForm.default_sort_type = Some(
-          luv.local_user.default_sort_type
-        );
-        this.state.saveUserSettingsForm.default_listing_type = Some(
-          luv.local_user.default_listing_type
-        );
-        this.state.saveUserSettingsForm.lang = Some(luv.local_user.lang);
-        this.state.saveUserSettingsForm.avatar = luv.person.avatar;
-        this.state.saveUserSettingsForm.banner = luv.person.banner;
-        this.state.saveUserSettingsForm.display_name = luv.person.display_name;
-        this.state.saveUserSettingsForm.show_avatars = Some(
-          luv.local_user.show_avatars
-        );
-        this.state.saveUserSettingsForm.bot_account = Some(
-          luv.person.bot_account
-        );
-        this.state.saveUserSettingsForm.show_bot_accounts = Some(
-          luv.local_user.show_bot_accounts
-        );
-        this.state.saveUserSettingsForm.show_scores = Some(
-          luv.local_user.show_scores
-        );
-        this.state.saveUserSettingsForm.show_read_posts = Some(
-          luv.local_user.show_read_posts
-        );
-        this.state.saveUserSettingsForm.show_new_post_notifs = Some(
-          luv.local_user.show_new_post_notifs
-        );
-        this.state.saveUserSettingsForm.email = luv.local_user.email;
-        this.state.saveUserSettingsForm.bio = luv.person.bio;
-        this.state.saveUserSettingsForm.send_notifications_to_email = Some(
-          luv.local_user.send_notifications_to_email
-        );
-        this.state.saveUserSettingsForm.matrix_user_id =
-          luv.person.matrix_user_id;
-        this.state.personBlocks = mui.person_blocks;
-        this.state.communityBlocks = mui.community_blocks;
-      },
-      none: void 0,
-    });
-  }
-
   parseMessage(msg: any) {
     let op = wsUserOp(msg);
     console.log(msg);
@@ -1196,15 +1191,13 @@ export class Settings extends Component<any, SettingsState> {
     } else if (op == UserOperation.SaveUserSettings) {
       let data = wsJsonToRes<LoginResponse>(msg, LoginResponse);
       UserService.Instance.login(data);
-      this.state.saveUserSettingsLoading = false;
-      this.setState(this.state);
+      this.setState({ saveUserSettingsLoading: false });
       toast(i18n.t("saved"));
       window.scrollTo(0, 0);
     } else if (op == UserOperation.ChangePassword) {
       let data = wsJsonToRes<LoginResponse>(msg, LoginResponse);
       UserService.Instance.login(data);
-      this.state.changePasswordLoading = false;
-      this.setState(this.state);
+      this.setState({ changePasswordLoading: false });
       window.scrollTo(0, 0);
       toast(i18n.t("password_changed"));
     } else if (op == UserOperation.DeleteAccount) {
index fed026fdff2419aa6829623012653bc6e249bb10..e5389584d41e045d44033537542385863fc2e080 100644 (file)
@@ -66,15 +66,15 @@ export class VerifyEmail extends Component<any, State> {
 
   render() {
     return (
-      <div class="container">
+      <div className="container">
         <HtmlTags
           title={this.documentTitle}
           path={this.context.router.route.match.url}
           description={None}
           image={None}
         />
-        <div class="row">
-          <div class="col-12 col-lg-6 offset-lg-3 mb-4">
+        <div className="row">
+          <div className="col-12 col-lg-6 offset-lg-3 mb-4">
             <h5>{i18n.t("verify_email")}</h5>
           </div>
         </div>
@@ -94,8 +94,7 @@ export class VerifyEmail extends Component<any, State> {
       let data = wsJsonToRes<VerifyEmailResponse>(msg, VerifyEmailResponse);
       if (data) {
         toast(i18n.t("email_verified"));
-        this.state = this.emptyState;
-        this.setState(this.state);
+        this.setState(this.emptyState);
         this.props.history.push("/login");
       }
     }
index 68d546e2e376f5009b81095d1bf0d33f82ef2787..6bb2f4d28df97f02ace4f1754538a5789b52a788 100644 (file)
@@ -50,23 +50,27 @@ export class CreatePost extends Component<any, CreatePostState> {
 
   constructor(props: any, context: any) {
     super(props, context);
-    this.handlePostCreate = this.handlePostCreate.bind(this);
     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()) {
       toast(i18n.t("not_logged_in"), "danger");
       this.context.router.history.push(`/login`);
     }
 
-    this.parseMessage = this.parseMessage.bind(this);
-    this.subscription = wsSubscribe(this.parseMessage);
-
     // Only fetch the data if coming from another route
     if (this.isoData.path == this.context.router.route.match.url) {
-      this.state.listCommunitiesResponse = Some(
-        this.isoData.routeData[0] as ListCommunitiesResponse
-      );
-      this.state.loading = false;
+      this.state = {
+        ...this.state,
+        listCommunitiesResponse: Some(
+          this.isoData.routeData[0] as ListCommunitiesResponse
+        ),
+        loading: false,
+      };
     } else {
       this.refetch();
     }
@@ -123,7 +127,7 @@ export class CreatePost extends Component<any, CreatePostState> {
 
   render() {
     return (
-      <div class="container">
+      <div className="container">
         <HtmlTags
           title={this.documentTitle}
           path={this.context.router.route.match.url}
@@ -137,8 +141,8 @@ export class CreatePost extends Component<any, CreatePostState> {
         ) : (
           this.state.listCommunitiesResponse.match({
             some: res => (
-              <div class="row">
-                <div class="col-12 col-lg-6 offset-lg-3 mb-4">
+              <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}
@@ -230,16 +234,15 @@ export class CreatePost extends Component<any, CreatePostState> {
         msg,
         ListCommunitiesResponse
       );
-      this.state.listCommunitiesResponse = Some(data);
-      this.state.loading = false;
-      this.setState(this.state);
+      this.setState({ listCommunitiesResponse: Some(data), loading: false });
     } else if (op == UserOperation.GetCommunity) {
       let data = wsJsonToRes<GetCommunityResponse>(msg, GetCommunityResponse);
-      this.state.listCommunitiesResponse = Some({
-        communities: [data.community_view],
+      this.setState({
+        listCommunitiesResponse: Some({
+          communities: [data.community_view],
+        }),
+        loading: false,
       });
-      this.state.loading = false;
-      this.setState(this.state);
     }
   }
 }
index 116cdc9a4ea3ba281f8a43afda09f2dbebeaec55..a3b5b75ec046183a2384e569b5e7da5f26d816c5 100644 (file)
@@ -34,27 +34,33 @@ export class MetadataCard extends Component<
             some: embedTitle =>
               post.url.match({
                 some: url => (
-                  <div class="card border-secondary mt-3 mb-2">
-                    <div class="row">
-                      <div class="col-12">
-                        <div class="card-body">
-                          {post.name !== embedTitle && [
-                            <h5 class="card-title d-inline">
-                              <a class="text-body" href={url} rel={relTags}>
-                                {embedTitle}
-                              </a>
-                            </h5>,
-                            <span class="d-inline-block ml-2 mb-2 small text-muted">
-                              <a
-                                class="text-muted font-italic"
-                                href={url}
-                                rel={relTags}
-                              >
-                                {new URL(url).hostname}
-                                <Icon icon="external-link" classes="ml-1" />
-                              </a>
-                            </span>,
-                          ]}
+                  <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
@@ -68,7 +74,7 @@ export class MetadataCard extends Component<
                           })}
                           {post.embed_html.isSome() && (
                             <button
-                              class="mt-2 btn btn-secondary text-monospace"
+                              className="mt-2 btn btn-secondary text-monospace"
                               onClick={linkEvent(this, this.handleIframeExpand)}
                               data-tippy-content={i18n.t("expand_here")}
                             >
@@ -88,7 +94,7 @@ export class MetadataCard extends Component<
           post.embed_html.match({
             some: html => (
               <div
-                class="mt-3 mb-2"
+                className="mt-3 mb-2"
                 dangerouslySetInnerHTML={{ __html: html }}
               />
             ),
@@ -99,7 +105,6 @@ export class MetadataCard extends Component<
   }
 
   handleIframeExpand(i: MetadataCard) {
-    i.state.expanded = !i.state.expanded;
-    i.setState(i.state);
+    i.setState({ expanded: !i.state.expanded });
   }
 }
index dde1cb4237d88ce60eb0eefadb49e592b80e4d4b..286378b114ab194ef1e2ed471eb2fa03e7e1ae55 100644 (file)
@@ -108,10 +108,16 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
 
     this.state = this.emptyState;
 
+    this.parseMessage = this.parseMessage.bind(this);
+    this.subscription = wsSubscribe(this.parseMessage);
+
     // Means its an edit
-    this.props.post_view.match({
-      some: pv =>
-        (this.state.postForm = new CreatePost({
+    if (this.props.post_view.isSome()) {
+      let pv = this.props.post_view.unwrap();
+
+      this.state = {
+        ...this.state,
+        postForm: new CreatePost({
           body: pv.post.body,
           name: pv.post.name,
           community_id: pv.community.id,
@@ -119,21 +125,22 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
           nsfw: Some(pv.post.nsfw),
           honeypot: None,
           auth: auth().unwrap(),
-        })),
-      none: void 0,
-    });
-
-    this.props.params.match({
-      some: params => {
-        this.state.postForm.name = toUndefined(params.name);
-        this.state.postForm.url = params.url;
-        this.state.postForm.body = params.body;
-      },
-      none: void 0,
-    });
+        }),
+      };
+    }
 
-    this.parseMessage = this.parseMessage.bind(this);
-    this.subscription = wsSubscribe(this.parseMessage);
+    if (this.props.params.isSome()) {
+      let params = this.props.params.unwrap();
+      this.state = {
+        ...this.state,
+        postForm: {
+          ...this.state.postForm,
+          name: toUndefined(params.name),
+          url: params.url,
+          body: params.body,
+        },
+      };
+    }
   }
 
   componentDidMount() {
@@ -177,15 +184,15 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
           message={i18n.t("block_leaving")}
         />
         <form onSubmit={linkEvent(this, this.handlePostSubmit)}>
-          <div class="form-group row">
-            <label class="col-sm-2 col-form-label" htmlFor="post-url">
+          <div className="form-group row">
+            <label className="col-sm-2 col-form-label" htmlFor="post-url">
               {i18n.t("url")}
             </label>
-            <div class="col-sm-10">
+            <div className="col-sm-10">
               <input
                 type="url"
                 id="post-url"
-                class="form-control"
+                className="form-control"
                 value={toUndefined(this.state.postForm.url)}
                 onInput={linkEvent(this, this.handlePostUrlChange)}
                 onPaste={linkEvent(this, this.handleImageUploadPaste)}
@@ -193,7 +200,7 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
               {this.state.suggestedTitle.match({
                 some: title => (
                   <div
-                    class="mt-1 text-muted small font-weight-bold pointer"
+                    className="mt-1 text-muted small font-weight-bold pointer"
                     role="button"
                     onClick={linkEvent(this, this.copySuggestedTitle)}
                   >
@@ -217,7 +224,7 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
                   type="file"
                   accept="image/*,video/*"
                   name="file"
-                  class="d-none"
+                  className="d-none"
                   disabled={UserService.Instance.myUserInfo.isNone()}
                   onChange={linkEvent(this, this.handleImageUpload)}
                 />
@@ -230,7 +237,7 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
                         href={`${webArchiveUrl}/save/${encodeURIComponent(
                           url
                         )}`}
-                        class="mr-2 d-inline-block float-right text-muted small font-weight-bold"
+                        className="mr-2 d-inline-block float-right text-muted small font-weight-bold"
                         rel={relTags}
                       >
                         archive.org {i18n.t("archive_link")}
@@ -239,7 +246,7 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
                         href={`${ghostArchiveUrl}/search?term=${encodeURIComponent(
                           url
                         )}`}
-                        class="mr-2 d-inline-block float-right text-muted small font-weight-bold"
+                        className="mr-2 d-inline-block float-right text-muted small font-weight-bold"
                         rel={relTags}
                       >
                         ghostarchive.org {i18n.t("archive_link")}
@@ -248,7 +255,7 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
                         href={`${archiveTodayUrl}/?run=1&url=${encodeURIComponent(
                           url
                         )}`}
-                        class="mr-2 d-inline-block float-right text-muted small font-weight-bold"
+                        className="mr-2 d-inline-block float-right text-muted small font-weight-bold"
                         rel={relTags}
                       >
                         archive.today {i18n.t("archive_link")}
@@ -260,14 +267,16 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
               {this.state.imageLoading && <Spinner />}
               {this.state.postForm.url.match({
                 some: url =>
-                  isImage(url) && <img src={url} class="img-fluid" alt="" />,
+                  isImage(url) && (
+                    <img src={url} className="img-fluid" alt="" />
+                  ),
                 none: <></>,
               })}
               {this.state.crossPosts.match({
                 some: xPosts =>
                   xPosts.length > 0 && (
                     <>
-                      <div class="my-1 text-muted small font-weight-bold">
+                      <div className="my-1 text-muted small font-weight-bold">
                         {i18n.t("cross_posts")}
                       </div>
                       <PostListings
@@ -282,16 +291,16 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
               })}
             </div>
           </div>
-          <div class="form-group row">
-            <label class="col-sm-2 col-form-label" htmlFor="post-title">
+          <div className="form-group row">
+            <label className="col-sm-2 col-form-label" htmlFor="post-title">
               {i18n.t("title")}
             </label>
-            <div class="col-sm-10">
+            <div className="col-sm-10">
               <textarea
                 value={this.state.postForm.name}
                 id="post-title"
                 onInput={linkEvent(this, this.handlePostNameChange)}
-                class={`form-control ${
+                className={`form-control ${
                   !validTitle(this.state.postForm.name) && "is-invalid"
                 }`}
                 required
@@ -300,7 +309,7 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
                 maxLength={MAX_POST_TITLE_LENGTH}
               />
               {!validTitle(this.state.postForm.name) && (
-                <div class="invalid-feedback">
+                <div className="invalid-feedback">
                   {i18n.t("invalid_post_title")}
                 </div>
               )}
@@ -308,7 +317,7 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
                 some: sPosts =>
                   sPosts.length > 0 && (
                     <>
-                      <div class="my-1 text-muted small font-weight-bold">
+                      <div className="my-1 text-muted small font-weight-bold">
                         {i18n.t("related_posts")}
                       </div>
                       <PostListings
@@ -324,9 +333,9 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
             </div>
           </div>
 
-          <div class="form-group row">
-            <label class="col-sm-2 col-form-label">{i18n.t("body")}</label>
-            <div class="col-sm-10">
+          <div className="form-group row">
+            <label className="col-sm-2 col-form-label">{i18n.t("body")}</label>
+            <div className="col-sm-10">
               <MarkdownTextArea
                 initialContent={this.state.postForm.body}
                 onContentChange={this.handlePostBodyChange}
@@ -337,20 +346,23 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
             </div>
           </div>
           {this.props.post_view.isNone() && (
-            <div class="form-group row">
-              <label class="col-sm-2 col-form-label" htmlFor="post-community">
+            <div className="form-group row">
+              <label
+                className="col-sm-2 col-form-label"
+                htmlFor="post-community"
+              >
                 {i18n.t("community")}
               </label>
-              <div class="col-sm-10">
+              <div className="col-sm-10">
                 <select
-                  class="form-control"
+                  className="form-control"
                   id="post-community"
                   value={this.state.postForm.community_id}
                   onInput={linkEvent(this, this.handlePostCommunityChange)}
                 >
                   <option>{i18n.t("select_a_community")}</option>
                   {this.props.communities.unwrapOr([]).map(cv => (
-                    <option value={cv.community.id}>
+                    <option key={cv.community.id} value={cv.community.id}>
                       {communitySelectName(cv)}
                     </option>
                   ))}
@@ -359,14 +371,14 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
             </div>
           )}
           {this.props.enableNsfw && (
-            <div class="form-group row">
-              <legend class="col-form-label col-sm-2 pt-0">
+            <div className="form-group row">
+              <legend className="col-form-label col-sm-2 pt-0">
                 {i18n.t("nsfw")}
               </legend>
-              <div class="col-sm-10">
-                <div class="form-check">
+              <div className="col-sm-10">
+                <div className="form-check">
                   <input
-                    class="form-check-input position-static"
+                    className="form-check-input position-static"
                     id="post-nsfw"
                     type="checkbox"
                     checked={toUndefined(this.state.postForm.nsfw)}
@@ -381,19 +393,19 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
             autoComplete="false"
             name="a_password"
             type="text"
-            class="form-control honeypot"
+            className="form-control honeypot"
             id="register-honey"
             value={toUndefined(this.state.postForm.honeypot)}
             onInput={linkEvent(this, this.handleHoneyPotChange)}
           />
-          <div class="form-group row">
-            <div class="col-sm-10">
+          <div className="form-group row">
+            <div className="col-sm-10">
               <button
                 disabled={
                   !this.state.postForm.community_id || this.state.loading
                 }
                 type="submit"
-                class="btn btn-secondary mr-2"
+                className="btn btn-secondary mr-2"
               >
                 {this.state.loading ? (
                   <Spinner />
@@ -406,7 +418,7 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
               {this.props.post_view.isSome() && (
                 <button
                   type="button"
-                  class="btn btn-secondary"
+                  className="btn btn-secondary"
                   onClick={linkEvent(this, this.handleCancel)}
                 >
                   {i18n.t("cancel")}
@@ -422,12 +434,14 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
   handlePostSubmit(i: PostForm, event: any) {
     event.preventDefault();
 
+    i.setState({ loading: true });
+
     // Coerce empty url string to undefined
     if (
       i.state.postForm.url.isSome() &&
       i.state.postForm.url.unwrapOr("blank") === ""
     ) {
-      i.state.postForm.url = None;
+      i.setState(s => ((s.postForm.url = None), s));
     }
 
     let pForm = i.state.postForm;
@@ -444,32 +458,33 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
         WebSocketService.Instance.send(wsClient.editPost(form));
       },
       none: () => {
-        i.state.postForm.auth = auth().unwrap();
-        WebSocketService.Instance.send(wsClient.createPost(i.state.postForm));
+        i.setState(s => ((s.postForm.auth = auth().unwrap()), s));
+        let form = new CreatePost({ ...i.state.postForm });
+        WebSocketService.Instance.send(wsClient.createPost(form));
       },
     });
-    i.state.loading = true;
-    i.setState(i.state);
   }
 
   copySuggestedTitle(i: PostForm) {
     i.state.suggestedTitle.match({
       some: sTitle => {
-        i.state.postForm.name = sTitle.substring(0, MAX_POST_TITLE_LENGTH);
-        i.state.suggestedTitle = None;
+        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);
-        i.setState(i.state);
       },
       none: void 0,
     });
   }
 
   handlePostUrlChange(i: PostForm, event: any) {
-    i.state.postForm.url = Some(event.target.value);
-    i.setState(i.state);
+    i.setState(s => ((s.postForm.url = Some(event.target.value)), s));
     i.fetchPageTitle();
   }
 
@@ -494,12 +509,10 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
 
           // Fetch the page title
           getSiteMetadata(url).then(d => {
-            this.state.suggestedTitle = d.metadata.title;
-            this.setState(this.state);
+            this.setState({ suggestedTitle: d.metadata.title });
           });
         } else {
-          this.state.suggestedTitle = None;
-          this.state.crossPosts = None;
+          this.setState({ suggestedTitle: None, crossPosts: None });
         }
       },
       none: void 0,
@@ -507,8 +520,7 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
   }
 
   handlePostNameChange(i: PostForm, event: any) {
-    i.state.postForm.name = event.target.value;
-    i.setState(i.state);
+    i.setState(s => ((s.postForm.name = event.target.value), s));
     i.fetchSimilarPosts();
   }
 
@@ -529,30 +541,26 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
     if (this.state.postForm.name !== "") {
       WebSocketService.Instance.send(wsClient.search(form));
     } else {
-      this.state.suggestedPosts = None;
+      this.setState({ suggestedPosts: None });
     }
-
-    this.setState(this.state);
   }
 
   handlePostBodyChange(val: string) {
-    this.state.postForm.body = Some(val);
-    this.setState(this.state);
+    this.setState(s => ((s.postForm.body = Some(val)), s));
   }
 
   handlePostCommunityChange(i: PostForm, event: any) {
-    i.state.postForm.community_id = Number(event.target.value);
-    i.setState(i.state);
+    i.setState(
+      s => ((s.postForm.community_id = Number(event.target.value)), s)
+    );
   }
 
   handlePostNsfwChange(i: PostForm, event: any) {
-    i.state.postForm.nsfw = Some(event.target.checked);
-    i.setState(i.state);
+    i.setState(s => ((s.postForm.nsfw = Some(event.target.checked)), s));
   }
 
   handleHoneyPotChange(i: PostForm, event: any) {
-    i.state.postForm.honeypot = Some(event.target.value);
-    i.setState(i.state);
+    i.setState(s => ((s.postForm.honeypot = Some(event.target.value)), s));
   }
 
   handleCancel(i: PostForm) {
@@ -561,8 +569,7 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
 
   handlePreviewToggle(i: PostForm, event: any) {
     event.preventDefault();
-    i.state.previewMode = !i.state.previewMode;
-    i.setState(i.state);
+    i.setState({ previewMode: !i.state.previewMode });
   }
 
   handleImageUploadPaste(i: PostForm, event: any) {
@@ -584,8 +591,7 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
     const formData = new FormData();
     formData.append("images[]", file);
 
-    i.state.imageLoading = true;
-    i.setState(i.state);
+    i.setState({ imageLoading: true });
 
     fetch(pictrsUri, {
       method: "POST",
@@ -601,22 +607,19 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
           let deleteToken = res.files[0].delete_token;
           let deleteUrl = `${pictrsUri}/delete/${deleteToken}/${hash}`;
           i.state.postForm.url = Some(url);
-          i.state.imageLoading = false;
-          i.setState(i.state);
+          i.setState({ imageLoading: false });
           pictrsDeleteToast(
             i18n.t("click_to_delete_picture"),
             i18n.t("picture_deleted"),
             deleteUrl
           );
         } else {
-          i.state.imageLoading = false;
-          i.setState(i.state);
+          i.setState({ imageLoading: false });
           toast(JSON.stringify(res), "danger");
         }
       })
       .catch(error => {
-        i.state.imageLoading = false;
-        i.setState(i.state);
+        i.setState({ imageLoading: false });
         console.error(error);
         toast(error, "danger");
       });
@@ -631,8 +634,11 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
         this.choices.passedElement.element.addEventListener(
           "choice",
           (e: any) => {
-            this.state.postForm.community_id = Number(e.detail.choice.value);
-            this.setState(this.state);
+            this.setState(
+              s => (
+                (s.postForm.community_id = Number(e.detail.choice.value)), s
+              )
+            );
           },
           false
         );
@@ -658,7 +664,8 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
     }
 
     this.props.post_view.match({
-      some: pv => (this.state.postForm.community_id = pv.community.id),
+      some: pv =>
+        this.setState(s => ((s.postForm.community_id = pv.community.id), s)),
       none: void 0,
     });
     this.props.params.match({
@@ -670,9 +677,12 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
                 let foundCommunityId = this.props.communities
                   .unwrapOr([])
                   .find(r => r.community.name == name).community.id;
-                this.state.postForm.community_id = foundCommunityId;
+                this.setState(
+                  s => ((s.postForm.community_id = foundCommunityId), s)
+                );
               },
-              right: id => (this.state.postForm.community_id = id),
+              right: id =>
+                this.setState(s => ((s.postForm.community_id = id), s)),
             }),
           none: void 0,
         }),
@@ -693,15 +703,13 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
     if (msg.error) {
       // Errors handled by top level pages
       // toast(i18n.t(msg.error), "danger");
-      this.state.loading = false;
-      this.setState(this.state);
+      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.state.loading = false;
             this.props.onCreate(data.post_view);
           }
         },
@@ -712,7 +720,7 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
       UserService.Instance.myUserInfo.match({
         some: mui => {
           if (data.post_view.creator.id == mui.local_user_view.person.id) {
-            this.state.loading = false;
+            this.setState({ loading: false });
             this.props.onEdit(data.post_view);
           }
         },
@@ -722,11 +730,10 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
       let data = wsJsonToRes<SearchResponse>(msg, SearchResponse);
 
       if (data.type_ == SearchType[SearchType.Posts]) {
-        this.state.suggestedPosts = Some(data.posts);
+        this.setState({ suggestedPosts: Some(data.posts) });
       } else if (data.type_ == SearchType[SearchType.Url]) {
-        this.state.crossPosts = Some(data.posts);
+        this.setState({ crossPosts: Some(data.posts) });
       }
-      this.setState(this.state);
     }
   }
 }
index 672d3d6925d28a13d245c29eeaaaf158dcdd1994..e2468247674d793be4057b3a98626e8bb55cd9d8 100644 (file)
@@ -135,20 +135,21 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
   }
 
   componentWillReceiveProps(nextProps: PostListingProps) {
-    this.state.my_vote = nextProps.post_view.my_vote;
-    this.state.upvotes = nextProps.post_view.counts.upvotes;
-    this.state.downvotes = nextProps.post_view.counts.downvotes;
-    this.state.score = nextProps.post_view.counts.score;
+    this.setState({
+      my_vote: nextProps.post_view.my_vote,
+      upvotes: nextProps.post_view.counts.upvotes,
+      downvotes: nextProps.post_view.counts.downvotes,
+      score: nextProps.post_view.counts.score,
+    });
     if (this.props.post_view.post.id !== nextProps.post_view.post.id) {
-      this.state.imageExpanded = false;
+      this.setState({ imageExpanded: false });
     }
-    this.setState(this.state);
   }
 
   render() {
     let post = this.props.post_view.post;
     return (
-      <div class="post-listing">
+      <div className="post-listing">
         {!this.state.showEdit ? (
           <>
             {this.listing()}
@@ -159,7 +160,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
             {this.showBody && this.body()}
           </>
         ) : (
-          <div class="col-12">
+          <div className="col-12">
             <PostForm
               post_view={Some(this.props.post_view)}
               communities={None}
@@ -178,7 +179,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
   body() {
     return this.props.post_view.post.body.match({
       some: body => (
-        <div class="col-12 card my-2 p-2">
+        <div className="col-12 card my-2 p-2">
           {this.state.viewSource ? (
             <pre>{body}</pre>
           ) : (
@@ -194,14 +195,14 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
     return this.imageSrc.match({
       some: src => (
         <>
-          <div class="offset-sm-3 my-2 d-none d-sm-block">
-            <a href={src} class="d-inline-block">
+          <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
-              class="d-inline-block"
+              className="d-inline-block"
               onClick={linkEvent(this, this.handleImageExpandClick)}
             >
               <PictrsImage src={src} />
@@ -254,7 +255,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
       return (
         <a
           href={this.imageSrc.unwrap()}
-          class="text-body d-inline-block position-relative mb-2"
+          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")}
@@ -266,7 +267,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
     } else if (url.isSome() && thumbnail.isSome()) {
       return (
         <a
-          class="text-body d-inline-block position-relative mb-2"
+          className="text-body d-inline-block position-relative mb-2"
           href={url.unwrap()}
           rel={relTags}
           title={url.unwrap()}
@@ -278,13 +279,13 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
     } else if (url.isSome()) {
       if (isVideo(url.unwrap())) {
         return (
-          <div class="embed-responsive embed-responsive-16by9">
+          <div className="embed-responsive embed-responsive-16by9">
             <video
-              playsinline
+              playsInline
               muted
               loop
               controls
-              class="embed-responsive-item"
+              className="embed-responsive-item"
             >
               <source src={url.unwrap()} type="video/mp4" />
             </video>
@@ -298,7 +299,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
             title={url.unwrap()}
             rel={relTags}
           >
-            <div class="thumbnail rounded bg-light d-flex justify-content-center">
+            <div className="thumbnail rounded bg-light d-flex justify-content-center">
               <Icon icon="external-link" classes="d-flex align-items-center" />
             </div>
           </a>
@@ -311,7 +312,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
           to={`/post/${post.id}`}
           title={i18n.t("comments")}
         >
-          <div class="thumbnail rounded bg-light d-flex justify-content-center">
+          <div className="thumbnail rounded bg-light d-flex justify-content-center">
             <Icon icon="message-square" classes="d-flex align-items-center" />
           </div>
         </Link>
@@ -322,7 +323,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
   createdLine() {
     let post_view = this.props.post_view;
     return (
-      <ul class="list-inline mb-1 text-muted small">
+      <ul className="list-inline mb-1 text-muted small">
         <li className="list-inline-item">
           <PersonListing person={post_view.creator} />
 
@@ -346,7 +347,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
           )}
           {this.props.showCommunity && (
             <span>
-              <span class="mx-1"> {i18n.t("to")} </span>
+              <span className="mx-1"> {i18n.t("to")} </span>
               <CommunityLink community={post_view.community} />
             </span>
           )}
@@ -416,13 +417,13 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
         </button>
         {showScores() ? (
           <div
-            class={`unselectable pointer font-weight-bold text-muted px-1`}
+            className={`unselectable pointer font-weight-bold text-muted px-1`}
             data-tippy-content={this.pointsTippy}
           >
             {numToSI(this.state.score)}
           </div>
         ) : (
-          <div class="p-1"></div>
+          <div className="p-1"></div>
         )}
         {this.props.enableDownvotes && (
           <button
@@ -470,7 +471,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
           })}
           {post.url.map(isImage).or(post.thumbnail_url).unwrapOr(false) && (
             <button
-              class="btn btn-link text-monospace text-muted small d-inline-block ml-2"
+              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)}
             >
@@ -525,13 +526,13 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
     return this.props.duplicates.match({
       some: dupes =>
         dupes.length > 0 && (
-          <ul class="list-inline mb-1 small text-muted">
+          <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 className="list-inline-item mr-2">
+                <li key={pv.post.id} className="list-inline-item mr-2">
                   <Link to={`/post/${pv.post.id}`}>
                     {pv.community.local
                       ? pv.community.name
@@ -551,7 +552,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
   commentsLine(mobile = false) {
     let post = this.props.post_view.post;
     return (
-      <div class="d-flex justify-content-start flex-wrap text-muted font-weight-bold mb-1">
+      <div className="d-flex justify-content-start flex-wrap text-muted font-weight-bold mb-1">
         {this.commentsButton}
         {!post.local && (
           <a
@@ -617,7 +618,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
   get commentsButton() {
     let post_view = this.props.post_view;
     return (
-      <button class="btn btn-link text-muted py-0 pl-0">
+      <button className="btn btn-link text-muted py-0 pl-0">
         <Link
           className="text-muted"
           title={i18n.t("number_of_comments", {
@@ -652,7 +653,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
           >
             <Icon icon="arrow-up1" classes="icon-inline small" />
             {showScores() && (
-              <span class="ml-2">{numToSI(this.state.upvotes)}</span>
+              <span className="ml-2">{numToSI(this.state.upvotes)}</span>
             )}
           </button>
           {this.props.enableDownvotes && (
@@ -669,7 +670,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
               <Icon icon="arrow-down1" classes="icon-inline small" />
               {showScores() && (
                 <span
-                  class={classNames("ml-2", {
+                  className={classNames("ml-2", {
                     invisible: this.state.downvotes === 0,
                   })}
                 >
@@ -688,7 +689,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
     let label = saved ? i18n.t("unsave") : i18n.t("save");
     return (
       <button
-        class="btn btn-link btn-animate text-muted py-0"
+        className="btn btn-link btn-animate text-muted py-0"
         onClick={linkEvent(this, this.handleSavePostClick)}
         data-tippy-content={label}
         aria-label={label}
@@ -717,7 +718,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
   get reportButton() {
     return (
       <button
-        class="btn btn-link btn-animate text-muted py-0"
+        className="btn btn-link btn-animate text-muted py-0"
         onClick={linkEvent(this, this.handleShowReportDialog)}
         data-tippy-content={i18n.t("show_report_dialog")}
         aria-label={i18n.t("show_report_dialog")}
@@ -730,7 +731,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
   get blockButton() {
     return (
       <button
-        class="btn btn-link btn-animate text-muted py-0"
+        className="btn btn-link btn-animate text-muted py-0"
         onClick={linkEvent(this, this.handleBlockUserClick)}
         data-tippy-content={i18n.t("block_user")}
         aria-label={i18n.t("block_user")}
@@ -743,7 +744,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
   get editButton() {
     return (
       <button
-        class="btn btn-link btn-animate text-muted py-0"
+        className="btn btn-link btn-animate text-muted py-0"
         onClick={linkEvent(this, this.handleEditClick)}
         data-tippy-content={i18n.t("edit")}
         aria-label={i18n.t("edit")}
@@ -758,7 +759,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
     let label = !deleted ? i18n.t("delete") : i18n.t("restore");
     return (
       <button
-        class="btn btn-link btn-animate text-muted py-0"
+        className="btn btn-link btn-animate text-muted py-0"
         onClick={linkEvent(this, this.handleDeleteClick)}
         data-tippy-content={label}
         aria-label={label}
@@ -775,7 +776,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
   get showMoreButton() {
     return (
       <button
-        class="btn btn-link btn-animate text-muted py-0"
+        className="btn btn-link btn-animate text-muted py-0"
         onClick={linkEvent(this, this.handleShowAdvanced)}
         data-tippy-content={i18n.t("more")}
         aria-label={i18n.t("more")}
@@ -788,7 +789,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
   get viewSourceButton() {
     return (
       <button
-        class="btn btn-link btn-animate text-muted py-0"
+        className="btn btn-link btn-animate text-muted py-0"
         onClick={linkEvent(this, this.handleViewSource)}
         data-tippy-content={i18n.t("view_source")}
         aria-label={i18n.t("view_source")}
@@ -807,7 +808,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
     let label = locked ? i18n.t("unlock") : i18n.t("lock");
     return (
       <button
-        class="btn btn-link btn-animate text-muted py-0"
+        className="btn btn-link btn-animate text-muted py-0"
         onClick={linkEvent(this, this.handleModLock)}
         data-tippy-content={label}
         aria-label={label}
@@ -826,7 +827,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
     let label = stickied ? i18n.t("unsticky") : i18n.t("sticky");
     return (
       <button
-        class="btn btn-link btn-animate text-muted py-0"
+        className="btn btn-link btn-animate text-muted py-0"
         onClick={linkEvent(this, this.handleModSticky)}
         data-tippy-content={label}
         aria-label={label}
@@ -844,7 +845,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
     let removed = this.props.post_view.post.removed;
     return (
       <button
-        class="btn btn-link btn-animate text-muted py-0"
+        className="btn btn-link btn-animate text-muted py-0"
         onClick={linkEvent(
           this,
           !removed ? this.handleModRemoveShow : this.handleModRemoveSubmit
@@ -870,7 +871,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
               {!this.creatorIsMod_ &&
                 (!post_view.creator_banned_from_community ? (
                   <button
-                    class="btn btn-link btn-animate text-muted py-0"
+                    className="btn btn-link btn-animate text-muted py-0"
                     onClick={linkEvent(
                       this,
                       this.handleModBanFromCommunityShow
@@ -881,7 +882,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
                   </button>
                 ) : (
                   <button
-                    class="btn btn-link btn-animate text-muted py-0"
+                    className="btn btn-link btn-animate text-muted py-0"
                     onClick={linkEvent(
                       this,
                       this.handleModBanFromCommunitySubmit
@@ -893,7 +894,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
                 ))}
               {!post_view.creator_banned_from_community && (
                 <button
-                  class="btn btn-link btn-animate text-muted py-0"
+                  className="btn btn-link btn-animate text-muted py-0"
                   onClick={linkEvent(this, this.handleAddModToCommunity)}
                   aria-label={
                     this.creatorIsMod_
@@ -914,7 +915,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
             this.creatorIsMod_ &&
             (!this.state.showConfirmTransferCommunity ? (
               <button
-                class="btn btn-link btn-animate text-muted py-0"
+                className="btn btn-link btn-animate text-muted py-0"
                 onClick={linkEvent(
                   this,
                   this.handleShowConfirmTransferCommunity
@@ -926,20 +927,20 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
             ) : (
               <>
                 <button
-                  class="d-inline-block mr-1 btn btn-link btn-animate text-muted py-0"
+                  className="d-inline-block mr-1 btn btn-link btn-animate text-muted py-0"
                   aria-label={i18n.t("are_you_sure")}
                 >
                   {i18n.t("are_you_sure")}
                 </button>
                 <button
-                  class="btn btn-link btn-animate text-muted py-0 d-inline-block mr-1"
+                  className="btn btn-link btn-animate text-muted py-0 d-inline-block mr-1"
                   aria-label={i18n.t("yes")}
                   onClick={linkEvent(this, this.handleTransferCommunity)}
                 >
                   {i18n.t("yes")}
                 </button>
                 <button
-                  class="btn btn-link btn-animate text-muted py-0 d-inline-block"
+                  className="btn btn-link btn-animate text-muted py-0 d-inline-block"
                   onClick={linkEvent(
                     this,
                     this.handleCancelShowConfirmTransferCommunity
@@ -957,7 +958,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
                 <>
                   {!isBanned(post_view.creator) ? (
                     <button
-                      class="btn btn-link btn-animate text-muted py-0"
+                      className="btn btn-link btn-animate text-muted py-0"
                       onClick={linkEvent(this, this.handleModBanShow)}
                       aria-label={i18n.t("ban_from_site")}
                     >
@@ -965,7 +966,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
                     </button>
                   ) : (
                     <button
-                      class="btn btn-link btn-animate text-muted py-0"
+                      className="btn btn-link btn-animate text-muted py-0"
                       onClick={linkEvent(this, this.handleModBanSubmit)}
                       aria-label={i18n.t("unban_from_site")}
                     >
@@ -973,14 +974,14 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
                     </button>
                   )}
                   <button
-                    class="btn btn-link btn-animate text-muted py-0"
+                    className="btn btn-link btn-animate text-muted py-0"
                     onClick={linkEvent(this, this.handlePurgePersonShow)}
                     aria-label={i18n.t("purge_user")}
                   >
                     {i18n.t("purge_user")}
                   </button>
                   <button
-                    class="btn btn-link btn-animate text-muted py-0"
+                    className="btn btn-link btn-animate text-muted py-0"
                     onClick={linkEvent(this, this.handlePurgePostShow)}
                     aria-label={i18n.t("purge_post")}
                   >
@@ -990,7 +991,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
               )}
               {!isBanned(post_view.creator) && post_view.creator.local && (
                 <button
-                  class="btn btn-link btn-animate text-muted py-0"
+                  className="btn btn-link btn-animate text-muted py-0"
                   onClick={linkEvent(this, this.handleAddAdmin)}
                   aria-label={
                     this.creatorIsAdmin_
@@ -1022,23 +1023,23 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
       <>
         {this.state.showRemoveDialog && (
           <form
-            class="form-inline"
+            className="form-inline"
             onSubmit={linkEvent(this, this.handleModRemoveSubmit)}
           >
-            <label class="sr-only" htmlFor="post-listing-remove-reason">
+            <label className="sr-only" htmlFor="post-listing-remove-reason">
               {i18n.t("reason")}
             </label>
             <input
               type="text"
               id="post-listing-remove-reason"
-              class="form-control mr-2"
+              className="form-control mr-2"
               placeholder={i18n.t("reason")}
               value={toUndefined(this.state.removeReason)}
               onInput={linkEvent(this, this.handleModRemoveReasonChange)}
             />
             <button
               type="submit"
-              class="btn btn-secondary"
+              className="btn btn-secondary"
               aria-label={i18n.t("remove_post")}
             >
               {i18n.t("remove_post")}
@@ -1047,40 +1048,43 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
         )}
         {this.state.showBanDialog && (
           <form onSubmit={linkEvent(this, this.handleModBanBothSubmit)}>
-            <div class="form-group row col-12">
-              <label class="col-form-label" htmlFor="post-listing-ban-reason">
+            <div className="form-group row col-12">
+              <label
+                className="col-form-label"
+                htmlFor="post-listing-ban-reason"
+              >
                 {i18n.t("reason")}
               </label>
               <input
                 type="text"
                 id="post-listing-ban-reason"
-                class="form-control mr-2"
+                className="form-control mr-2"
                 placeholder={i18n.t("reason")}
                 value={toUndefined(this.state.banReason)}
                 onInput={linkEvent(this, this.handleModBanReasonChange)}
               />
-              <label class="col-form-label" htmlFor={`mod-ban-expires`}>
+              <label className="col-form-label" htmlFor={`mod-ban-expires`}>
                 {i18n.t("expires")}
               </label>
               <input
                 type="number"
                 id={`mod-ban-expires`}
-                class="form-control mr-2"
+                className="form-control mr-2"
                 placeholder={i18n.t("number_of_days")}
                 value={toUndefined(this.state.banExpireDays)}
                 onInput={linkEvent(this, this.handleModBanExpireDaysChange)}
               />
-              <div class="form-group">
-                <div class="form-check">
+              <div className="form-group">
+                <div className="form-check">
                   <input
-                    class="form-check-input"
+                    className="form-check-input"
                     id="mod-ban-remove-data"
                     type="checkbox"
                     checked={this.state.removeData}
                     onChange={linkEvent(this, this.handleModRemoveDataChange)}
                   />
                   <label
-                    class="form-check-label"
+                    className="form-check-label"
                     htmlFor="mod-ban-remove-data"
                     title={i18n.t("remove_content_more")}
                   >
@@ -1094,10 +1098,10 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
             {/*   <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 class="form-group row">
+            <div className="form-group row">
               <button
                 type="submit"
-                class="btn btn-secondary"
+                className="btn btn-secondary"
                 aria-label={i18n.t("ban")}
               >
                 {i18n.t("ban")} {post.creator.name}
@@ -1107,16 +1111,16 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
         )}
         {this.state.showReportDialog && (
           <form
-            class="form-inline"
+            className="form-inline"
             onSubmit={linkEvent(this, this.handleReportSubmit)}
           >
-            <label class="sr-only" htmlFor="post-report-reason">
+            <label className="sr-only" htmlFor="post-report-reason">
               {i18n.t("reason")}
             </label>
             <input
               type="text"
               id="post-report-reason"
-              class="form-control mr-2"
+              className="form-control mr-2"
               placeholder={i18n.t("reason")}
               required
               value={toUndefined(this.state.reportReason)}
@@ -1124,7 +1128,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
             />
             <button
               type="submit"
-              class="btn btn-secondary"
+              className="btn btn-secondary"
               aria-label={i18n.t("create_report")}
             >
               {i18n.t("create_report")}
@@ -1133,17 +1137,17 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
         )}
         {this.state.showPurgeDialog && (
           <form
-            class="form-inline"
+            className="form-inline"
             onSubmit={linkEvent(this, this.handlePurgeSubmit)}
           >
             <PurgeWarning />
-            <label class="sr-only" htmlFor="purge-reason">
+            <label className="sr-only" htmlFor="purge-reason">
               {i18n.t("reason")}
             </label>
             <input
               type="text"
               id="purge-reason"
-              class="form-control mr-2"
+              className="form-control mr-2"
               placeholder={i18n.t("reason")}
               value={toUndefined(this.state.purgeReason)}
               onInput={linkEvent(this, this.handlePurgeReasonChange)}
@@ -1153,7 +1157,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
             ) : (
               <button
                 type="submit"
-                class="btn btn-secondary"
+                className="btn btn-secondary"
                 aria-label={purgeTypeText}
               >
                 {purgeTypeText}
@@ -1169,11 +1173,11 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
     let post = this.props.post_view.post;
     return post.thumbnail_url.isSome() ||
       post.url.map(isImage).unwrapOr(false) ? (
-      <div class="row">
+      <div className="row">
         <div className={`${this.state.imageExpanded ? "col-12" : "col-8"}`}>
           {this.postTitleLine()}
         </div>
-        <div class="col-4">
+        <div className="col-4">
           {/* Post body prev or thumbnail */}
           {!this.state.imageExpanded && this.thumbnail()}
         </div>
@@ -1198,9 +1202,9 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
     return (
       <>
         {/* The mobile view*/}
-        <div class="d-block d-sm-none">
-          <div class="row">
-            <div class="col-12">
+        <div className="d-block d-sm-none">
+          <div className="row">
+            <div className="col-12">
               {this.createdLine()}
 
               {/* If it has a thumbnail, do a right aligned thumbnail */}
@@ -1218,14 +1222,14 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
         </div>
 
         {/* The larger view*/}
-        <div class="d-none d-sm-block">
-          <div class="row">
+        <div className="d-none d-sm-block">
+          <div className="row">
             {!this.props.viewOnly && this.voteBar()}
-            <div class="col-sm-2 pr-0">
-              <div class="">{this.thumbnail()}</div>
+            <div className="col-sm-2 pr-0">
+              <div className="">{this.thumbnail()}</div>
             </div>
-            <div class="col-12 col-sm-9">
-              <div class="row">
+            <div className="col-12 col-sm-9">
+              <div className="row">
                 <div className="col-12">
                   {this.postTitleLine()}
                   {this.createdLine()}
@@ -1260,18 +1264,24 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
     let newVote = myVote == 1 ? 0 : 1;
 
     if (myVote == 1) {
-      this.state.score--;
-      this.state.upvotes--;
+      this.setState({
+        score: this.state.score - 1,
+        upvotes: this.state.upvotes - 1,
+      });
     } else if (myVote == -1) {
-      this.state.downvotes--;
-      this.state.upvotes++;
-      this.state.score += 2;
+      this.setState({
+        score: this.state.score + 2,
+        upvotes: this.state.upvotes + 1,
+        downvotes: this.state.downvotes - 1,
+      });
     } else {
-      this.state.upvotes++;
-      this.state.score++;
+      this.setState({
+        score: this.state.score + 1,
+        upvotes: this.state.upvotes + 1,
+      });
     }
 
-    this.state.my_vote = Some(newVote);
+    this.setState({ my_vote: Some(newVote) });
 
     let form = new CreatePostLike({
       post_id: this.props.post_view.post.id,
@@ -1294,18 +1304,24 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
     let newVote = myVote == -1 ? 0 : -1;
 
     if (myVote == 1) {
-      this.state.score -= 2;
-      this.state.upvotes--;
-      this.state.downvotes++;
+      this.setState({
+        score: this.state.score - 2,
+        upvotes: this.state.upvotes - 1,
+        downvotes: this.state.downvotes + 1,
+      });
     } else if (myVote == -1) {
-      this.state.downvotes--;
-      this.state.score++;
+      this.setState({
+        score: this.state.score + 1,
+        downvotes: this.state.downvotes - 1,
+      });
     } else {
-      this.state.downvotes++;
-      this.state.score--;
+      this.setState({
+        score: this.state.score - 1,
+        downvotes: this.state.downvotes + 1,
+      });
     }
 
-    this.state.my_vote = Some(newVote);
+    this.setState({ my_vote: Some(newVote) });
 
     let form = new CreatePostLike({
       post_id: this.props.post_view.post.id,
@@ -1319,29 +1335,24 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
   }
 
   handleEditClick(i: PostListing) {
-    i.state.showEdit = true;
-    i.setState(i.state);
+    i.setState({ showEdit: true });
   }
 
   handleEditCancel() {
-    this.state.showEdit = false;
-    this.setState(this.state);
+    this.setState({ showEdit: false });
   }
 
   // The actual editing is done in the recieve for post
   handleEditPost() {
-    this.state.showEdit = false;
-    this.setState(this.state);
+    this.setState({ showEdit: false });
   }
 
   handleShowReportDialog(i: PostListing) {
-    i.state.showReportDialog = !i.state.showReportDialog;
-    i.setState(this.state);
+    i.setState({ showReportDialog: !i.state.showReportDialog });
   }
 
   handleReportReasonChange(i: PostListing, event: any) {
-    i.state.reportReason = Some(event.target.value);
-    i.setState(i.state);
+    i.setState({ reportReason: Some(event.target.value) });
   }
 
   handleReportSubmit(i: PostListing, event: any) {
@@ -1353,8 +1364,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
     });
     WebSocketService.Instance.send(wsClient.createPostReport(form));
 
-    i.state.showReportDialog = false;
-    i.setState(i.state);
+    i.setState({ showReportDialog: false });
   }
 
   handleBlockUserClick(i: PostListing) {
@@ -1413,19 +1423,18 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
   }
 
   handleModRemoveShow(i: PostListing) {
-    i.state.showRemoveDialog = !i.state.showRemoveDialog;
-    i.state.showBanDialog = false;
-    i.setState(i.state);
+    i.setState({
+      showRemoveDialog: !i.state.showRemoveDialog,
+      showBanDialog: false,
+    });
   }
 
   handleModRemoveReasonChange(i: PostListing, event: any) {
-    i.state.removeReason = Some(event.target.value);
-    i.setState(i.state);
+    i.setState({ removeReason: Some(event.target.value) });
   }
 
   handleModRemoveDataChange(i: PostListing, event: any) {
-    i.state.removeData = event.target.checked;
-    i.setState(i.state);
+    i.setState({ removeData: event.target.checked });
   }
 
   handleModRemoveSubmit(i: PostListing, event: any) {
@@ -1438,8 +1447,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
     });
     WebSocketService.Instance.send(wsClient.removePost(form));
 
-    i.state.showRemoveDialog = false;
-    i.setState(i.state);
+    i.setState({ showRemoveDialog: false });
   }
 
   handleModLock(i: PostListing) {
@@ -1461,36 +1469,39 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
   }
 
   handleModBanFromCommunityShow(i: PostListing) {
-    i.state.showBanDialog = true;
-    i.state.banType = BanType.Community;
-    i.state.showRemoveDialog = false;
-    i.setState(i.state);
+    i.setState({
+      showBanDialog: true,
+      banType: BanType.Community,
+      showRemoveDialog: false,
+    });
   }
 
   handleModBanShow(i: PostListing) {
-    i.state.showBanDialog = true;
-    i.state.banType = BanType.Site;
-    i.state.showRemoveDialog = false;
-    i.setState(i.state);
+    i.setState({
+      showBanDialog: true,
+      banType: BanType.Site,
+      showRemoveDialog: false,
+    });
   }
 
   handlePurgePersonShow(i: PostListing) {
-    i.state.showPurgeDialog = true;
-    i.state.purgeType = PurgeType.Person;
-    i.state.showRemoveDialog = false;
-    i.setState(i.state);
+    i.setState({
+      showPurgeDialog: true,
+      purgeType: PurgeType.Person,
+      showRemoveDialog: false,
+    });
   }
 
   handlePurgePostShow(i: PostListing) {
-    i.state.showPurgeDialog = true;
-    i.state.purgeType = PurgeType.Post;
-    i.state.showRemoveDialog = false;
-    i.setState(i.state);
+    i.setState({
+      showPurgeDialog: true,
+      purgeType: PurgeType.Post,
+      showRemoveDialog: false,
+    });
   }
 
   handlePurgeReasonChange(i: PostListing, event: any) {
-    i.state.purgeReason = Some(event.target.value);
-    i.setState(i.state);
+    i.setState({ purgeReason: Some(event.target.value) });
   }
 
   handlePurgeSubmit(i: PostListing, event: any) {
@@ -1512,29 +1523,24 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
       WebSocketService.Instance.send(wsClient.purgePost(form));
     }
 
-    i.state.purgeLoading = true;
-    i.setState(i.state);
+    i.setState({ purgeLoading: true });
   }
 
   handleModBanReasonChange(i: PostListing, event: any) {
-    i.state.banReason = Some(event.target.value);
-    i.setState(i.state);
+    i.setState({ banReason: Some(event.target.value) });
   }
 
   handleModBanExpireDaysChange(i: PostListing, event: any) {
-    i.state.banExpireDays = Some(event.target.value);
-    i.setState(i.state);
+    i.setState({ banExpireDays: Some(event.target.value) });
   }
 
   handleModBanFromCommunitySubmit(i: PostListing) {
-    i.state.banType = BanType.Community;
-    i.setState(i.state);
+    i.setState({ banType: BanType.Community });
     i.handleModBanBothSubmit(i);
   }
 
   handleModBanSubmit(i: PostListing) {
-    i.state.banType = BanType.Site;
-    i.setState(i.state);
+    i.setState({ banType: BanType.Site });
     i.handleModBanBothSubmit(i);
   }
 
@@ -1545,7 +1551,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
       // If its an unban, restore all their data
       let ban = !i.props.post_view.creator_banned_from_community;
       if (ban == false) {
-        i.state.removeData = false;
+        i.setState({ removeData: false });
       }
       let form = new BanFromCommunity({
         person_id: i.props.post_view.creator.id,
@@ -1561,7 +1567,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
       // If its an unban, restore all their data
       let ban = !i.props.post_view.creator.banned;
       if (ban == false) {
-        i.state.removeData = false;
+        i.setState({ removeData: false });
       }
       let form = new BanPerson({
         person_id: i.props.post_view.creator.id,
@@ -1574,8 +1580,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
       WebSocketService.Instance.send(wsClient.banPerson(form));
     }
 
-    i.state.showBanDialog = false;
-    i.setState(i.state);
+    i.setState({ showBanDialog: false });
   }
 
   handleAddModToCommunity(i: PostListing) {
@@ -1600,13 +1605,11 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
   }
 
   handleShowConfirmTransferCommunity(i: PostListing) {
-    i.state.showConfirmTransferCommunity = true;
-    i.setState(i.state);
+    i.setState({ showConfirmTransferCommunity: true });
   }
 
   handleCancelShowConfirmTransferCommunity(i: PostListing) {
-    i.state.showConfirmTransferCommunity = false;
-    i.setState(i.state);
+    i.setState({ showConfirmTransferCommunity: false });
   }
 
   handleTransferCommunity(i: PostListing) {
@@ -1616,48 +1619,42 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
       auth: auth().unwrap(),
     });
     WebSocketService.Instance.send(wsClient.transferCommunity(form));
-    i.state.showConfirmTransferCommunity = false;
-    i.setState(i.state);
+    i.setState({ showConfirmTransferCommunity: false });
   }
 
   handleShowConfirmTransferSite(i: PostListing) {
-    i.state.showConfirmTransferSite = true;
-    i.setState(i.state);
+    i.setState({ showConfirmTransferSite: true });
   }
 
   handleCancelShowConfirmTransferSite(i: PostListing) {
-    i.state.showConfirmTransferSite = false;
-    i.setState(i.state);
+    i.setState({ showConfirmTransferSite: false });
   }
 
   handleImageExpandClick(i: PostListing, event: any) {
     event.preventDefault();
-    i.state.imageExpanded = !i.state.imageExpanded;
-    i.setState(i.state);
+    i.setState({ imageExpanded: !i.state.imageExpanded });
     setupTippy();
   }
 
   handleViewSource(i: PostListing) {
-    i.state.viewSource = !i.state.viewSource;
-    i.setState(i.state);
+    i.setState({ viewSource: !i.state.viewSource });
   }
 
   handleShowAdvanced(i: PostListing) {
-    i.state.showAdvanced = !i.state.showAdvanced;
-    i.setState(i.state);
+    i.setState({ showAdvanced: !i.state.showAdvanced });
     setupTippy();
   }
 
   handleShowMoreMobile(i: PostListing) {
-    i.state.showMoreMobile = !i.state.showMoreMobile;
-    i.state.showAdvanced = !i.state.showAdvanced;
-    i.setState(i.state);
+    i.setState({
+      showMoreMobile: !i.state.showMoreMobile,
+      showAdvanced: !i.state.showAdvanced,
+    });
     setupTippy();
   }
 
   handleShowBody(i: PostListing) {
-    i.state.showBody = !i.state.showBody;
-    i.setState(i.state);
+    i.setState({ showBody: !i.state.showBody });
     setupTippy();
   }
 
index a26d9d6d4483b3731753d16dcb11e1bfe2f81478..158bd8e425cbaa0a13295f2e950a62c4630968f7 100644 (file)
@@ -42,7 +42,7 @@ export class PostListings extends Component<PostListingsProps, any> {
                 enableDownvotes={this.props.enableDownvotes}
                 enableNsfw={this.props.enableNsfw}
               />
-              <hr class="my-3" />
+              <hr className="my-3" />
             </>
           ))
         ) : (
index 58cf71e8158150850fef8eef730f8733e5686081..2d3f4245af6522172b50f6d5392a5ab332ddd42c 100644 (file)
@@ -120,28 +120,31 @@ export class Post extends Component<any, PostState> {
     super(props, context);
 
     this.state = this.emptyState;
-    this.state.commentSectionRef = createRef();
 
     this.parseMessage = this.parseMessage.bind(this);
     this.subscription = wsSubscribe(this.parseMessage);
 
+    this.state = { ...this.state, commentSectionRef: createRef() };
+
     // Only fetch the data if coming from another route
     if (this.isoData.path == this.context.router.route.match.url) {
-      this.state.postRes = Some(this.isoData.routeData[0] as GetPostResponse);
-      this.state.commentsRes = Some(
-        this.isoData.routeData[1] as GetCommentsResponse
-      );
-
-      this.state.commentsRes.match({
-        some: res => {
-          this.state.commentTree = buildCommentsTree(
-            res.comments,
+      this.state = {
+        ...this.state,
+        postRes: Some(this.isoData.routeData[0] as GetPostResponse),
+        commentsRes: Some(this.isoData.routeData[1] as GetCommentsResponse),
+      };
+
+      if (this.state.commentsRes.isSome()) {
+        this.state = {
+          ...this.state,
+          commentTree: buildCommentsTree(
+            this.state.commentsRes.unwrap().comments,
             this.state.commentId.isSome()
-          );
-        },
-        none: void 0,
-      });
-      this.state.loading = false;
+          ),
+        };
+      }
+
+      this.state = { ...this.state, loading: false };
 
       if (isBrowser()) {
         WebSocketService.Instance.send(
@@ -305,8 +308,9 @@ export class Post extends Component<any, PostState> {
   trackCommentsBoxScrolling = () => {
     const wrappedElement = document.getElementsByClassName("comments")[0];
     if (wrappedElement && this.isBottom(wrappedElement)) {
-      this.state.maxCommentsShown += commentsShownInterval;
-      this.setState(this.state);
+      this.setState({
+        maxCommentsShown: this.state.maxCommentsShown + commentsShownInterval,
+      });
     }
   };
 
@@ -341,7 +345,7 @@ export class Post extends Component<any, PostState> {
 
   render() {
     return (
-      <div class="container">
+      <div className="container">
         {this.state.loading ? (
           <h5>
             <Spinner large />
@@ -349,8 +353,8 @@ export class Post extends Component<any, PostState> {
         ) : (
           this.state.postRes.match({
             some: res => (
-              <div class="row">
-                <div class="col-12 col-md-8 mb-3">
+              <div className="row">
+                <div className="col-12 col-md-8 mb-3">
                   <HtmlTags
                     title={this.documentTitle}
                     path={this.context.router.route.match.url}
@@ -372,9 +376,9 @@ export class Post extends Component<any, PostState> {
                     node={Right(res.post_view.post.id)}
                     disabled={res.post_view.post.locked}
                   />
-                  <div class="d-block d-md-none">
+                  <div className="d-block d-md-none">
                     <button
-                      class="btn btn-secondary d-inline-block mb-2 mr-3"
+                      className="btn btn-secondary d-inline-block mb-2 mr-3"
                       onClick={linkEvent(this, this.handleShowSidebarMobile)}
                     >
                       {i18n.t("sidebar")}{" "}
@@ -395,7 +399,9 @@ export class Post extends Component<any, PostState> {
                   {this.state.commentViewType == CommentViewType.Flat &&
                     this.commentsFlat()}
                 </div>
-                <div class="d-none d-md-block col-md-4">{this.sidebar()}</div>
+                <div className="d-none d-md-block col-md-4">
+                  {this.sidebar()}
+                </div>
               </div>
             ),
             none: <></>,
@@ -408,7 +414,7 @@ export class Post extends Component<any, PostState> {
   sortRadios() {
     return (
       <>
-        <div class="btn-group btn-group-toggle flex-wrap mr-3 mb-2">
+        <div className="btn-group btn-group-toggle flex-wrap mr-3 mb-2">
           <label
             className={`btn btn-outline-secondary pointer ${
               CommentSortType[this.state.commentSort] === CommentSortType.Hot &&
@@ -466,7 +472,7 @@ export class Post extends Component<any, PostState> {
             />
           </label>
         </div>
-        <div class="btn-group btn-group-toggle flex-wrap mb-2">
+        <div className="btn-group btn-group-toggle flex-wrap mb-2">
           <label
             className={`btn btn-outline-secondary pointer ${
               this.state.commentViewType === CommentViewType.Flat && "active"
@@ -514,7 +520,7 @@ export class Post extends Component<any, PostState> {
   sidebar() {
     return this.state.postRes.match({
       some: res => (
-        <div class="mb-3">
+        <div className="mb-3">
           <Sidebar
             community_view={res.community_view}
             moderators={res.moderators}
@@ -530,25 +536,26 @@ export class Post extends Component<any, PostState> {
   }
 
   handleCommentSortChange(i: Post, event: any) {
-    i.state.commentSort = CommentSortType[event.target.value];
-    i.state.commentViewType = CommentViewType.Tree;
-    i.setState(i.state);
+    i.setState({
+      commentSort: CommentSortType[event.target.value],
+      commentViewType: CommentViewType.Tree,
+    });
     i.fetchPost();
   }
 
   handleCommentViewTypeChange(i: Post, event: any) {
-    i.state.commentViewType = Number(event.target.value);
-    i.state.commentSort = CommentSortType.New;
-    i.state.commentTree = buildCommentsTree(
-      i.state.commentsRes.map(r => r.comments).unwrapOr([]),
-      i.state.commentId.isSome()
-    );
-    i.setState(i.state);
+    i.setState({
+      commentViewType: Number(event.target.value),
+      commentSort: CommentSortType.New,
+      commentTree: buildCommentsTree(
+        i.state.commentsRes.map(r => r.comments).unwrapOr([]),
+        i.state.commentId.isSome()
+      ),
+    });
   }
 
   handleShowSidebarMobile(i: Post) {
-    i.state.showSidebarMobile = !i.state.showSidebarMobile;
-    i.setState(i.state);
+    i.setState({ showSidebarMobile: !i.state.showSidebarMobile });
   }
 
   handleViewPost(i: Post) {
@@ -581,14 +588,14 @@ export class Post extends Component<any, PostState> {
           {this.state.commentId.isSome() && (
             <>
               <button
-                class="pl-0 d-block btn btn-link text-muted"
+                className="pl-0 d-block btn btn-link text-muted"
                 onClick={linkEvent(this, this.handleViewPost)}
               >
                 {i18n.t("view_all_comments")} âž”
               </button>
               {showContextButton && (
                 <button
-                  class="pl-0 d-block btn btn-link text-muted"
+                  className="pl-0 d-block btn btn-link text-muted"
                   onClick={linkEvent(this, this.handleViewContext)}
                 >
                   {i18n.t("show_context")} âž”
@@ -636,7 +643,7 @@ export class Post extends Component<any, PostState> {
       });
     } else if (op == UserOperation.GetPost) {
       let data = wsJsonToRes<GetPostResponse>(msg, GetPostResponse);
-      this.state.postRes = Some(data);
+      this.setState({ postRes: Some(data) });
 
       // join the rooms
       WebSocketService.Instance.send(
@@ -651,7 +658,6 @@ export class Post extends Component<any, PostState> {
       // Get cross-posts
       // TODO move this into initial fetch and refetch
       this.fetchCrossPosts();
-      this.setState(this.state);
       setupTippy();
       if (this.state.commentId.isNone()) restoreScrollPosition(this.context);
 
@@ -668,16 +674,16 @@ export class Post extends Component<any, PostState> {
           newComments.shift();
           res.comments.push(...newComments);
         },
-        none: () => {
-          this.state.commentsRes = Some(data);
-        },
+        none: () => this.setState({ commentsRes: Some(data) }),
       });
       // this.state.commentsRes = Some(data);
-      this.state.commentTree = buildCommentsTree(
-        this.state.commentsRes.map(r => r.comments).unwrapOr([]),
-        this.state.commentId.isSome()
-      );
-      this.state.loading = false;
+      this.setState({
+        commentTree: buildCommentsTree(
+          this.state.commentsRes.map(r => r.comments).unwrapOr([]),
+          this.state.commentId.isSome()
+        ),
+        loading: false,
+      });
       this.setState(this.state);
     } else if (op == UserOperation.CreateComment) {
       let data = wsJsonToRes<CommentResponse>(msg, CommentResponse);
@@ -827,19 +833,16 @@ export class Post extends Component<any, PostState> {
       });
     } else if (op == UserOperation.AddAdmin) {
       let data = wsJsonToRes<AddAdminResponse>(msg, AddAdminResponse);
-      this.state.siteRes.admins = data.admins;
-      this.setState(this.state);
+      this.setState(s => ((s.siteRes.admins = data.admins), s));
     } else if (op == UserOperation.Search) {
       let data = wsJsonToRes<SearchResponse>(msg, SearchResponse);
       let xPosts = data.posts.filter(
         p => p.post.id != Number(this.props.match.params.id)
       );
-      this.state.crossPosts = xPosts.length > 0 ? Some(xPosts) : None;
-      this.setState(this.state);
+      this.setState({ crossPosts: xPosts.length > 0 ? Some(xPosts) : None });
     } else if (op == UserOperation.LeaveAdmin) {
       let data = wsJsonToRes<GetSiteResponse>(msg, GetSiteResponse);
-      this.state.siteRes = data;
-      this.setState(this.state);
+      this.setState({ siteRes: data });
     } else if (op == UserOperation.TransferCommunity) {
       let data = wsJsonToRes<GetCommunityResponse>(msg, GetCommunityResponse);
       this.state.postRes.match({
index a93ba99ccf42a330a83dce42e90ab5166cb99e03..9cbe47a9f737d723825167036e2b9554f264726f 100644 (file)
@@ -45,6 +45,7 @@ export class CreatePrivateMessage extends Component<
     recipient_id: getRecipientIdFromProps(this.props),
     loading: true,
   };
+
   constructor(props: any, context: any) {
     super(props, context);
     this.state = this.emptyState;
@@ -61,10 +62,13 @@ export class CreatePrivateMessage extends Component<
 
     // Only fetch the data if coming from another route
     if (this.isoData.path == this.context.router.route.match.url) {
-      this.state.recipientDetailsRes = Some(
-        this.isoData.routeData[0] as GetPersonDetailsResponse
-      );
-      this.state.loading = false;
+      this.state = {
+        ...this.state,
+        recipientDetailsRes: Some(
+          this.isoData.routeData[0] as GetPersonDetailsResponse
+        ),
+        loading: false,
+      };
     } else {
       this.fetchPersonDetails();
     }
@@ -115,7 +119,7 @@ export class CreatePrivateMessage extends Component<
 
   render() {
     return (
-      <div class="container">
+      <div className="container">
         <HtmlTags
           title={this.documentTitle}
           path={this.context.router.route.match.url}
@@ -129,8 +133,8 @@ export class CreatePrivateMessage extends Component<
         ) : (
           this.state.recipientDetailsRes.match({
             some: res => (
-              <div class="row">
-                <div class="col-12 col-lg-6 offset-lg-3 mb-4">
+              <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}
@@ -151,7 +155,7 @@ export class CreatePrivateMessage extends Component<
     toast(i18n.t("message_sent"));
 
     // Navigate to the front
-    this.context.router.history.push(`/`);
+    this.context.router.history.push("/");
   }
 
   parseMessage(msg: any) {
@@ -159,17 +163,14 @@ export class CreatePrivateMessage extends Component<
     console.log(msg);
     if (msg.error) {
       toast(i18n.t(msg.error), "danger");
-      this.state.loading = false;
-      this.setState(this.state);
+      this.setState({ loading: false });
       return;
     } else if (op == UserOperation.GetPersonDetails) {
       let data = wsJsonToRes<GetPersonDetailsResponse>(
         msg,
         GetPersonDetailsResponse
       );
-      this.state.recipientDetailsRes = Some(data);
-      this.state.loading = false;
-      this.setState(this.state);
+      this.setState({ recipientDetailsRes: Some(data), loading: false });
     }
   }
 }
index 62649816d0ea37b72efd84deadb97a2f96532582..46953d723746a3f4c161565be04c44f824d303d1 100644 (file)
@@ -71,11 +71,10 @@ export class PrivateMessageForm extends Component<
     this.subscription = wsSubscribe(this.parseMessage);
 
     // Its an edit
-    this.props.privateMessageView.match({
-      some: pm =>
-        (this.state.privateMessageForm.content = pm.private_message.content),
-      none: void 0,
-    });
+    if (this.props.privateMessageView.isSome()) {
+      this.state.privateMessageForm.content =
+        this.props.privateMessageView.unwrap().private_message.content;
+    }
   }
 
   componentDidMount() {
@@ -106,21 +105,21 @@ export class PrivateMessageForm extends Component<
         />
         <form onSubmit={linkEvent(this, this.handlePrivateMessageSubmit)}>
           {this.props.privateMessageView.isNone() && (
-            <div class="form-group row">
-              <label class="col-sm-2 col-form-label">
+            <div className="form-group row">
+              <label className="col-sm-2 col-form-label">
                 {capitalizeFirstLetter(i18n.t("to"))}
               </label>
 
-              <div class="col-sm-10 form-control-plaintext">
+              <div className="col-sm-10 form-control-plaintext">
                 <PersonListing person={this.props.recipient} />
               </div>
             </div>
           )}
-          <div class="form-group row">
-            <label class="col-sm-2 col-form-label">
+          <div className="form-group row">
+            <label className="col-sm-2 col-form-label">
               {i18n.t("message")}
               <button
-                class="btn btn-link text-warning d-inline-block"
+                className="btn btn-link text-warning d-inline-block"
                 onClick={linkEvent(this, this.handleShowDisclaimer)}
                 data-tippy-content={i18n.t("private_message_disclaimer")}
                 aria-label={i18n.t("private_message_disclaimer")}
@@ -128,7 +127,7 @@ export class PrivateMessageForm extends Component<
                 <Icon icon="alert-triangle" classes="icon-inline" />
               </button>
             </label>
-            <div class="col-sm-10">
+            <div className="col-sm-10">
               <MarkdownTextArea
                 initialContent={Some(this.state.privateMessageForm.content)}
                 placeholder={None}
@@ -140,13 +139,13 @@ export class PrivateMessageForm extends Component<
           </div>
 
           {this.state.showDisclaimer && (
-            <div class="form-group row">
-              <div class="offset-sm-2 col-sm-10">
-                <div class="alert alert-danger" role="alert">
+            <div className="form-group row">
+              <div className="offset-sm-2 col-sm-10">
+                <div className="alert alert-danger" role="alert">
                   <T i18nKey="private_message_disclaimer">
                     #
                     <a
-                      class="alert-link"
+                      className="alert-link"
                       rel={relTags}
                       href="https://element.io/get-started"
                     >
@@ -157,11 +156,11 @@ export class PrivateMessageForm extends Component<
               </div>
             </div>
           )}
-          <div class="form-group row">
-            <div class="offset-sm-2 col-sm-10">
+          <div className="form-group row">
+            <div className="offset-sm-2 col-sm-10">
               <button
                 type="submit"
-                class="btn btn-secondary mr-2"
+                className="btn btn-secondary mr-2"
                 disabled={this.state.loading}
               >
                 {this.state.loading ? (
@@ -175,14 +174,14 @@ export class PrivateMessageForm extends Component<
               {this.props.privateMessageView.isSome() && (
                 <button
                   type="button"
-                  class="btn btn-secondary"
+                  className="btn btn-secondary"
                   onClick={linkEvent(this, this.handleCancel)}
                 >
                   {i18n.t("cancel")}
                 </button>
               )}
-              <ul class="d-inline-block float-right list-inline mb-1 text-muted font-weight-bold">
-                <li class="list-inline-item"></li>
+              <ul className="d-inline-block float-right list-inline mb-1 text-muted font-weight-bold">
+                <li className="list-inline-item"></li>
               </ul>
             </div>
           </div>
@@ -206,13 +205,11 @@ export class PrivateMessageForm extends Component<
         wsClient.createPrivateMessage(i.state.privateMessageForm)
       ),
     });
-    i.state.loading = true;
-    i.setState(i.state);
+    i.setState({ loading: true });
   }
 
   handleContentChange(val: string) {
-    this.state.privateMessageForm.content = val;
-    this.setState(this.state);
+    this.setState(s => ((s.privateMessageForm.content = val), s));
   }
 
   handleCancel(i: PrivateMessageForm) {
@@ -221,13 +218,11 @@ export class PrivateMessageForm extends Component<
 
   handlePreviewToggle(i: PrivateMessageForm, event: any) {
     event.preventDefault();
-    i.state.previewMode = !i.state.previewMode;
-    i.setState(i.state);
+    i.setState({ previewMode: !i.state.previewMode });
   }
 
   handleShowDisclaimer(i: PrivateMessageForm) {
-    i.state.showDisclaimer = !i.state.showDisclaimer;
-    i.setState(i.state);
+    i.setState({ showDisclaimer: !i.state.showDisclaimer });
   }
 
   parseMessage(msg: any) {
@@ -235,8 +230,7 @@ export class PrivateMessageForm extends Component<
     console.log(msg);
     if (msg.error) {
       toast(i18n.t(msg.error), "danger");
-      this.state.loading = false;
-      this.setState(this.state);
+      this.setState({ loading: false });
       return;
     } else if (
       op == UserOperation.EditPrivateMessage ||
@@ -247,16 +241,14 @@ export class PrivateMessageForm extends Component<
         msg,
         PrivateMessageResponse
       );
-      this.state.loading = false;
+      this.setState({ loading: false });
       this.props.onEdit(data.private_message_view);
     } else if (op == UserOperation.CreatePrivateMessage) {
       let data = wsJsonToRes<PrivateMessageResponse>(
         msg,
         PrivateMessageResponse
       );
-      this.state.loading = false;
       this.props.onCreate(data.private_message_view);
-      this.setState(this.state);
     }
   }
 }
index 57662606518f421cd08e384c3368b28269e28464..436a07a53a482fa8188534bcd80249c92ebcc97f 100644 (file)
@@ -63,9 +63,9 @@ export class PrivateMessage extends Component<
       : message_view.creator;
 
     return (
-      <div class="border-top border-light">
+      <div className="border-top border-light">
         <div>
-          <ul class="list-inline mb-0 text-muted small">
+          <ul className="list-inline mb-0 text-muted small">
             {/* TODO refactor this */}
             <li className="list-inline-item">
               {this.mine ? i18n.t("to") : i18n.t("from")}
@@ -114,12 +114,12 @@ export class PrivateMessage extends Component<
                   dangerouslySetInnerHTML={mdToHtml(this.messageUnlessRemoved)}
                 />
               )}
-              <ul class="list-inline mb-0 text-muted font-weight-bold">
+              <ul className="list-inline mb-0 text-muted font-weight-bold">
                 {!this.mine && (
                   <>
                     <li className="list-inline-item">
                       <button
-                        class="btn btn-link btn-animate text-muted"
+                        className="btn btn-link btn-animate text-muted"
                         onClick={linkEvent(this, this.handleMarkRead)}
                         data-tippy-content={
                           message_view.private_message.read
@@ -142,7 +142,7 @@ export class PrivateMessage extends Component<
                     </li>
                     <li className="list-inline-item">
                       <button
-                        class="btn btn-link btn-animate text-muted"
+                        className="btn btn-link btn-animate text-muted"
                         onClick={linkEvent(this, this.handleReplyClick)}
                         data-tippy-content={i18n.t("reply")}
                         aria-label={i18n.t("reply")}
@@ -156,7 +156,7 @@ export class PrivateMessage extends Component<
                   <>
                     <li className="list-inline-item">
                       <button
-                        class="btn btn-link btn-animate text-muted"
+                        className="btn btn-link btn-animate text-muted"
                         onClick={linkEvent(this, this.handleEditClick)}
                         data-tippy-content={i18n.t("edit")}
                         aria-label={i18n.t("edit")}
@@ -166,7 +166,7 @@ export class PrivateMessage extends Component<
                     </li>
                     <li className="list-inline-item">
                       <button
-                        class="btn btn-link btn-animate text-muted"
+                        className="btn btn-link btn-animate text-muted"
                         onClick={linkEvent(this, this.handleDeleteClick)}
                         data-tippy-content={
                           !message_view.private_message.deleted
@@ -192,7 +192,7 @@ export class PrivateMessage extends Component<
                 )}
                 <li className="list-inline-item">
                   <button
-                    class="btn btn-link btn-animate text-muted"
+                    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")}
@@ -217,7 +217,7 @@ export class PrivateMessage extends Component<
           />
         )}
         {/* A collapsed clearfix */}
-        {this.state.collapsed && <div class="row col-12"></div>}
+        {this.state.collapsed && <div className="row col-12"></div>}
       </div>
     );
   }
@@ -228,12 +228,11 @@ export class PrivateMessage extends Component<
   }
 
   handleReplyClick(i: PrivateMessage) {
-    i.state.showReply = true;
-    i.setState(i.state);
+    i.setState({ showReply: true });
   }
 
   handleEditClick(i: PrivateMessage) {
-    i.state.showEdit = true;
+    i.setState({ showEdit: true });
     i.setState(i.state);
   }
 
@@ -247,9 +246,7 @@ export class PrivateMessage extends Component<
   }
 
   handleReplyCancel() {
-    this.state.showReply = false;
-    this.state.showEdit = false;
-    this.setState(this.state);
+    this.setState({ showReply: false, showEdit: false });
   }
 
   handleMarkRead(i: PrivateMessage) {
@@ -262,26 +259,22 @@ export class PrivateMessage extends Component<
   }
 
   handleMessageCollapse(i: PrivateMessage) {
-    i.state.collapsed = !i.state.collapsed;
-    i.setState(i.state);
+    i.setState({ collapsed: !i.state.collapsed });
   }
 
   handleViewSource(i: PrivateMessage) {
-    i.state.viewSource = !i.state.viewSource;
-    i.setState(i.state);
+    i.setState({ viewSource: !i.state.viewSource });
   }
 
   handlePrivateMessageEdit() {
-    this.state.showEdit = false;
-    this.setState(this.state);
+    this.setState({ showEdit: false });
   }
 
   handlePrivateMessageCreate(message: PrivateMessageView) {
     UserService.Instance.myUserInfo.match({
       some: mui => {
         if (message.creator.id == mui.local_user_view.person.id) {
-          this.state.showReply = false;
-          this.setState(this.state);
+          this.setState({ showReply: false });
           toast(i18n.t("message_sent"));
         }
       },
index 30bd9b88c2380220530e0eed0c4fa171d6a76711..09f28f9b9389162cbb5c72a53dbee0c4ff394520 100644 (file)
@@ -200,28 +200,36 @@ export class Search extends Component<any, SearchState> {
       );
 
       // This can be single or multiple communities given
-      communitiesRes.match({
-        some: res => (this.state.communities = res.communities),
-        none: void 0,
-      });
+      if (communitiesRes.isSome()) {
+        this.state = {
+          ...this.state,
+          communities: communitiesRes.unwrap().communities,
+        };
+      }
 
-      communityRes.match({
-        some: res => (this.state.communities = [res.community_view]),
-        none: void 0,
-      });
+      if (communityRes.isSome()) {
+        this.state = {
+          ...this.state,
+          communities: [communityRes.unwrap().community_view],
+        };
+      }
 
-      this.state.creatorDetails = Some(
-        this.isoData.routeData[2] as GetPersonDetailsResponse
-      );
+      this.state = {
+        ...this.state,
+        creatorDetails: Some(
+          this.isoData.routeData[2] as GetPersonDetailsResponse
+        ),
+      };
 
       if (this.state.q != "") {
-        this.state.searchResponse = Some(
-          this.isoData.routeData[3] as SearchResponse
-        );
-        this.state.resolveObjectResponse = Some(
-          this.isoData.routeData[4] as ResolveObjectResponse
-        );
-        this.state.loading = false;
+        this.state = {
+          ...this.state,
+          searchResponse: Some(this.isoData.routeData[3] as SearchResponse),
+          resolveObjectResponse: Some(
+            this.isoData.routeData[4] as ResolveObjectResponse
+          ),
+          loading: false,
+        };
       } else {
         this.search();
       }
@@ -382,7 +390,7 @@ export class Search extends Component<any, SearchState> {
 
   render() {
     return (
-      <div class="container">
+      <div className="container">
         <HtmlTags
           title={this.documentTitle}
           path={this.context.router.route.match.url}
@@ -407,12 +415,12 @@ export class Search extends Component<any, SearchState> {
   searchForm() {
     return (
       <form
-        class="form-inline"
+        className="form-inline"
         onSubmit={linkEvent(this, this.handleSearchSubmit)}
       >
         <input
           type="text"
-          class="form-control mr-2 mb-2"
+          className="form-control mr-2 mb-2"
           value={this.state.searchText}
           placeholder={`${i18n.t("search")}...`}
           aria-label={i18n.t("search")}
@@ -420,7 +428,7 @@ export class Search extends Component<any, SearchState> {
           required
           minLength={1}
         />
-        <button type="submit" class="btn btn-secondary mr-2 mb-2">
+        <button type="submit" className="btn btn-secondary mr-2 mb-2">
           {this.state.loading ? <Spinner /> : <span>{i18n.t("search")}</span>}
         </button>
       </form>
@@ -433,7 +441,7 @@ export class Search extends Component<any, SearchState> {
         <select
           value={this.state.type_}
           onChange={linkEvent(this, this.handleTypeChange)}
-          class="custom-select w-auto mb-2"
+          className="custom-select w-auto mb-2"
           aria-label={i18n.t("type")}
         >
           <option disabled aria-hidden="true">
@@ -448,7 +456,7 @@ export class Search extends Component<any, SearchState> {
           <option value={SearchType.Users}>{i18n.t("users")}</option>
           <option value={SearchType.Url}>{i18n.t("url")}</option>
         </select>
-        <span class="ml-2">
+        <span className="ml-2">
           <ListingTypeSelect
             type_={this.state.listingType}
             showLocal={showLocal(this.isoData)}
@@ -456,7 +464,7 @@ export class Search extends Component<any, SearchState> {
             onChange={this.handleListingTypeChange}
           />
         </span>
-        <span class="ml-2">
+        <span className="ml-2">
           <SortSelect
             sort={this.state.sort}
             onChange={this.handleSortChange}
@@ -464,7 +472,7 @@ export class Search extends Component<any, SearchState> {
             hideMostComments
           />
         </span>
-        <div class="form-row">
+        <div className="form-row">
           {this.state.communities.length > 0 && this.communityFilter()}
           {this.creatorFilter()}
         </div>
@@ -577,8 +585,8 @@ export class Search extends Component<any, SearchState> {
     return (
       <div>
         {combined.map(i => (
-          <div class="row">
-            <div class="col-12">
+          <div key={i.published} className="row">
+            <div className="col-12">
               {i.type_ == "posts" && (
                 <PostListing
                   key={(i.data as PostView).post.id}
@@ -616,7 +624,7 @@ export class Search extends Component<any, SearchState> {
                 <div>{this.communityListing(i.data as CommunityView)}</div>
               )}
               {i.type_ == "users" && (
-                <div>{this.userListing(i.data as PersonViewSafe)}</div>
+                <div>{this.personListing(i.data as PersonViewSafe)}</div>
               )}
             </div>
           </div>
@@ -666,11 +674,11 @@ export class Search extends Component<any, SearchState> {
 
     return (
       <>
-        {posts.map(post => (
-          <div class="row">
-            <div class="col-12">
+        {posts.map(pv => (
+          <div key={pv.post.id} className="row">
+            <div className="col-12">
               <PostListing
-                post_view={post}
+                post_view={pv}
                 showCommunity
                 duplicates={None}
                 moderators={None}
@@ -700,9 +708,9 @@ export class Search extends Component<any, SearchState> {
 
     return (
       <>
-        {communities.map(community => (
-          <div class="row">
-            <div class="col-12">{this.communityListing(community)}</div>
+        {communities.map(cv => (
+          <div key={cv.community.id} className="row">
+            <div className="col-12">{this.communityListing(cv)}</div>
           </div>
         ))}
       </>
@@ -723,9 +731,9 @@ export class Search extends Component<any, SearchState> {
 
     return (
       <>
-        {users.map(user => (
-          <div class="row">
-            <div class="col-12">{this.userListing(user)}</div>
+        {users.map(pvs => (
+          <div key={pvs.person.id} className="row">
+            <div className="col-12">{this.personListing(pvs)}</div>
           </div>
         ))}
       </>
@@ -748,33 +756,37 @@ export class Search extends Component<any, SearchState> {
     );
   }
 
-  userListing(person_view: PersonViewSafe) {
-    return [
-      <span>
-        <PersonListing person={person_view.person} showApubName />
-      </span>,
-      <span>{` - ${i18n.t("number_of_comments", {
-        count: person_view.counts.comment_count,
-        formattedCount: numToSI(person_view.counts.comment_count),
-      })}`}</span>,
-    ];
+  personListing(person_view: PersonViewSafe) {
+    return (
+      <>
+        <span>
+          <PersonListing person={person_view.person} showApubName />
+        </span>
+        <span>{` - ${i18n.t("number_of_comments", {
+          count: person_view.counts.comment_count,
+          formattedCount: numToSI(person_view.counts.comment_count),
+        })}`}</span>
+      </>
+    );
   }
 
   communityFilter() {
     return (
-      <div class="form-group col-sm-6">
-        <label class="col-form-label" htmlFor="community-filter">
+      <div className="form-group col-sm-6">
+        <label className="col-form-label" htmlFor="community-filter">
           {i18n.t("community")}
         </label>
         <div>
           <select
-            class="form-control"
+            className="form-control"
             id="community-filter"
             value={this.state.communityId}
           >
             <option value="0">{i18n.t("all")}</option>
             {this.state.communities.map(cv => (
-              <option value={cv.community.id}>{communitySelectName(cv)}</option>
+              <option key={cv.community.id} value={cv.community.id}>
+                {communitySelectName(cv)}
+              </option>
             ))}
           </select>
         </div>
@@ -784,13 +796,13 @@ export class Search extends Component<any, SearchState> {
 
   creatorFilter() {
     return (
-      <div class="form-group col-sm-6">
-        <label class="col-form-label" htmlFor="creator-filter">
+      <div className="form-group col-sm-6">
+        <label className="col-form-label" htmlFor="creator-filter">
           {capitalizeFirstLetter(i18n.t("creator"))}
         </label>
         <div>
           <select
-            class="form-control"
+            className="form-control"
             id="creator-filter"
             value={this.state.creatorId}
           >
@@ -856,10 +868,11 @@ export class Search extends Component<any, SearchState> {
     });
 
     if (this.state.q != "") {
-      this.state.searchResponse = None;
-      this.state.resolveObjectResponse = None;
-      this.state.loading = true;
-      this.setState(this.state);
+      this.setState({
+        searchResponse: None,
+        resolveObjectResponse: None,
+        loading: true,
+      });
       WebSocketService.Instance.send(wsClient.search(form));
       WebSocketService.Instance.send(wsClient.resolveObject(resolveObjectForm));
     }
@@ -1000,11 +1013,13 @@ export class Search extends Component<any, SearchState> {
     let op = wsUserOp(msg);
     if (msg.error) {
       if (msg.error == "couldnt_find_object") {
-        this.state.resolveObjectResponse = Some({
-          comment: None,
-          post: None,
-          community: None,
-          person: None,
+        this.setState({
+          resolveObjectResponse: Some({
+            comment: None,
+            post: None,
+            community: None,
+            person: None,
+          }),
         });
         this.checkFinishedLoading();
       } else {
@@ -1013,7 +1028,7 @@ export class Search extends Component<any, SearchState> {
       }
     } else if (op == UserOperation.Search) {
       let data = wsJsonToRes<SearchResponse>(msg, SearchResponse);
-      this.state.searchResponse = Some(data);
+      this.setState({ searchResponse: Some(data) });
       window.scrollTo(0, 0);
       this.checkFinishedLoading();
       restoreScrollPosition(this.context);
@@ -1036,12 +1051,11 @@ export class Search extends Component<any, SearchState> {
         msg,
         ListCommunitiesResponse
       );
-      this.state.communities = data.communities;
-      this.setState(this.state);
+      this.setState({ communities: data.communities });
       this.setupCommunityFilter();
     } else if (op == UserOperation.ResolveObject) {
       let data = wsJsonToRes<ResolveObjectResponse>(msg, ResolveObjectResponse);
-      this.state.resolveObjectResponse = Some(data);
+      this.setState({ resolveObjectResponse: Some(data) });
       this.checkFinishedLoading();
     }
   }
@@ -1051,8 +1065,7 @@ export class Search extends Component<any, SearchState> {
       this.state.searchResponse.isSome() &&
       this.state.resolveObjectResponse.isSome()
     ) {
-      this.state.loading = false;
-      this.setState(this.state);
+      this.setState({ loading: false });
     }
   }
 }
index 695ba1e6f392d910e258f44cead4972889928bb2..69452f798ca10d746becc6a1bde1ea146cef9cd8 100644 (file)
@@ -1360,7 +1360,7 @@ export const choicesModLogConfig = {
   shouldSort: false,
   searchResultLimit: fetchLimit,
   classNames: {
-    containerOuter: "choices mb-2 custom-select col-4 px-0",
+    containerOuter: "choices mb-2 custom-select px-0",
     containerInner:
       "choices__inner bg-secondary border-0 py-0 modlog-choices-font-size",
     input: "form-control",
index 93f1f5d0f233e6a7b8ce15bdffd9dfdc74cc4c59..8d0e3f52529642cf815e0e57984f2997f549a107 100644 (file)
--- a/yarn.lock
+++ b/yarn.lock
@@ -3,11 +3,12 @@
 
 
 "@ampproject/remapping@^2.1.0":
-  version "2.1.2"
-  resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.1.2.tgz#4edca94973ded9630d20101cd8559cedb8d8bd34"
-  integrity sha512-hoyByceqwKirw7w3Z7gnIIZC3Wx3J484Y3L/cMpXFbr7d9ZQj2mODrirNzcJa+SM3UlpWXYvKV4RlRpFXlWgXg==
+  version "2.2.0"
+  resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.2.0.tgz#56c133824780de3174aed5ab6834f3026790154d"
+  integrity sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==
   dependencies:
-    "@jridgewell/trace-mapping" "^0.3.0"
+    "@jridgewell/gen-mapping" "^0.1.0"
+    "@jridgewell/trace-mapping" "^0.3.9"
 
 "@babel/code-frame@^7.16.7":
   version "7.16.7"
   resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.16.8.tgz#31560f9f29fdf1868de8cb55049538a1b9732a60"
   integrity sha512-m7OkX0IdKLKPpBlJtF561YJal5y/jyI5fNfWbPxh2D/nbzzGI4qRyrD8xO2jB24u7l+5I2a43scCG2IrfjC50Q==
 
-"@babel/compat-data@^7.17.7":
-  version "7.17.7"
-  resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.17.7.tgz#078d8b833fbbcc95286613be8c716cef2b519fa2"
-  integrity sha512-p8pdE6j0a29TNGebNm7NzYZWB3xVZJBZ7XGs42uAKzQo8VQ3F0By/cQCtUEABwIqw5zo6WA4NbmxsfzADzMKnQ==
-
-"@babel/compat-data@^7.18.8":
-  version "7.18.8"
-  resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.18.8.tgz#2483f565faca607b8535590e84e7de323f27764d"
-  integrity sha512-HSmX4WZPPK3FUxYp7g2T6EyO8j96HlZJlxmKPSh6KAcqwyDrfx7hKjXpAW/0FhFfTJsR0Yt4lAjLI2coMptIHQ==
+"@babel/compat-data@^7.17.7", "@babel/compat-data@^7.18.8", "@babel/compat-data@^7.19.1":
+  version "7.19.1"
+  resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.19.1.tgz#72d647b4ff6a4f82878d184613353af1dd0290f9"
+  integrity sha512-72a9ghR0gnESIa7jBN53U32FOVCEoztyIlKaNoU05zRhEecduGK9L9c3ww7Mp06JiR+0ls0GBPFJQwwtjn9ksg==
 
 "@babel/core@^7.18.9":
-  version "7.18.9"
-  resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.18.9.tgz#805461f967c77ff46c74ca0460ccf4fe933ddd59"
-  integrity sha512-1LIb1eL8APMy91/IMW+31ckrfBM4yCoLaVzoDhZUKSM4cu1L1nIidyxkCgzPAgrC5WEz36IPEr/eSeSF9pIn+g==
+  version "7.19.1"
+  resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.19.1.tgz#c8fa615c5e88e272564ace3d42fbc8b17bfeb22b"
+  integrity sha512-1H8VgqXme4UXCRv7/Wa1bq7RVymKOzC7znjyFM8KiEzwFqcKUKYNoQef4GhdklgNvoBXyW4gYhuBNCM5o1zImw==
   dependencies:
     "@ampproject/remapping" "^2.1.0"
     "@babel/code-frame" "^7.18.6"
-    "@babel/generator" "^7.18.9"
-    "@babel/helper-compilation-targets" "^7.18.9"
-    "@babel/helper-module-transforms" "^7.18.9"
-    "@babel/helpers" "^7.18.9"
-    "@babel/parser" "^7.18.9"
-    "@babel/template" "^7.18.6"
-    "@babel/traverse" "^7.18.9"
-    "@babel/types" "^7.18.9"
+    "@babel/generator" "^7.19.0"
+    "@babel/helper-compilation-targets" "^7.19.1"
+    "@babel/helper-module-transforms" "^7.19.0"
+    "@babel/helpers" "^7.19.0"
+    "@babel/parser" "^7.19.1"
+    "@babel/template" "^7.18.10"
+    "@babel/traverse" "^7.19.1"
+    "@babel/types" "^7.19.0"
     convert-source-map "^1.7.0"
     debug "^4.1.0"
     gensync "^1.0.0-beta.2"
     jsesc "^2.5.1"
     source-map "^0.5.0"
 
-"@babel/generator@^7.18.9":
-  version "7.18.9"
-  resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.18.9.tgz#68337e9ea8044d6ddc690fb29acae39359cca0a5"
-  integrity sha512-wt5Naw6lJrL1/SGkipMiFxJjtyczUWTP38deiP1PO60HsBjDeKk08CGC3S8iVuvf0FmTdgKwU1KIXzSKL1G0Ug==
+"@babel/generator@^7.19.0":
+  version "7.19.0"
+  resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.19.0.tgz#785596c06425e59334df2ccee63ab166b738419a"
+  integrity sha512-S1ahxf1gZ2dpoiFgA+ohK9DIpz50bJ0CWs7Zlzb54Z4sG8qmdIrGrVqmy1sAtTVRb+9CU6U8VqT9L0Zj7hxHVg==
   dependencies:
-    "@babel/types" "^7.18.9"
+    "@babel/types" "^7.19.0"
     "@jridgewell/gen-mapping" "^0.3.2"
     jsesc "^2.5.1"
 
     browserslist "^4.17.5"
     semver "^6.3.0"
 
-"@babel/helper-compilation-targets@^7.17.7":
-  version "7.17.7"
-  resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.17.7.tgz#a3c2924f5e5f0379b356d4cfb313d1414dc30e46"
-  integrity sha512-UFzlz2jjd8kroj0hmCFV5zr+tQPi1dpC2cRsDV/3IEW8bJfCPrPpmcSN6ZS8RqIq4LXcmpipCQFPddyFA5Yc7w==
+"@babel/helper-compilation-targets@^7.17.7", "@babel/helper-compilation-targets@^7.18.9", "@babel/helper-compilation-targets@^7.19.0", "@babel/helper-compilation-targets@^7.19.1":
+  version "7.19.1"
+  resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.19.1.tgz#7f630911d83b408b76fe584831c98e5395d7a17c"
+  integrity sha512-LlLkkqhCMyz2lkQPvJNdIYU7O5YjWRgC2R4omjCTpZd8u8KMQzZvX4qce+/BluN1rcQiV7BoGUpmQ0LeHerbhg==
   dependencies:
-    "@babel/compat-data" "^7.17.7"
-    "@babel/helper-validator-option" "^7.16.7"
-    browserslist "^4.17.5"
-    semver "^6.3.0"
-
-"@babel/helper-compilation-targets@^7.18.9":
-  version "7.18.9"
-  resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.18.9.tgz#69e64f57b524cde3e5ff6cc5a9f4a387ee5563bf"
-  integrity sha512-tzLCyVmqUiFlcFoAPLA/gL9TeYrF61VLNtb+hvkuVaB5SUjW7jcfrglBIX1vUIoT7CLP3bBlIMeyEsIl2eFQNg==
-  dependencies:
-    "@babel/compat-data" "^7.18.8"
+    "@babel/compat-data" "^7.19.1"
     "@babel/helper-validator-option" "^7.18.6"
-    browserslist "^4.20.2"
+    browserslist "^4.21.3"
     semver "^6.3.0"
 
-"@babel/helper-create-class-features-plugin@^7.18.6", "@babel/helper-create-class-features-plugin@^7.18.9":
-  version "7.18.9"
-  resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.18.9.tgz#d802ee16a64a9e824fcbf0a2ffc92f19d58550ce"
-  integrity sha512-WvypNAYaVh23QcjpMR24CwZY2Nz6hqdOcFdPbNpV56hL5H6KiFheO7Xm1aPdlLQ7d5emYZX7VZwPp9x3z+2opw==
+"@babel/helper-create-class-features-plugin@^7.18.6", "@babel/helper-create-class-features-plugin@^7.19.0":
+  version "7.19.0"
+  resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.19.0.tgz#bfd6904620df4e46470bae4850d66be1054c404b"
+  integrity sha512-NRz8DwF4jT3UfrmUoZjd0Uph9HQnP30t7Ash+weACcyNkiYTywpIjDBgReJMKgr+n86sn2nPVVmJ28Dm053Kqw==
   dependencies:
     "@babel/helper-annotate-as-pure" "^7.18.6"
     "@babel/helper-environment-visitor" "^7.18.9"
-    "@babel/helper-function-name" "^7.18.9"
+    "@babel/helper-function-name" "^7.19.0"
     "@babel/helper-member-expression-to-functions" "^7.18.9"
     "@babel/helper-optimise-call-expression" "^7.18.6"
     "@babel/helper-replace-supers" "^7.18.9"
     "@babel/helper-annotate-as-pure" "^7.18.6"
     regexpu-core "^5.1.0"
 
-"@babel/helper-define-polyfill-provider@^0.3.1", "@babel/helper-define-polyfill-provider@^0.3.2":
-  version "0.3.2"
-  resolved "https://registry.yarnpkg.com/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.3.2.tgz#bd10d0aca18e8ce012755395b05a79f45eca5073"
-  integrity sha512-r9QJJ+uDWrd+94BSPcP6/de67ygLtvVy6cK4luE6MOuDsZIdoaPBnfSpbO/+LTifjPckbKXRuI9BB/Z2/y3iTg==
+"@babel/helper-create-regexp-features-plugin@^7.19.0":
+  version "7.19.0"
+  resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.19.0.tgz#7976aca61c0984202baca73d84e2337a5424a41b"
+  integrity sha512-htnV+mHX32DF81amCDrwIDr8nrp1PTm+3wfBN9/v8QJOLEioOCOG7qNyq0nHeFiWbT3Eb7gsPwEmV64UCQ1jzw==
+  dependencies:
+    "@babel/helper-annotate-as-pure" "^7.18.6"
+    regexpu-core "^5.1.0"
+
+"@babel/helper-define-polyfill-provider@^0.3.3":
+  version "0.3.3"
+  resolved "https://registry.yarnpkg.com/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.3.3.tgz#8612e55be5d51f0cd1f36b4a5a83924e89884b7a"
+  integrity sha512-z5aQKU4IzbqCC1XH0nAqfsFLMVSo22SBKUc0BxGrLkolTdPTructy0ToNnlO2zA4j9Q/7pjMZf0DSY+DSTYzww==
   dependencies:
     "@babel/helper-compilation-targets" "^7.17.7"
     "@babel/helper-plugin-utils" "^7.16.7"
   dependencies:
     "@babel/types" "^7.16.7"
 
-"@babel/helper-environment-visitor@^7.18.6", "@babel/helper-environment-visitor@^7.18.9":
+"@babel/helper-environment-visitor@^7.18.9":
   version "7.18.9"
   resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz#0c0cee9b35d2ca190478756865bb3528422f51be"
   integrity sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg==
     "@babel/template" "^7.16.7"
     "@babel/types" "^7.16.7"
 
-"@babel/helper-function-name@^7.18.9":
-  version "7.18.9"
-  resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.18.9.tgz#940e6084a55dee867d33b4e487da2676365e86b0"
-  integrity sha512-fJgWlZt7nxGksJS9a0XdSaI4XvpExnNIgRP+rVefWh5U7BL8pPuir6SJUmFKRfjWQ51OtWSzwOxhaH/EBWWc0A==
+"@babel/helper-function-name@^7.18.9", "@babel/helper-function-name@^7.19.0":
+  version "7.19.0"
+  resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.19.0.tgz#941574ed5390682e872e52d3f38ce9d1bef4648c"
+  integrity sha512-WAwHBINyrpqywkUH0nTnNgI5ina5TFn85HKS0pbPDfxFfhyR/aNQEn4hGi1P1JyT//I0t4OgXUlofzWILRvS5w==
   dependencies:
-    "@babel/template" "^7.18.6"
-    "@babel/types" "^7.18.9"
+    "@babel/template" "^7.18.10"
+    "@babel/types" "^7.19.0"
 
 "@babel/helper-get-function-arity@^7.16.7":
   version "7.16.7"
     "@babel/traverse" "^7.16.7"
     "@babel/types" "^7.16.7"
 
-"@babel/helper-module-transforms@^7.18.6", "@babel/helper-module-transforms@^7.18.9":
+"@babel/helper-module-transforms@^7.18.6":
   version "7.18.9"
   resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.18.9.tgz#5a1079c005135ed627442df31a42887e80fcb712"
   integrity sha512-KYNqY0ICwfv19b31XzvmI/mfcylOzbLtowkw+mfvGPAQ3kfCnMLYbED3YecL5tPd8nAYFQFAd6JHp2LxZk/J1g==
     "@babel/traverse" "^7.18.9"
     "@babel/types" "^7.18.9"
 
+"@babel/helper-module-transforms@^7.19.0":
+  version "7.19.0"
+  resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.19.0.tgz#309b230f04e22c58c6a2c0c0c7e50b216d350c30"
+  integrity sha512-3HBZ377Fe14RbLIA+ac3sY4PTgpxHVkFrESaWhoI5PuyXPBBX8+C34qblV9G89ZtycGJCmCI/Ut+VUDK4bltNQ==
+  dependencies:
+    "@babel/helper-environment-visitor" "^7.18.9"
+    "@babel/helper-module-imports" "^7.18.6"
+    "@babel/helper-simple-access" "^7.18.6"
+    "@babel/helper-split-export-declaration" "^7.18.6"
+    "@babel/helper-validator-identifier" "^7.18.6"
+    "@babel/template" "^7.18.10"
+    "@babel/traverse" "^7.19.0"
+    "@babel/types" "^7.19.0"
+
 "@babel/helper-optimise-call-expression@^7.18.6":
   version "7.18.6"
   resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.18.6.tgz#9369aa943ee7da47edab2cb4e838acf09d290ffe"
   dependencies:
     "@babel/types" "^7.18.6"
 
-"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.14.5", "@babel/helper-plugin-utils@^7.16.7", "@babel/helper-plugin-utils@^7.8.0", "@babel/helper-plugin-utils@^7.8.3":
+"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.14.5", "@babel/helper-plugin-utils@^7.8.0", "@babel/helper-plugin-utils@^7.8.3":
   version "7.16.7"
   resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.16.7.tgz#aa3a8ab4c3cceff8e65eb9e73d87dc4ff320b2f5"
   integrity sha512-Qg3Nk7ZxpgMrsox6HreY1ZNKdBq7K72tDSliA6dCl5f007jR4ne8iD5UzuNnCJH2xBf2BEEVGr+/OL6Gdp7RxA==
 
-"@babel/helper-plugin-utils@^7.18.6", "@babel/helper-plugin-utils@^7.18.9":
-  version "7.18.9"
-  resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.18.9.tgz#4b8aea3b069d8cb8a72cdfe28ddf5ceca695ef2f"
-  integrity sha512-aBXPT3bmtLryXaoJLyYPXPlSD4p1ld9aYeR+sJNOZjJJGiOpb+fKfh3NkcCu7J54nUJwCERPBExCCpyCOHnu/w==
+"@babel/helper-plugin-utils@^7.16.7", "@babel/helper-plugin-utils@^7.18.6", "@babel/helper-plugin-utils@^7.18.9", "@babel/helper-plugin-utils@^7.19.0":
+  version "7.19.0"
+  resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.19.0.tgz#4796bb14961521f0f8715990bee2fb6e51ce21bf"
+  integrity sha512-40Ryx7I8mT+0gaNxm8JGTZFUITNqdLAgdg0hXzeVZxVD6nFsdhQvip6v8dqkRHzsz1VFpFAaOCHNn0vKBL7Czw==
 
-"@babel/helper-remap-async-to-generator@^7.18.6":
+"@babel/helper-remap-async-to-generator@^7.18.6", "@babel/helper-remap-async-to-generator@^7.18.9":
   version "7.18.9"
   resolved "https://registry.yarnpkg.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.18.9.tgz#997458a0e3357080e54e1d79ec347f8a8cd28519"
   integrity sha512-dI7q50YKd8BAv3VEfgg7PS7yD3Rtbi2J1XMXaalXO0W0164hYLnh8zpjRS0mte9MfVp/tltvr/cfdXPvJr1opA==
     "@babel/helper-wrap-function" "^7.18.9"
     "@babel/types" "^7.18.9"
 
-"@babel/helper-replace-supers@^7.18.6", "@babel/helper-replace-supers@^7.18.9":
+"@babel/helper-replace-supers@^7.18.6":
   version "7.18.9"
   resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.18.9.tgz#1092e002feca980fbbb0bd4d51b74a65c6a500e6"
   integrity sha512-dNsWibVI4lNT6HiuOIBr1oyxo40HvIVmbwPUm3XZ7wMh4k2WxrxTqZwSqw/eEmXDS9np0ey5M2bz9tBmO9c+YQ==
     "@babel/traverse" "^7.18.9"
     "@babel/types" "^7.18.9"
 
+"@babel/helper-replace-supers@^7.18.9", "@babel/helper-replace-supers@^7.19.1":
+  version "7.19.1"
+  resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.19.1.tgz#e1592a9b4b368aa6bdb8784a711e0bcbf0612b78"
+  integrity sha512-T7ahH7wV0Hfs46SFh5Jz3s0B6+o8g3c+7TMxu7xKfmHikg7EAZ3I2Qk9LFhjxXq8sL7UkP5JflezNwoZa8WvWw==
+  dependencies:
+    "@babel/helper-environment-visitor" "^7.18.9"
+    "@babel/helper-member-expression-to-functions" "^7.18.9"
+    "@babel/helper-optimise-call-expression" "^7.18.6"
+    "@babel/traverse" "^7.19.1"
+    "@babel/types" "^7.19.0"
+
 "@babel/helper-simple-access@^7.16.7":
   version "7.16.7"
   resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.16.7.tgz#d656654b9ea08dbb9659b69d61063ccd343ff0f7"
   dependencies:
     "@babel/types" "^7.18.6"
 
+"@babel/helper-string-parser@^7.18.10":
+  version "7.18.10"
+  resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.18.10.tgz#181f22d28ebe1b3857fa575f5c290b1aaf659b56"
+  integrity sha512-XtIfWmeNY3i4t7t4D2t02q50HvqHybPqW2ki1kosnvWCwuCMeo81Jf0gwr85jy/neUdg5XDdeFE/80DXiO+njw==
+
 "@babel/helper-validator-identifier@^7.16.7":
   version "7.16.7"
   resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.16.7.tgz#e8c602438c4a8195751243da9031d1607d247cad"
   integrity sha512-hsEnFemeiW4D08A5gUAZxLBTXpZ39P+a+DGDsHw1yxqyQ/jzFEnxf5uTEGp+3bzAbNOxU1paTgYS4ECU/IgfDw==
 
 "@babel/helper-validator-identifier@^7.18.6":
-  version "7.18.6"
-  resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.18.6.tgz#9c97e30d31b2b8c72a1d08984f2ca9b574d7a076"
-  integrity sha512-MmetCkz9ej86nJQV+sFCxoGGrUbU3q02kgLciwkrt9QqEB7cP39oKEY0PakknEO0Gu20SskMRi+AYZ3b1TpN9g==
-
-"@babel/helper-validator-option@^7.16.7":
-  version "7.16.7"
-  resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.16.7.tgz#b203ce62ce5fe153899b617c08957de860de4d23"
-  integrity sha512-TRtenOuRUVo9oIQGPC5G9DgK4743cdxvtOw0weQNpZXaS16SCBi5MNjZF8vba3ETURjZpTbVn7Vvcf2eAwFozQ==
+  version "7.19.1"
+  resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz#7eea834cf32901ffdc1a7ee555e2f9c27e249ca2"
+  integrity sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==
 
-"@babel/helper-validator-option@^7.18.6":
+"@babel/helper-validator-option@^7.16.7", "@babel/helper-validator-option@^7.18.6":
   version "7.18.6"
   resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.18.6.tgz#bf0d2b5a509b1f336099e4ff36e1a63aa5db4db8"
   integrity sha512-XO7gESt5ouv/LRJdrVjkShckw6STTaB7l9BrpBaAHDeF5YZT+01PCwmR0SJHnkW6i8OwW/EVWRShfi4j2x+KQw==
     "@babel/traverse" "^7.16.7"
     "@babel/types" "^7.16.7"
 
-"@babel/helpers@^7.18.9":
-  version "7.18.9"
-  resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.18.9.tgz#4bef3b893f253a1eced04516824ede94dcfe7ff9"
-  integrity sha512-Jf5a+rbrLoR4eNdUmnFu8cN5eNJT6qdTdOg5IHIzq87WwyRw9PwguLFOWYgktN/60IP4fgDUawJvs7PjQIzELQ==
+"@babel/helpers@^7.19.0":
+  version "7.19.0"
+  resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.19.0.tgz#f30534657faf246ae96551d88dd31e9d1fa1fc18"
+  integrity sha512-DRBCKGwIEdqY3+rPJgG/dKfQy9+08rHIAJx8q2p+HSWP87s2HCrQmaAMMyMll2kIXKCW0cO1RdQskx15Xakftg==
   dependencies:
-    "@babel/template" "^7.18.6"
-    "@babel/traverse" "^7.18.9"
-    "@babel/types" "^7.18.9"
+    "@babel/template" "^7.18.10"
+    "@babel/traverse" "^7.19.0"
+    "@babel/types" "^7.19.0"
 
 "@babel/highlight@^7.16.7":
   version "7.16.7"
   resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.16.8.tgz#61c243a3875f7d0b0962b0543a33ece6ff2f1f17"
   integrity sha512-i7jDUfrVBWc+7OKcBzEe5n7fbv3i2fWtxKzzCvOjnzSxMfWMigAhtfJ7qzZNGFNMsCCd67+uz553dYKWXPvCKw==
 
-"@babel/parser@^7.18.6", "@babel/parser@^7.18.9":
-  version "7.18.9"
-  resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.18.9.tgz#f2dde0c682ccc264a9a8595efd030a5cc8fd2539"
-  integrity sha512-9uJveS9eY9DJ0t64YbIBZICtJy8a5QrDEVdiLCG97fVLpDTpGX7t8mMSb6OWw6Lrnjqj4O8zwjELX3dhoMgiBg==
+"@babel/parser@^7.18.10", "@babel/parser@^7.19.1":
+  version "7.19.1"
+  resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.19.1.tgz#6f6d6c2e621aad19a92544cc217ed13f1aac5b4c"
+  integrity sha512-h7RCSorm1DdTVGJf3P2Mhj3kdnkmF/EiysUkzS2TdgAYqyjFdMQJbVuXOBej2SBJaXan/lIVtT6KkGbyyq753A==
 
 "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@^7.18.6":
   version "7.18.6"
     "@babel/helper-skip-transparent-expression-wrappers" "^7.18.9"
     "@babel/plugin-proposal-optional-chaining" "^7.18.9"
 
-"@babel/plugin-proposal-async-generator-functions@^7.18.6":
-  version "7.18.6"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.18.6.tgz#aedac81e6fc12bb643374656dd5f2605bf743d17"
-  integrity sha512-WAz4R9bvozx4qwf74M+sfqPMKfSqwM0phxPTR6iJIi8robgzXwkEgmeJG1gEKhm6sDqT/U9aV3lfcqybIpev8w==
+"@babel/plugin-proposal-async-generator-functions@^7.19.1":
+  version "7.19.1"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.19.1.tgz#34f6f5174b688529342288cd264f80c9ea9fb4a7"
+  integrity sha512-0yu8vNATgLy4ivqMNBIwb1HebCelqN7YX8SL3FDXORv/RqT0zEEWUCH4GH44JsSrvCu6GqnAdR5EBFAPeNBB4Q==
   dependencies:
-    "@babel/helper-environment-visitor" "^7.18.6"
-    "@babel/helper-plugin-utils" "^7.18.6"
-    "@babel/helper-remap-async-to-generator" "^7.18.6"
+    "@babel/helper-environment-visitor" "^7.18.9"
+    "@babel/helper-plugin-utils" "^7.19.0"
+    "@babel/helper-remap-async-to-generator" "^7.18.9"
     "@babel/plugin-syntax-async-generators" "^7.8.4"
 
 "@babel/plugin-proposal-class-properties@^7.18.6":
     "@babel/plugin-syntax-class-static-block" "^7.14.5"
 
 "@babel/plugin-proposal-decorators@^7.18.9":
-  version "7.18.9"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.18.9.tgz#d09d41ffc74af8499d2ac706ed0dbd5474711665"
-  integrity sha512-KD7zDNaD14CRpjQjVbV4EnH9lsKYlcpUrhZH37ei2IY+AlXrfAPy5pTmRUE4X6X1k8EsKXPraykxeaogqQvSGA==
+  version "7.19.1"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.19.1.tgz#4bab3e7afe894fdbf47ffa86701266104fcb6ecc"
+  integrity sha512-LfIKNBBY7Q1OX5C4xAgRQffOg2OnhAo9fnbcOHgOC9Yytm2Sw+4XqHufRYU86tHomzepxtvuVaNO+3EVKR4ivw==
   dependencies:
-    "@babel/helper-create-class-features-plugin" "^7.18.9"
-    "@babel/helper-plugin-utils" "^7.18.9"
-    "@babel/helper-replace-supers" "^7.18.9"
+    "@babel/helper-create-class-features-plugin" "^7.19.0"
+    "@babel/helper-plugin-utils" "^7.19.0"
+    "@babel/helper-replace-supers" "^7.19.1"
     "@babel/helper-split-export-declaration" "^7.18.6"
-    "@babel/plugin-syntax-decorators" "^7.18.6"
+    "@babel/plugin-syntax-decorators" "^7.19.0"
 
 "@babel/plugin-proposal-dynamic-import@^7.18.6":
   version "7.18.6"
   dependencies:
     "@babel/helper-plugin-utils" "^7.14.5"
 
-"@babel/plugin-syntax-decorators@^7.18.6":
-  version "7.18.6"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-7.18.6.tgz#2e45af22835d0b0f8665da2bfd4463649ce5dbc1"
-  integrity sha512-fqyLgjcxf/1yhyZ6A+yo1u9gJ7eleFQod2lkaUsF9DQ7sbbY3Ligym3L0+I2c0WmqNKDpoD9UTb1AKP3qRMOAQ==
+"@babel/plugin-syntax-decorators@^7.19.0":
+  version "7.19.0"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-7.19.0.tgz#5f13d1d8fce96951bea01a10424463c9a5b3a599"
+  integrity sha512-xaBZUEDntt4faL1yN8oIFlhfXeQAWJW7CLKYsHTUqriCUbj8xOra8bfxxKGi/UwExPFBuPdH4XfHc9rGQhrVkQ==
   dependencies:
-    "@babel/helper-plugin-utils" "^7.18.6"
+    "@babel/helper-plugin-utils" "^7.19.0"
 
 "@babel/plugin-syntax-dynamic-import@^7.8.3":
   version "7.8.3"
   dependencies:
     "@babel/helper-plugin-utils" "^7.18.9"
 
-"@babel/plugin-transform-classes@^7.18.9":
-  version "7.18.9"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.18.9.tgz#90818efc5b9746879b869d5ce83eb2aa48bbc3da"
-  integrity sha512-EkRQxsxoytpTlKJmSPYrsOMjCILacAjtSVkd4gChEe2kXjFCun3yohhW5I7plXJhCemM0gKsaGMcO8tinvCA5g==
+"@babel/plugin-transform-classes@^7.19.0":
+  version "7.19.0"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.19.0.tgz#0e61ec257fba409c41372175e7c1e606dc79bb20"
+  integrity sha512-YfeEE9kCjqTS9IitkgfJuxjcEtLUHMqa8yUJ6zdz8vR7hKuo6mOy2C05P0F1tdMmDCeuyidKnlrw/iTppHcr2A==
   dependencies:
     "@babel/helper-annotate-as-pure" "^7.18.6"
+    "@babel/helper-compilation-targets" "^7.19.0"
     "@babel/helper-environment-visitor" "^7.18.9"
-    "@babel/helper-function-name" "^7.18.9"
+    "@babel/helper-function-name" "^7.19.0"
     "@babel/helper-optimise-call-expression" "^7.18.6"
-    "@babel/helper-plugin-utils" "^7.18.9"
+    "@babel/helper-plugin-utils" "^7.19.0"
     "@babel/helper-replace-supers" "^7.18.9"
     "@babel/helper-split-export-declaration" "^7.18.6"
     globals "^11.1.0"
   dependencies:
     "@babel/helper-plugin-utils" "^7.18.9"
 
-"@babel/plugin-transform-destructuring@^7.18.9":
-  version "7.18.9"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.18.9.tgz#68906549c021cb231bee1db21d3b5b095f8ee292"
-  integrity sha512-p5VCYNddPLkZTq4XymQIaIfZNJwT9YsjkPOhkVEqt6QIpQFZVM9IltqqYpOEkJoN1DPznmxUDyZ5CTZs/ZCuHA==
+"@babel/plugin-transform-destructuring@^7.18.13":
+  version "7.18.13"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.18.13.tgz#9e03bc4a94475d62b7f4114938e6c5c33372cbf5"
+  integrity sha512-TodpQ29XekIsex2A+YJPj5ax2plkGa8YYY6mFjCohk/IG9IY42Rtuj1FuDeemfg2ipxIFLzPeA83SIBnlhSIow==
   dependencies:
     "@babel/helper-plugin-utils" "^7.18.9"
 
     "@babel/helper-simple-access" "^7.18.6"
     babel-plugin-dynamic-import-node "^2.3.3"
 
-"@babel/plugin-transform-modules-systemjs@^7.18.9":
-  version "7.18.9"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.18.9.tgz#545df284a7ac6a05125e3e405e536c5853099a06"
-  integrity sha512-zY/VSIbbqtoRoJKo2cDTewL364jSlZGvn0LKOf9ntbfxOvjfmyrdtEEOAdswOswhZEb8UH3jDkCKHd1sPgsS0A==
+"@babel/plugin-transform-modules-systemjs@^7.19.0":
+  version "7.19.0"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.19.0.tgz#5f20b471284430f02d9c5059d9b9a16d4b085a1f"
+  integrity sha512-x9aiR0WXAWmOWsqcsnrzGR+ieaTMVyGyffPVA7F8cXAGt/UxefYv6uSHZLkAFChN5M5Iy1+wjE+xJuPt22H39A==
   dependencies:
     "@babel/helper-hoist-variables" "^7.18.6"
-    "@babel/helper-module-transforms" "^7.18.9"
-    "@babel/helper-plugin-utils" "^7.18.9"
+    "@babel/helper-module-transforms" "^7.19.0"
+    "@babel/helper-plugin-utils" "^7.19.0"
     "@babel/helper-validator-identifier" "^7.18.6"
     babel-plugin-dynamic-import-node "^2.3.3"
 
     "@babel/helper-module-transforms" "^7.18.6"
     "@babel/helper-plugin-utils" "^7.18.6"
 
-"@babel/plugin-transform-named-capturing-groups-regex@^7.18.6":
-  version "7.18.6"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.18.6.tgz#c89bfbc7cc6805d692f3a49bc5fc1b630007246d"
-  integrity sha512-UmEOGF8XgaIqD74bC8g7iV3RYj8lMf0Bw7NJzvnS9qQhM4mg+1WHKotUIdjxgD2RGrgFLZZPCFPFj3P/kVDYhg==
+"@babel/plugin-transform-named-capturing-groups-regex@^7.19.1":
+  version "7.19.1"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.19.1.tgz#ec7455bab6cd8fb05c525a94876f435a48128888"
+  integrity sha512-oWk9l9WItWBQYS4FgXD4Uyy5kq898lvkXpXQxoJEY1RnvPk4R/Dvu2ebXU9q8lP+rlMwUQTFf2Ok6d78ODa0kw==
   dependencies:
-    "@babel/helper-create-regexp-features-plugin" "^7.18.6"
-    "@babel/helper-plugin-utils" "^7.18.6"
+    "@babel/helper-create-regexp-features-plugin" "^7.19.0"
+    "@babel/helper-plugin-utils" "^7.19.0"
 
 "@babel/plugin-transform-new-target@^7.18.6":
   version "7.18.6"
     "@babel/helper-plugin-utils" "^7.18.6"
 
 "@babel/plugin-transform-runtime@^7.18.9":
-  version "7.18.9"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.18.9.tgz#d9e4b1b25719307bfafbf43065ed7fb3a83adb8f"
-  integrity sha512-wS8uJwBt7/b/mzE13ktsJdmS4JP/j7PQSaADtnb4I2wL0zK51MQ0pmF8/Jy0wUIS96fr+fXT6S/ifiPXnvrlSg==
+  version "7.19.1"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.19.1.tgz#a3df2d7312eea624c7889a2dcd37fd1dfd25b2c6"
+  integrity sha512-2nJjTUFIzBMP/f/miLxEK9vxwW/KUXsdvN4sR//TmuDhe6yU2h57WmIOE12Gng3MDP/xpjUV/ToZRdcf8Yj4fA==
   dependencies:
     "@babel/helper-module-imports" "^7.18.6"
-    "@babel/helper-plugin-utils" "^7.18.9"
-    babel-plugin-polyfill-corejs2 "^0.3.1"
-    babel-plugin-polyfill-corejs3 "^0.5.2"
-    babel-plugin-polyfill-regenerator "^0.3.1"
+    "@babel/helper-plugin-utils" "^7.19.0"
+    babel-plugin-polyfill-corejs2 "^0.3.3"
+    babel-plugin-polyfill-corejs3 "^0.6.0"
+    babel-plugin-polyfill-regenerator "^0.4.1"
     semver "^6.3.0"
 
 "@babel/plugin-transform-shorthand-properties@^7.18.6":
   dependencies:
     "@babel/helper-plugin-utils" "^7.18.6"
 
-"@babel/plugin-transform-spread@^7.18.9":
-  version "7.18.9"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.18.9.tgz#6ea7a6297740f381c540ac56caf75b05b74fb664"
-  integrity sha512-39Q814wyoOPtIB/qGopNIL9xDChOE1pNU0ZY5dO0owhiVt/5kFm4li+/bBtwc7QotG0u5EPzqhZdjMtmqBqyQA==
+"@babel/plugin-transform-spread@^7.19.0":
+  version "7.19.0"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.19.0.tgz#dd60b4620c2fec806d60cfaae364ec2188d593b6"
+  integrity sha512-RsuMk7j6n+r752EtzyScnWkQyuJdli6LdO5Klv8Yx0OfPVTcQkIUfS8clx5e9yHXzlnhOZF3CbQ8C2uP5j074w==
   dependencies:
-    "@babel/helper-plugin-utils" "^7.18.9"
+    "@babel/helper-plugin-utils" "^7.19.0"
     "@babel/helper-skip-transparent-expression-wrappers" "^7.18.9"
 
 "@babel/plugin-transform-sticky-regex@^7.18.6":
   dependencies:
     "@babel/helper-plugin-utils" "^7.18.9"
 
-"@babel/plugin-transform-typescript@^7.18.6", "@babel/plugin-transform-typescript@^7.18.8":
+"@babel/plugin-transform-typescript@^7.18.6":
   version "7.18.8"
   resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.18.8.tgz#303feb7a920e650f2213ef37b36bbf327e6fa5a0"
   integrity sha512-p2xM8HI83UObjsZGofMV/EdYjamsDm6MoN3hXPYIT0+gxIoopE+B7rPYKAxfrz9K9PK7JafTTjqYC6qipLExYA==
     "@babel/helper-plugin-utils" "^7.18.6"
     "@babel/plugin-syntax-typescript" "^7.18.6"
 
-"@babel/plugin-transform-unicode-escapes@^7.18.6":
-  version "7.18.6"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.18.6.tgz#0d01fb7fb2243ae1c033f65f6e3b4be78db75f27"
-  integrity sha512-XNRwQUXYMP7VLuy54cr/KS/WeL3AZeORhrmeZ7iewgu+X2eBqmpaLI/hzqr9ZxCeUoq0ASK4GUzSM0BDhZkLFw==
+"@babel/plugin-transform-typescript@^7.18.8":
+  version "7.19.1"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.19.1.tgz#adcf180a041dcbd29257ad31b0c65d4de531ce8d"
+  integrity sha512-+ILcOU+6mWLlvCwnL920m2Ow3wWx3Wo8n2t5aROQmV55GZt+hOiLvBaa3DNzRjSEHa1aauRs4/YLmkCfFkhhRQ==
   dependencies:
-    "@babel/helper-plugin-utils" "^7.18.6"
+    "@babel/helper-create-class-features-plugin" "^7.19.0"
+    "@babel/helper-plugin-utils" "^7.19.0"
+    "@babel/plugin-syntax-typescript" "^7.18.6"
+
+"@babel/plugin-transform-unicode-escapes@^7.18.10":
+  version "7.18.10"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.18.10.tgz#1ecfb0eda83d09bbcb77c09970c2dd55832aa246"
+  integrity sha512-kKAdAI+YzPgGY/ftStBFXTI1LZFju38rYThnfMykS+IXy8BVx+res7s2fxf1l8I35DV2T97ezo6+SGrXz6B3iQ==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.18.9"
 
 "@babel/plugin-transform-unicode-regex@^7.18.6":
   version "7.18.6"
     "@babel/helper-create-regexp-features-plugin" "^7.18.6"
     "@babel/helper-plugin-utils" "^7.18.6"
 
-"@babel/preset-env@7.18.9":
-  version "7.18.9"
-  resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.18.9.tgz#9b3425140d724fbe590322017466580844c7eaff"
-  integrity sha512-75pt/q95cMIHWssYtyfjVlvI+QEZQThQbKvR9xH+F/Agtw/s4Wfc2V9Bwd/P39VtixB7oWxGdH4GteTTwYJWMg==
+"@babel/preset-env@7.19.1":
+  version "7.19.1"
+  resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.19.1.tgz#9f04c916f9c0205a48ebe5cc1be7768eb1983f67"
+  integrity sha512-c8B2c6D16Lp+Nt6HcD+nHl0VbPKVnNPTpszahuxJJnurfMtKeZ80A+qUv48Y7wqvS+dTFuLuaM9oYxyNHbCLWA==
   dependencies:
-    "@babel/compat-data" "^7.18.8"
-    "@babel/helper-compilation-targets" "^7.18.9"
-    "@babel/helper-plugin-utils" "^7.18.9"
+    "@babel/compat-data" "^7.19.1"
+    "@babel/helper-compilation-targets" "^7.19.1"
+    "@babel/helper-plugin-utils" "^7.19.0"
     "@babel/helper-validator-option" "^7.18.6"
     "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression" "^7.18.6"
     "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining" "^7.18.9"
-    "@babel/plugin-proposal-async-generator-functions" "^7.18.6"
+    "@babel/plugin-proposal-async-generator-functions" "^7.19.1"
     "@babel/plugin-proposal-class-properties" "^7.18.6"
     "@babel/plugin-proposal-class-static-block" "^7.18.6"
     "@babel/plugin-proposal-dynamic-import" "^7.18.6"
     "@babel/plugin-transform-async-to-generator" "^7.18.6"
     "@babel/plugin-transform-block-scoped-functions" "^7.18.6"
     "@babel/plugin-transform-block-scoping" "^7.18.9"
-    "@babel/plugin-transform-classes" "^7.18.9"
+    "@babel/plugin-transform-classes" "^7.19.0"
     "@babel/plugin-transform-computed-properties" "^7.18.9"
-    "@babel/plugin-transform-destructuring" "^7.18.9"
+    "@babel/plugin-transform-destructuring" "^7.18.13"
     "@babel/plugin-transform-dotall-regex" "^7.18.6"
     "@babel/plugin-transform-duplicate-keys" "^7.18.9"
     "@babel/plugin-transform-exponentiation-operator" "^7.18.6"
     "@babel/plugin-transform-member-expression-literals" "^7.18.6"
     "@babel/plugin-transform-modules-amd" "^7.18.6"
     "@babel/plugin-transform-modules-commonjs" "^7.18.6"
-    "@babel/plugin-transform-modules-systemjs" "^7.18.9"
+    "@babel/plugin-transform-modules-systemjs" "^7.19.0"
     "@babel/plugin-transform-modules-umd" "^7.18.6"
-    "@babel/plugin-transform-named-capturing-groups-regex" "^7.18.6"
+    "@babel/plugin-transform-named-capturing-groups-regex" "^7.19.1"
     "@babel/plugin-transform-new-target" "^7.18.6"
     "@babel/plugin-transform-object-super" "^7.18.6"
     "@babel/plugin-transform-parameters" "^7.18.8"
     "@babel/plugin-transform-regenerator" "^7.18.6"
     "@babel/plugin-transform-reserved-words" "^7.18.6"
     "@babel/plugin-transform-shorthand-properties" "^7.18.6"
-    "@babel/plugin-transform-spread" "^7.18.9"
+    "@babel/plugin-transform-spread" "^7.19.0"
     "@babel/plugin-transform-sticky-regex" "^7.18.6"
     "@babel/plugin-transform-template-literals" "^7.18.9"
     "@babel/plugin-transform-typeof-symbol" "^7.18.9"
-    "@babel/plugin-transform-unicode-escapes" "^7.18.6"
+    "@babel/plugin-transform-unicode-escapes" "^7.18.10"
     "@babel/plugin-transform-unicode-regex" "^7.18.6"
     "@babel/preset-modules" "^0.1.5"
-    "@babel/types" "^7.18.9"
-    babel-plugin-polyfill-corejs2 "^0.3.1"
-    babel-plugin-polyfill-corejs3 "^0.5.2"
-    babel-plugin-polyfill-regenerator "^0.3.1"
-    core-js-compat "^3.22.1"
+    "@babel/types" "^7.19.0"
+    babel-plugin-polyfill-corejs2 "^0.3.3"
+    babel-plugin-polyfill-corejs3 "^0.6.0"
+    babel-plugin-polyfill-regenerator "^0.4.1"
+    core-js-compat "^3.25.1"
     semver "^6.3.0"
 
 "@babel/preset-modules@^0.1.5":
     "@babel/helper-validator-option" "^7.18.6"
     "@babel/plugin-transform-typescript" "^7.18.6"
 
-"@babel/runtime@^7.1.2", "@babel/runtime@^7.8.4", "@babel/runtime@^7.9.2":
-  version "7.16.7"
-  resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.16.7.tgz#03ff99f64106588c9c403c6ecb8c3bafbbdff1fa"
-  integrity sha512-9E9FJowqAsytyOY6LG+1KuueckRL+aQW+mKvXRXnuFGyRAyepJPmEo9vgMfXUA6O9u3IeEdv9MAkppFcaQwogQ==
-  dependencies:
-    regenerator-runtime "^0.13.4"
-
-"@babel/runtime@^7.17.2":
-  version "7.17.8"
-  resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.17.8.tgz#3e56e4aff81befa55ac3ac6a0967349fd1c5bca2"
-  integrity sha512-dQpEpK0O9o6lj6oPu0gRDbbnk+4LeHlNcBpspf6Olzt3GIX4P1lWF1gS+pHLDFlaJvbR6q7jCfQ08zA4QJBnmA==
+"@babel/runtime@^7.17.2", "@babel/runtime@^7.18.9", "@babel/runtime@^7.7.6":
+  version "7.19.0"
+  resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.19.0.tgz#22b11c037b094d27a8a2504ea4dcff00f50e2259"
+  integrity sha512-eR8Lo9hnDS7tqkO7NsV+mKvCmv5boaXFSZ70DnfhcgiEne8hv9oCEd36Klw74EtizEqLsy4YnW8UWwpBVolHZA==
   dependencies:
     regenerator-runtime "^0.13.4"
 
-"@babel/runtime@^7.18.9":
-  version "7.18.9"
-  resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.18.9.tgz#b4fcfce55db3d2e5e080d2490f608a3b9f407f4a"
-  integrity sha512-lkqXDcvlFT5rvEjiu6+QYO+1GXrEHRo2LOtS7E4GtX5ESIZOgepqsZBVIj6Pv+a6zqsya9VCgiK1KAK4BvJDAw==
+"@babel/runtime@^7.8.4", "@babel/runtime@^7.9.2":
+  version "7.16.7"
+  resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.16.7.tgz#03ff99f64106588c9c403c6ecb8c3bafbbdff1fa"
+  integrity sha512-9E9FJowqAsytyOY6LG+1KuueckRL+aQW+mKvXRXnuFGyRAyepJPmEo9vgMfXUA6O9u3IeEdv9MAkppFcaQwogQ==
   dependencies:
     regenerator-runtime "^0.13.4"
 
     "@babel/parser" "^7.16.7"
     "@babel/types" "^7.16.7"
 
-"@babel/template@^7.18.6":
-  version "7.18.6"
-  resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.18.6.tgz#1283f4993e00b929d6e2d3c72fdc9168a2977a31"
-  integrity sha512-JoDWzPe+wgBsTTgdnIma3iHNFC7YVJoPssVBDjiHfNlyt4YcunDtcDOUmfVDfCK5MfdsaIoX9PkijPhjH3nYUw==
+"@babel/template@^7.18.10", "@babel/template@^7.18.6":
+  version "7.18.10"
+  resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.18.10.tgz#6f9134835970d1dbf0835c0d100c9f38de0c5e71"
+  integrity sha512-TI+rCtooWHr3QJ27kJxfjutghu44DLnasDMwpDqCXVTal9RLp3RSYNh4NdBrRP2cQAoG9A8juOQl6P6oZG4JxA==
   dependencies:
     "@babel/code-frame" "^7.18.6"
-    "@babel/parser" "^7.18.6"
-    "@babel/types" "^7.18.6"
+    "@babel/parser" "^7.18.10"
+    "@babel/types" "^7.18.10"
 
 "@babel/traverse@^7.0.0-beta.54", "@babel/traverse@^7.16.7":
   version "7.16.8"
     debug "^4.1.0"
     globals "^11.1.0"
 
-"@babel/traverse@^7.18.9":
-  version "7.18.9"
-  resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.18.9.tgz#deeff3e8f1bad9786874cb2feda7a2d77a904f98"
-  integrity sha512-LcPAnujXGwBgv3/WHv01pHtb2tihcyW1XuL9wd7jqh1Z8AQkTd+QVjMrMijrln0T7ED3UXLIy36P9Ao7W75rYg==
+"@babel/traverse@^7.18.9", "@babel/traverse@^7.19.0", "@babel/traverse@^7.19.1":
+  version "7.19.1"
+  resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.19.1.tgz#0fafe100a8c2a603b4718b1d9bf2568d1d193347"
+  integrity sha512-0j/ZfZMxKukDaag2PtOPDbwuELqIar6lLskVPPJDjXMXjfLb1Obo/1yjxIGqqAJrmfaTIY3z2wFLAQ7qSkLsuA==
   dependencies:
     "@babel/code-frame" "^7.18.6"
-    "@babel/generator" "^7.18.9"
+    "@babel/generator" "^7.19.0"
     "@babel/helper-environment-visitor" "^7.18.9"
-    "@babel/helper-function-name" "^7.18.9"
+    "@babel/helper-function-name" "^7.19.0"
     "@babel/helper-hoist-variables" "^7.18.6"
     "@babel/helper-split-export-declaration" "^7.18.6"
-    "@babel/parser" "^7.18.9"
-    "@babel/types" "^7.18.9"
+    "@babel/parser" "^7.19.1"
+    "@babel/types" "^7.19.0"
     debug "^4.1.0"
     globals "^11.1.0"
 
     "@babel/helper-validator-identifier" "^7.16.7"
     to-fast-properties "^2.0.0"
 
-"@babel/types@^7.18.6", "@babel/types@^7.18.9":
-  version "7.18.9"
-  resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.18.9.tgz#7148d64ba133d8d73a41b3172ac4b83a1452205f"
-  integrity sha512-WwMLAg2MvJmt/rKEVQBBhIVffMmnilX4oe0sRe7iPOHIGsqpruFHHdrfj4O1CMMtgMtCU4oPafZjDPCRgO57Wg==
+"@babel/types@^7.18.10", "@babel/types@^7.18.6", "@babel/types@^7.18.9", "@babel/types@^7.19.0":
+  version "7.19.0"
+  resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.19.0.tgz#75f21d73d73dc0351f3368d28db73465f4814600"
+  integrity sha512-YuGopBq3ke25BVSiS6fgF49Ul9gH1x70Bcr6bqRLjWCkcX8Hre1/5+z+IiWOIerRMSSEfGZVB9z9kyq7wVs9YA==
   dependencies:
+    "@babel/helper-string-parser" "^7.18.10"
     "@babel/helper-validator-identifier" "^7.18.6"
     to-fast-properties "^2.0.0"
 
   resolved "https://registry.yarnpkg.com/@discoveryjs/json-ext/-/json-ext-0.5.6.tgz#d5e0706cf8c6acd8c6032f8d54070af261bbbb2f"
   integrity sha512-ws57AidsDvREKrZKYffXddNkyaF14iHNHm8VQnZH6t99E8gczjNN0GpvcGny0imC80yQ0tHz1xVUKk/KFQSUyA==
 
-"@eslint/eslintrc@^1.3.0":
-  version "1.3.0"
-  resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-1.3.0.tgz#29f92c30bb3e771e4a2048c95fa6855392dfac4f"
-  integrity sha512-UWW0TMTmk2d7hLcWD1/e2g5HDM/HQ3csaLSqXCfqwh4uNDuNqlaKWXmEsL4Cs41Z0KnILNvwbHAah3C2yt06kw==
+"@eslint/eslintrc@^1.3.2":
+  version "1.3.2"
+  resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-1.3.2.tgz#58b69582f3b7271d8fa67fe5251767a5b38ea356"
+  integrity sha512-AXYd23w1S/bv3fTs3Lz0vjiYemS08jWkI3hYyS9I1ry+0f+Yjs1wm+sU0BS8qDOPrBIkp4qHYC16I8uVtpLajQ==
   dependencies:
     ajv "^6.12.4"
     debug "^4.3.2"
-    espree "^9.3.2"
+    espree "^9.4.0"
     globals "^13.15.0"
     ignore "^5.2.0"
     import-fresh "^3.2.1"
     minimatch "^3.1.2"
     strip-json-comments "^3.1.1"
 
-"@humanwhocodes/config-array@^0.9.2":
-  version "0.9.2"
-  resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.9.2.tgz#68be55c737023009dfc5fe245d51181bb6476914"
-  integrity sha512-UXOuFCGcwciWckOpmfKDq/GyhlTf9pN/BzG//x8p8zTOFEcGuA68ANXheFS0AGvy3qgZqLBUkMs7hqzqCKOVwA==
+"@humanwhocodes/config-array@^0.10.4":
+  version "0.10.4"
+  resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.10.4.tgz#01e7366e57d2ad104feea63e72248f22015c520c"
+  integrity sha512-mXAIHxZT3Vcpg83opl1wGlVZ9xydbfZO3r5YfRSH6Gpp2J/PfdBP0wbDa2sO6/qRbcalpoevVyW6A/fI6LfeMw==
   dependencies:
     "@humanwhocodes/object-schema" "^1.2.1"
     debug "^4.1.1"
     minimatch "^3.0.4"
 
+"@humanwhocodes/gitignore-to-minimatch@^1.0.2":
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/@humanwhocodes/gitignore-to-minimatch/-/gitignore-to-minimatch-1.0.2.tgz#316b0a63b91c10e53f242efb4ace5c3b34e8728d"
+  integrity sha512-rSqmMJDdLFUsyxR6FMtD00nfQKKLFb1kv+qBbOVKqErvloEIJLo5bDTJTQNTYgeyp78JsA7u/NPi5jT1GR/MuA==
+
+"@humanwhocodes/module-importer@^1.0.1":
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz#af5b2691a22b44be847b0ca81641c5fb6ad0172c"
+  integrity sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==
+
 "@humanwhocodes/object-schema@^1.2.1":
   version "1.2.1"
   resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz#b520529ec21d8e5945a1851dfd1c32e94e39ff45"
     update-notifier "^2.2.0"
     yargs "^8.0.2"
 
-"@jridgewell/gen-mapping@^0.3.0":
-  version "0.3.1"
-  resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.1.tgz#cf92a983c83466b8c0ce9124fadeaf09f7c66ea9"
-  integrity sha512-GcHwniMlA2z+WFPWuY8lp3fsza0I8xPFMWL5+n8LYyP6PSvPrXf4+n8stDHZY2DM0zy9sVkRDy1jDI4XGzYVqg==
+"@jridgewell/gen-mapping@^0.1.0":
+  version "0.1.1"
+  resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz#e5d2e450306a9491e3bd77e323e38d7aff315996"
+  integrity sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==
   dependencies:
     "@jridgewell/set-array" "^1.0.0"
     "@jridgewell/sourcemap-codec" "^1.4.10"
-    "@jridgewell/trace-mapping" "^0.3.9"
 
-"@jridgewell/gen-mapping@^0.3.2":
+"@jridgewell/gen-mapping@^0.3.0", "@jridgewell/gen-mapping@^0.3.2":
   version "0.3.2"
   resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz#c1aedc61e853f2bb9f5dfe6d4442d3b565b253b9"
   integrity sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==
     "@jridgewell/trace-mapping" "^0.3.9"
 
 "@jridgewell/resolve-uri@^3.0.3":
-  version "3.0.5"
-  resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.0.5.tgz#68eb521368db76d040a6315cdb24bf2483037b9c"
-  integrity sha512-VPeQ7+wH0itvQxnG+lIzWgkysKIr3L9sslimFW55rHMdGu/qCQ5z5h9zq4gI8uBtqkpHhsF4Z/OwExufUCThew==
-
-"@jridgewell/set-array@^1.0.0":
-  version "1.1.1"
-  resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.1.1.tgz#36a6acc93987adcf0ba50c66908bd0b70de8afea"
-  integrity sha512-Ct5MqZkLGEXTVmQYbGtx9SVqD2fqwvdubdps5D3djjAkgkKwT918VNOz65pEHFaYTeWcukmJmH5SwsA9Tn2ObQ==
+  version "3.1.0"
+  resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz#2203b118c157721addfe69d47b70465463066d78"
+  integrity sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==
 
-"@jridgewell/set-array@^1.0.1":
+"@jridgewell/set-array@^1.0.0", "@jridgewell/set-array@^1.0.1":
   version "1.1.2"
   resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.1.2.tgz#7c6cf998d6d20b914c0a55a91ae928ff25965e72"
   integrity sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==
     "@jridgewell/trace-mapping" "^0.3.9"
 
 "@jridgewell/sourcemap-codec@^1.4.10":
-  version "1.4.11"
-  resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.11.tgz#771a1d8d744eeb71b6adb35808e1a6c7b9b8c8ec"
-  integrity sha512-Fg32GrJo61m+VqYSdRSjRXMjQ06j8YIYfcTqndLYVAaHmroZHLJZCydsWBOTDqXS2v+mjxohBWEMfg97GXmYQg==
-
-"@jridgewell/trace-mapping@^0.3.0":
-  version "0.3.4"
-  resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.4.tgz#f6a0832dffd5b8a6aaa633b7d9f8e8e94c83a0c3"
-  integrity sha512-vFv9ttIedivx0ux3QSjhgtCVjPZd5l46ZOMDSCwnH1yUO2e964gO8LZGyv2QkqcgR6TnBU1v+1IFqmeoG+0UJQ==
-  dependencies:
-    "@jridgewell/resolve-uri" "^3.0.3"
-    "@jridgewell/sourcemap-codec" "^1.4.10"
+  version "1.4.14"
+  resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz#add4c98d341472a289190b424efbdb096991bb24"
+  integrity sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==
 
 "@jridgewell/trace-mapping@^0.3.9":
-  version "0.3.13"
-  resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.13.tgz#dcfe3e95f224c8fe97a87a5235defec999aa92ea"
-  integrity sha512-o1xbKhp9qnIAoHJSWd6KlCZfqslL4valSF81H8ImioOAxluWYWOpWkpyktY2vnt4tbrX9XYaxovq6cgowaJp2w==
+  version "0.3.15"
+  resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.15.tgz#aba35c48a38d3fd84b37e66c9c0423f9744f9774"
+  integrity sha512-oWZNOULl+UbhsgB51uuZzglikfIKSUBO/M9W2OfEjn7cmqoAiCgmv9lyACTUacZwBz0ITnJ2NqjU8Tx0DHL88g==
   dependencies:
     "@jridgewell/resolve-uri" "^3.0.3"
     "@jridgewell/sourcemap-codec" "^1.4.10"
   resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.51.tgz#cfd70924a25a3fd32b218e5e420e6897e1ac4f40"
   integrity sha512-CuPgU6f3eT/XgKKPqKd/gLZV1Xmvf1a2R5POBOGQa6uv82xpls89HU5zKeVoyR8XzHd1RGNOlQlvUe3CFkjWNQ==
 
-"@types/express-serve-static-core@*", "@types/express-serve-static-core@^4.17.18":
+"@types/express-serve-static-core@*":
   version "4.17.27"
   resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.17.27.tgz#7a776191e47295d2a05962ecbb3a4ce97e38b401"
   integrity sha512-e/sVallzUTPdyOTiqi8O8pMdBBphscvI6E4JYaKlja4Lm+zh7UFSSdW5VMkRbhDtmrONqOUHOXRguPsDckzxNA==
     "@types/qs" "*"
     "@types/range-parser" "*"
 
-"@types/express@*", "@types/express@^4.17.13":
+"@types/express-serve-static-core@^4.17.18":
+  version "4.17.31"
+  resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.17.31.tgz#a1139efeab4e7323834bb0226e62ac019f474b2f"
+  integrity sha512-DxMhY+NAsTwMMFHBTtJFNp5qiHKJ7TeqOo23zVEM9alT1Ml27Q3xcTH0xwxn7Q0BbMcVEJOs/7aQtUWupUQN3Q==
+  dependencies:
+    "@types/node" "*"
+    "@types/qs" "*"
+    "@types/range-parser" "*"
+
+"@types/express@*":
   version "4.17.13"
   resolved "https://registry.yarnpkg.com/@types/express/-/express-4.17.13.tgz#a76e2995728999bab51a33fabce1d705a3709034"
   integrity sha512-6bSZTPaTIACxn48l50SR+axgrqm6qXFIxrdAKaG6PaJk3+zuUr35hBlgT7vOmJcum+OEaIBLtHV/qloEAFITeA==
     "@types/qs" "*"
     "@types/serve-static" "*"
 
+"@types/express@^4.17.13":
+  version "4.17.14"
+  resolved "https://registry.yarnpkg.com/@types/express/-/express-4.17.14.tgz#143ea0557249bc1b3b54f15db4c81c3d4eb3569c"
+  integrity sha512-TEbt+vaPFQ+xpxFLFssxUDXj5cWCxZJjIcB7Yg0k0GMHGtgtQgpvx/MUQUeAkNbA9AAGrwkAsoeItdTgS7FMyg==
+  dependencies:
+    "@types/body-parser" "*"
+    "@types/express-serve-static-core" "^4.17.18"
+    "@types/qs" "*"
+    "@types/serve-static" "*"
+
 "@types/glob@^7.1.1":
   version "7.2.0"
   resolved "https://registry.yarnpkg.com/@types/glob/-/glob-7.2.0.tgz#bc1b5bf3aa92f25bd5dd39f35c57361bdce5b2eb"
   dependencies:
     "@types/node" "*"
 
-"@types/json-schema@*", "@types/json-schema@^7.0.5", "@types/json-schema@^7.0.8", "@types/json-schema@^7.0.9":
+"@types/json-schema@*", "@types/json-schema@^7.0.5", "@types/json-schema@^7.0.8":
   version "7.0.9"
   resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.9.tgz#97edc9037ea0c38585320b28964dde3b39e4660d"
   integrity sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ==
 
+"@types/json-schema@^7.0.9":
+  version "7.0.11"
+  resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.11.tgz#d421b6c527a3037f7c84433fd2c4229e016863d3"
+  integrity sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==
+
+"@types/mime@*":
+  version "3.0.1"
+  resolved "https://registry.yarnpkg.com/@types/mime/-/mime-3.0.1.tgz#5f8f2bca0a5863cb69bc0b0acd88c96cb1d4ae10"
+  integrity sha512-Y4XFY5VJAuw0FgAqPNd6NNoV44jbq9Bz2L7Rh/J6jLTiHBSBJa9fxqQIvkIld4GsoDOcCbvzOUAbLPsSKKg+uA==
+
 "@types/mime@^1":
   version "1.3.2"
   resolved "https://registry.yarnpkg.com/@types/mime/-/mime-1.3.2.tgz#93e25bf9ee75fe0fd80b594bc4feb0e862111b5a"
     "@types/node" "*"
     form-data "^3.0.0"
 
-"@types/node@*":
-  version "17.0.8"
-  resolved "https://registry.yarnpkg.com/@types/node/-/node-17.0.8.tgz#50d680c8a8a78fe30abe6906453b21ad8ab0ad7b"
-  integrity sha512-YofkM6fGv4gDJq78g4j0mMuGMkZVxZDgtU0JRdx6FgiJDG+0fY0GKVolOV8WqVmEhLCXkQRjwDdKyPxJp/uucg==
-
-"@types/node@^18.6.2":
-  version "18.6.2"
-  resolved "https://registry.yarnpkg.com/@types/node/-/node-18.6.2.tgz#ffc5f0f099d27887c8d9067b54e55090fcd54126"
-  integrity sha512-KcfkBq9H4PI6Vpu5B/KoPeuVDAbmi+2mDBqGPGUgoL7yXQtcWGu2vJWmmRkneWK3Rh0nIAX192Aa87AqKHYChQ==
+"@types/node@*", "@types/node@^18.6.2":
+  version "18.7.18"
+  resolved "https://registry.yarnpkg.com/@types/node/-/node-18.7.18.tgz#633184f55c322e4fb08612307c274ee6d5ed3154"
+  integrity sha512-m+6nTEOadJZuTPkKR/SYK3A2d7FZrgElol9UP1Kae90VVU4a6mxnPuLiIW1m4Cq4gZ/nWb9GrdVXJCoCazDAbg==
 
 "@types/qs@*":
   version "6.9.7"
   dependencies:
     "@types/express" "*"
 
-"@types/serve-static@*", "@types/serve-static@^1.13.10":
+"@types/serve-static@*":
+  version "1.15.0"
+  resolved "https://registry.yarnpkg.com/@types/serve-static/-/serve-static-1.15.0.tgz#c7930ff61afb334e121a9da780aac0d9b8f34155"
+  integrity sha512-z5xyF6uh8CbjAu9760KDKsH2FcDxZ2tFCsA4HIMWE6IkiYMXfVoa+4f9KX+FN0ZLsaMw1WNG2ETLA6N+/YA+cg==
+  dependencies:
+    "@types/mime" "*"
+    "@types/node" "*"
+
+"@types/serve-static@^1.13.10":
   version "1.13.10"
   resolved "https://registry.yarnpkg.com/@types/serve-static/-/serve-static-1.13.10.tgz#f5e0ce8797d2d7cc5ebeda48a52c96c4fa47a8d9"
   integrity sha512-nCkHGI4w7ZgAdNkrEu0bv+4xNV/XDqW+DydknebMOQwkpDGx8G+HTlj7R7ABI8i8nKxVw0wtKPi1D+lPOkh4YQ==
     "@types/node" "*"
 
 "@typescript-eslint/eslint-plugin@^5.31.0":
-  version "5.31.0"
-  resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.31.0.tgz#cae1967b1e569e6171bbc6bec2afa4e0c8efccfe"
-  integrity sha512-VKW4JPHzG5yhYQrQ1AzXgVgX8ZAJEvCz0QI6mLRX4tf7rnFfh5D8SKm0Pq6w5PyNfAWJk6sv313+nEt3ohWMBQ==
+  version "5.38.0"
+  resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.38.0.tgz#ac919a199548861012e8c1fb2ec4899ac2bc22ae"
+  integrity sha512-GgHi/GNuUbTOeoJiEANi0oI6fF3gBQc3bGFYj40nnAPCbhrtEDf2rjBmefFadweBmO1Du1YovHeDP2h5JLhtTQ==
   dependencies:
-    "@typescript-eslint/scope-manager" "5.31.0"
-    "@typescript-eslint/type-utils" "5.31.0"
-    "@typescript-eslint/utils" "5.31.0"
+    "@typescript-eslint/scope-manager" "5.38.0"
+    "@typescript-eslint/type-utils" "5.38.0"
+    "@typescript-eslint/utils" "5.38.0"
     debug "^4.3.4"
-    functional-red-black-tree "^1.0.1"
     ignore "^5.2.0"
     regexpp "^3.2.0"
     semver "^7.3.7"
     tsutils "^3.21.0"
 
 "@typescript-eslint/parser@^5.31.0":
-  version "5.31.0"
-  resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.31.0.tgz#7f42d7dcc68a0a6d80a0f3d9a65063aee7bb8d2c"
-  integrity sha512-UStjQiZ9OFTFReTrN+iGrC6O/ko9LVDhreEK5S3edmXgR396JGq7CoX2TWIptqt/ESzU2iRKXAHfSF2WJFcWHw==
+  version "5.38.0"
+  resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.38.0.tgz#5a59a1ff41a7b43aacd1bb2db54f6bf1c02b2ff8"
+  integrity sha512-/F63giJGLDr0ms1Cr8utDAxP2SPiglaD6V+pCOcG35P2jCqdfR7uuEhz1GIC3oy4hkUF8xA1XSXmd9hOh/a5EA==
   dependencies:
-    "@typescript-eslint/scope-manager" "5.31.0"
-    "@typescript-eslint/types" "5.31.0"
-    "@typescript-eslint/typescript-estree" "5.31.0"
+    "@typescript-eslint/scope-manager" "5.38.0"
+    "@typescript-eslint/types" "5.38.0"
+    "@typescript-eslint/typescript-estree" "5.38.0"
     debug "^4.3.4"
 
-"@typescript-eslint/scope-manager@5.31.0":
-  version "5.31.0"
-  resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.31.0.tgz#f47a794ba84d9b818ab7f8f44fff55a61016c606"
-  integrity sha512-8jfEzBYDBG88rcXFxajdVavGxb5/XKXyvWgvD8Qix3EEJLCFIdVloJw+r9ww0wbyNLOTYyBsR+4ALNGdlalLLg==
+"@typescript-eslint/scope-manager@5.38.0":
+  version "5.38.0"
+  resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.38.0.tgz#8f0927024b6b24e28671352c93b393a810ab4553"
+  integrity sha512-ByhHIuNyKD9giwkkLqzezZ9y5bALW8VNY6xXcP+VxoH4JBDKjU5WNnsiD4HJdglHECdV+lyaxhvQjTUbRboiTA==
   dependencies:
-    "@typescript-eslint/types" "5.31.0"
-    "@typescript-eslint/visitor-keys" "5.31.0"
+    "@typescript-eslint/types" "5.38.0"
+    "@typescript-eslint/visitor-keys" "5.38.0"
 
-"@typescript-eslint/type-utils@5.31.0":
-  version "5.31.0"
-  resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-5.31.0.tgz#70a0b7201360b5adbddb0c36080495aa08f6f3d9"
-  integrity sha512-7ZYqFbvEvYXFn9ax02GsPcEOmuWNg+14HIf4q+oUuLnMbpJ6eHAivCg7tZMVwzrIuzX3QCeAOqKoyMZCv5xe+w==
+"@typescript-eslint/type-utils@5.38.0":
+  version "5.38.0"
+  resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-5.38.0.tgz#c8b7f681da825fcfc66ff2b63d70693880496876"
+  integrity sha512-iZq5USgybUcj/lfnbuelJ0j3K9dbs1I3RICAJY9NZZpDgBYXmuUlYQGzftpQA9wC8cKgtS6DASTvF3HrXwwozA==
   dependencies:
-    "@typescript-eslint/utils" "5.31.0"
+    "@typescript-eslint/typescript-estree" "5.38.0"
+    "@typescript-eslint/utils" "5.38.0"
     debug "^4.3.4"
     tsutils "^3.21.0"
 
-"@typescript-eslint/types@5.31.0":
-  version "5.31.0"
-  resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.31.0.tgz#7aa389122b64b18e473c1672fb3b8310e5f07a9a"
-  integrity sha512-/f/rMaEseux+I4wmR6mfpM2wvtNZb1p9hAV77hWfuKc3pmaANp5dLAZSiE3/8oXTYTt3uV9KW5yZKJsMievp6g==
+"@typescript-eslint/types@5.38.0":
+  version "5.38.0"
+  resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.38.0.tgz#8cd15825e4874354e31800dcac321d07548b8a5f"
+  integrity sha512-HHu4yMjJ7i3Cb+8NUuRCdOGu2VMkfmKyIJsOr9PfkBVYLYrtMCK/Ap50Rpov+iKpxDTfnqvDbuPLgBE5FwUNfA==
 
-"@typescript-eslint/typescript-estree@5.31.0":
-  version "5.31.0"
-  resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.31.0.tgz#eb92970c9d6e3946690d50c346fb9b1d745ee882"
-  integrity sha512-3S625TMcARX71wBc2qubHaoUwMEn+l9TCsaIzYI/ET31Xm2c9YQ+zhGgpydjorwQO9pLfR/6peTzS/0G3J/hDw==
+"@typescript-eslint/typescript-estree@5.38.0":
+  version "5.38.0"
+  resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.38.0.tgz#89f86b2279815c6fb7f57d68cf9b813f0dc25d98"
+  integrity sha512-6P0RuphkR+UuV7Avv7MU3hFoWaGcrgOdi8eTe1NwhMp2/GjUJoODBTRWzlHpZh6lFOaPmSvgxGlROa0Sg5Zbyg==
   dependencies:
-    "@typescript-eslint/types" "5.31.0"
-    "@typescript-eslint/visitor-keys" "5.31.0"
+    "@typescript-eslint/types" "5.38.0"
+    "@typescript-eslint/visitor-keys" "5.38.0"
     debug "^4.3.4"
     globby "^11.1.0"
     is-glob "^4.0.3"
     semver "^7.3.7"
     tsutils "^3.21.0"
 
-"@typescript-eslint/utils@5.31.0":
-  version "5.31.0"
-  resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.31.0.tgz#e146fa00dca948bfe547d665b2138a2dc1b79acd"
-  integrity sha512-kcVPdQS6VIpVTQ7QnGNKMFtdJdvnStkqS5LeALr4rcwx11G6OWb2HB17NMPnlRHvaZP38hL9iK8DdE9Fne7NYg==
+"@typescript-eslint/utils@5.38.0":
+  version "5.38.0"
+  resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.38.0.tgz#5b31f4896471818153790700eb02ac869a1543f4"
+  integrity sha512-6sdeYaBgk9Fh7N2unEXGz+D+som2QCQGPAf1SxrkEr+Z32gMreQ0rparXTNGRRfYUWk/JzbGdcM8NSSd6oqnTA==
   dependencies:
     "@types/json-schema" "^7.0.9"
-    "@typescript-eslint/scope-manager" "5.31.0"
-    "@typescript-eslint/types" "5.31.0"
-    "@typescript-eslint/typescript-estree" "5.31.0"
+    "@typescript-eslint/scope-manager" "5.38.0"
+    "@typescript-eslint/types" "5.38.0"
+    "@typescript-eslint/typescript-estree" "5.38.0"
     eslint-scope "^5.1.1"
     eslint-utils "^3.0.0"
 
-"@typescript-eslint/visitor-keys@5.31.0":
-  version "5.31.0"
-  resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.31.0.tgz#b0eca264df01ce85dceb76aebff3784629258f54"
-  integrity sha512-ZK0jVxSjS4gnPirpVjXHz7mgdOsZUHzNYSfTw2yPa3agfbt9YfqaBiBZFSSxeBWnpWkzCxTfUpnzA3Vily/CSg==
+"@typescript-eslint/visitor-keys@5.38.0":
+  version "5.38.0"
+  resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.38.0.tgz#60591ca3bf78aa12b25002c0993d067c00887e34"
+  integrity sha512-MxnrdIyArnTi+XyFLR+kt/uNAcdOnmT+879os7qDRI+EYySR4crXJq9BXPfRzzLGq0wgxkwidrCJ9WCAoacm1w==
   dependencies:
-    "@typescript-eslint/types" "5.31.0"
+    "@typescript-eslint/types" "5.38.0"
     eslint-visitor-keys "^3.3.0"
 
 "@webassemblyjs/ast@1.11.1":
@@ -1790,12 +1841,7 @@ acorn-jsx@^5.3.2:
   resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937"
   integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==
 
-acorn@^8.5.0:
-  version "8.7.0"
-  resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.7.0.tgz#90951fde0f8f09df93549481e5fc141445b791cf"
-  integrity sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ==
-
-acorn@^8.7.1:
+acorn@^8.5.0, acorn@^8.7.1, acorn@^8.8.0:
   version "8.8.0"
   resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.8.0.tgz#88c0187620435c7f6015803f5539dae05a9dbea8"
   integrity sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w==
@@ -1994,6 +2040,17 @@ array-flatten@^2.1.2:
   resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-2.1.2.tgz#24ef80a28c1a893617e2149b0c6d0d788293b099"
   integrity sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ==
 
+array-includes@^3.1.5:
+  version "3.1.5"
+  resolved "https://registry.yarnpkg.com/array-includes/-/array-includes-3.1.5.tgz#2c320010db8d31031fd2a5f6b3bbd4b1aad31bdb"
+  integrity sha512-iSDYZMMyTPkiFasVqfuAQnWAYcvO/SeBSCGKePoEthjp4LEMTe4uLc7b025o4jAZpHhihh8xPo99TNWUWWkGDQ==
+  dependencies:
+    call-bind "^1.0.2"
+    define-properties "^1.1.4"
+    es-abstract "^1.19.5"
+    get-intrinsic "^1.1.1"
+    is-string "^1.0.7"
+
 array-union@^1.0.1:
   version "1.0.2"
   resolved "https://registry.yarnpkg.com/array-union/-/array-union-1.0.2.tgz#9a34410e4f4e3da23dea375be5be70f24778ec39"
@@ -2078,29 +2135,29 @@ babel-plugin-inferno@^6.5.0:
     "@babel/plugin-syntax-jsx" "^7"
     "@babel/types" "^7"
 
-babel-plugin-polyfill-corejs2@^0.3.1:
-  version "0.3.2"
-  resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.3.2.tgz#e4c31d4c89b56f3cf85b92558954c66b54bd972d"
-  integrity sha512-LPnodUl3lS0/4wN3Rb+m+UK8s7lj2jcLRrjho4gLw+OJs+I4bvGXshINesY5xx/apM+biTnQ9reDI8yj+0M5+Q==
+babel-plugin-polyfill-corejs2@^0.3.3:
+  version "0.3.3"
+  resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.3.3.tgz#5d1bd3836d0a19e1b84bbf2d9640ccb6f951c122"
+  integrity sha512-8hOdmFYFSZhqg2C/JgLUQ+t52o5nirNwaWM2B9LWteozwIvM14VSwdsCAUET10qT+kmySAlseadmfeeSWFCy+Q==
   dependencies:
     "@babel/compat-data" "^7.17.7"
-    "@babel/helper-define-polyfill-provider" "^0.3.2"
+    "@babel/helper-define-polyfill-provider" "^0.3.3"
     semver "^6.1.1"
 
-babel-plugin-polyfill-corejs3@^0.5.2:
-  version "0.5.3"
-  resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.5.3.tgz#d7e09c9a899079d71a8b670c6181af56ec19c5c7"
-  integrity sha512-zKsXDh0XjnrUEW0mxIHLfjBfnXSMr5Q/goMe/fxpQnLm07mcOZiIZHBNWCMx60HmdvjxfXcalac0tfFg0wqxyw==
+babel-plugin-polyfill-corejs3@^0.6.0:
+  version "0.6.0"
+  resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.6.0.tgz#56ad88237137eade485a71b52f72dbed57c6230a"
+  integrity sha512-+eHqR6OPcBhJOGgsIar7xoAB1GcSwVUA3XjAd7HJNzOXT4wv6/H7KIdA/Nc60cvUlDbKApmqNvD1B1bzOt4nyA==
   dependencies:
-    "@babel/helper-define-polyfill-provider" "^0.3.2"
-    core-js-compat "^3.21.0"
+    "@babel/helper-define-polyfill-provider" "^0.3.3"
+    core-js-compat "^3.25.1"
 
-babel-plugin-polyfill-regenerator@^0.3.1:
-  version "0.3.1"
-  resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.3.1.tgz#2c0678ea47c75c8cc2fbb1852278d8fb68233990"
-  integrity sha512-Y2B06tvgHYt1x0yz17jGkGeeMr5FeKUu+ASJ+N6nB5lQ8Dapfg42i0OVrf8PNGJ3zKL4A23snMi1IRwrqqND7A==
+babel-plugin-polyfill-regenerator@^0.4.1:
+  version "0.4.1"
+  resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.4.1.tgz#390f91c38d90473592ed43351e801a9d3e0fd747"
+  integrity sha512-NtQGmyQDXjQqQ+IzRkBVwEOz9lQ4zxAQZgoAYEtU9dJjnl1Oc98qnN7jcp+bE7O7aYzVpavXE3/VKXNzUbh7aw==
   dependencies:
-    "@babel/helper-define-polyfill-provider" "^0.3.1"
+    "@babel/helper-define-polyfill-provider" "^0.3.3"
 
 balanced-match@^1.0.0:
   version "1.0.2"
@@ -2187,14 +2244,14 @@ bonjour-service@^1.0.11:
     multicast-dns "^7.2.4"
 
 bootstrap@^5.2.0:
-  version "5.2.0"
-  resolved "https://registry.yarnpkg.com/bootstrap/-/bootstrap-5.2.0.tgz#838727fb60f1630db370fe57c63cbcf2962bb3d3"
-  integrity sha512-qlnS9GL6YZE6Wnef46GxGv1UpGGzAwO0aPL1yOjzDIJpeApeMvqV24iL+pjr2kU4dduoBA9fINKWKgMToobx9A==
+  version "5.2.1"
+  resolved "https://registry.yarnpkg.com/bootstrap/-/bootstrap-5.2.1.tgz#45f97ff05cbe828bad807b014d8425f3aeb8ec3a"
+  integrity sha512-UQi3v2NpVPEi1n35dmRRzBJFlgvWHYwyem6yHhuT6afYF+sziEt46McRbT//kVXZ7b1YUYEVGdXEH74Nx3xzGA==
 
 bootswatch@^5.2.0:
-  version "5.2.0"
-  resolved "https://registry.yarnpkg.com/bootswatch/-/bootswatch-5.2.0.tgz#c02a0d84e0382552f8a7b9bdd055f36b758ffed9"
-  integrity sha512-v9krdPdybb5hUwVwlv3f7/FhOa5cXbCb5U5CI4gdnalcxR3ekclXE6kPZWL5O8V8qwNI9BB73apASO1MLmRpIA==
+  version "5.2.1"
+  resolved "https://registry.yarnpkg.com/bootswatch/-/bootswatch-5.2.1.tgz#80c49ac3ad394e58be630978c95e3bce1d1679ac"
+  integrity sha512-tbuZb0nJ1XUvRO8KYoEULsbAlAqazcFS6S5dTeJOkw2RxQgg0RJqKkngRTfNSFfqfI4mTdFPpC4mKkaw0YoDFA==
 
 boxen@^1.2.1:
   version "1.3.0"
@@ -2217,6 +2274,13 @@ brace-expansion@^1.1.7:
     balanced-match "^1.0.0"
     concat-map "0.0.1"
 
+brace-expansion@^2.0.1:
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.0.1.tgz#1edc459e0f0c548486ecf9fc99f2221364b9a0ae"
+  integrity sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==
+  dependencies:
+    balanced-match "^1.0.0"
+
 braces@^3.0.1, braces@^3.0.2, braces@~3.0.2:
   version "3.0.2"
   resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107"
@@ -2224,7 +2288,7 @@ braces@^3.0.1, braces@^3.0.2, braces@~3.0.2:
   dependencies:
     fill-range "^7.0.1"
 
-browserslist@^4.14.5, browserslist@^4.17.5:
+browserslist@^4.14.5:
   version "4.19.1"
   resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.19.1.tgz#4ac0435b35ab655896c31d53018b6dd5e9e4c9a3"
   integrity sha512-u2tbbG5PdKRTUoctO3NBD8FQ5HdPh1ZXPHzp1rwaa5jTc+RV9/+RlWiAIKmjRPQF+xbGM9Kklj5bZQFa2s/38A==
@@ -2235,15 +2299,15 @@ browserslist@^4.14.5, browserslist@^4.17.5:
     node-releases "^2.0.1"
     picocolors "^1.0.0"
 
-browserslist@^4.20.2, browserslist@^4.21.2:
-  version "4.21.3"
-  resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.21.3.tgz#5df277694eb3c48bc5c4b05af3e8b7e09c5a6d1a"
-  integrity sha512-898rgRXLAyRkM1GryrrBHGkqA5hlpkV5MhtZwg9QXeiyLUYs2k00Un05aX5l2/yJIOObYKOpS2JNo8nJDE7fWQ==
+browserslist@^4.17.5, browserslist@^4.21.3, browserslist@^4.21.4:
+  version "4.21.4"
+  resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.21.4.tgz#e7496bbc67b9e39dd0f98565feccdcb0d4ff6987"
+  integrity sha512-CBHJJdDmgjl3daYjN5Cp5kbTf1mUhZoS+beLklHIvkOWscs83YAhLlF3Wsh/lciQYAcbBJgTOD44VtG31ZM4Hw==
   dependencies:
-    caniuse-lite "^1.0.30001370"
-    electron-to-chromium "^1.4.202"
+    caniuse-lite "^1.0.30001400"
+    electron-to-chromium "^1.4.251"
     node-releases "^2.0.6"
-    update-browserslist-db "^1.0.5"
+    update-browserslist-db "^1.0.9"
 
 buffer-from@^1.0.0:
   version "1.1.2"
@@ -2371,15 +2435,10 @@ camelcase@^5.0.0:
   resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320"
   integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==
 
-caniuse-lite@^1.0.30001286:
-  version "1.0.30001298"
-  resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001298.tgz#0e690039f62e91c3ea581673d716890512e7ec52"
-  integrity sha512-AcKqikjMLlvghZL/vfTHorlQsLDhGRalYf1+GmWCf5SCMziSGjRYQW/JEksj14NaYHIR6KIhrFAy0HV5C25UzQ==
-
-caniuse-lite@^1.0.30001370:
-  version "1.0.30001373"
-  resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001373.tgz#2dc3bc3bfcb5d5a929bec11300883040d7b4b4be"
-  integrity sha512-pJYArGHrPp3TUqQzFYRmP/lwJlj8RCbVe3Gd3eJQkAV8SAC6b19XS9BjMvRdvaS8RMkaTN8ZhoHP6S1y8zzwEQ==
+caniuse-lite@^1.0.30001286, caniuse-lite@^1.0.30001400:
+  version "1.0.30001409"
+  resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001409.tgz#6135da9dcab34cd9761d9cdb12a68e6740c5e96e"
+  integrity sha512-V0mnJ5dwarmhYv8/MzhJ//aW68UpvnQBXv8lJ2QUsvn2pHcmAuNtu8hQEDz37XnA1iE+lRR9CIfGWWpgJ5QedQ==
 
 capture-stack-trace@^1.0.0:
   version "1.0.1"
@@ -2422,22 +2481,7 @@ choices.js@^10.1.0:
     fuse.js "^6.5.3"
     redux "^4.1.2"
 
-"chokidar@>=3.0.0 <4.0.0":
-  version "3.5.2"
-  resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.2.tgz#dba3976fcadb016f66fd365021d91600d01c1e75"
-  integrity sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ==
-  dependencies:
-    anymatch "~3.1.2"
-    braces "~3.0.2"
-    glob-parent "~5.1.2"
-    is-binary-path "~2.1.0"
-    is-glob "~4.0.1"
-    normalize-path "~3.0.0"
-    readdirp "~3.6.0"
-  optionalDependencies:
-    fsevents "~2.3.2"
-
-chokidar@^3.5.3:
+"chokidar@>=3.0.0 <4.0.0", chokidar@^3.5.3:
   version "3.5.3"
   resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd"
   integrity sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==
@@ -2483,9 +2527,9 @@ class-transformer@^0.5.1:
   integrity sha512-SQa1Ws6hUbfC98vKGxZH3KFY0Y1lm5Zm0SY8XX9zbK7FJCyVEac3ATW0RIpwzW+oOfmHE5PMPufDG9hCfoEOMw==
 
 classnames@^2.3.1:
-  version "2.3.1"
-  resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.3.1.tgz#dfcfa3891e306ec1dad105d0e88f4417b8535e8e"
-  integrity sha512-OlQdbZ7gLfGarSqxesMesDa5uz7KFbID8Kpq/SxIoNGDqY8lSYs0D+hhtBXhcdB3rcbXArFr7vlHheLk1voeNA==
+  version "2.3.2"
+  resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.3.2.tgz#351d813bf0137fcc6a76a16b88208d2560a0d924"
+  integrity sha512-CSbhY4cFEJRe6/GQzIk5qXZ4Jeg5pcsP7b5peFSDpffpe1cqjASH/n9UTjBwOp6XpMSTwQ8Za2K5V02ueA7Tmw==
 
 clean-stack@^2.0.0:
   version "2.2.0"
@@ -2625,7 +2669,7 @@ color-convert@^2.0.1:
 color-name@1.1.3:
   version "1.1.3"
   resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25"
-  integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=
+  integrity sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==
 
 color-name@~1.1.4:
   version "1.1.4"
@@ -2705,7 +2749,7 @@ compression@^1.7.4:
 concat-map@0.0.1:
   version "0.0.1"
   resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
-  integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=
+  integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==
 
 concat-stream@^1.5.0, concat-stream@^1.5.2:
   version "1.6.2"
@@ -2805,13 +2849,12 @@ copy-webpack-plugin@^11.0.0:
     schema-utils "^4.0.0"
     serialize-javascript "^6.0.0"
 
-core-js-compat@^3.21.0, core-js-compat@^3.22.1:
-  version "3.24.0"
-  resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.24.0.tgz#885958fac38bf3f4464a90f2663b4620f6aee6e3"
-  integrity sha512-F+2E63X3ff/nj8uIrf8Rf24UDGIz7p838+xjEp+Bx3y8OWXj+VTPPZNCtdqovPaS9o7Tka5mCH01Zn5vOd6UQg==
+core-js-compat@^3.25.1:
+  version "3.25.2"
+  resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.25.2.tgz#7875573586809909c69e03ef310810c1969ee138"
+  integrity sha512-TxfyECD4smdn3/CjWxczVtJqVLEEC2up7/82t7vC0AzNogr+4nQ8vyF7abxAuTXWvjTClSbvGhU0RgqA4ToQaQ==
   dependencies:
-    browserslist "^4.21.2"
-    semver "7.0.0"
+    browserslist "^4.21.4"
 
 core-util-is@1.0.2:
   version "1.0.2"
@@ -2882,6 +2925,11 @@ cssesc@^3.0.0:
   resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-3.0.0.tgz#37741919903b868565e1c09ea747445cd18983ee"
   integrity sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==
 
+csstype@^3.1.0:
+  version "3.1.1"
+  resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.1.tgz#841b532c45c758ee546a11d5bd7b7b473c8c30b9"
+  integrity sha512-DJR/VvkAvSZW9bTouZue2sSxDwdTN92uHjqeKVm+0dAqdfNykRzQ95tay8aXMBAAPpUiq4Qcug2L7neoRh2Egw==
+
 cyclist@^1.0.1:
   version "1.0.1"
   resolved "https://registry.yarnpkg.com/cyclist/-/cyclist-1.0.1.tgz#596e9698fd0c80e12038c2b82d6eb1b35b6224d9"
@@ -2915,14 +2963,7 @@ debug@^3.1.0:
   dependencies:
     ms "^2.1.1"
 
-debug@^4.1.0, debug@^4.1.1, debug@^4.3.2:
-  version "4.3.3"
-  resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.3.tgz#04266e0b70a98d4462e6e288e38259213332b664"
-  integrity sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==
-  dependencies:
-    ms "2.1.2"
-
-debug@^4.3.4:
+debug@^4.1.0, debug@^4.1.1, debug@^4.3.2, debug@^4.3.4:
   version "4.3.4"
   resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865"
   integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==
@@ -2997,6 +3038,14 @@ define-properties@^1.1.3:
   dependencies:
     object-keys "^1.0.12"
 
+define-properties@^1.1.4:
+  version "1.1.4"
+  resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.4.tgz#0b14d7bd7fbeb2f3572c3a7eda80ea5d57fb05b1"
+  integrity sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA==
+  dependencies:
+    has-property-descriptors "^1.0.0"
+    object-keys "^1.1.1"
+
 del@^4.1.1:
   version "4.1.1"
   resolved "https://registry.yarnpkg.com/del/-/del-4.1.1.tgz#9e8f117222ea44a31ff3a156c049b99052a9f0b4"
@@ -3144,15 +3193,10 @@ ee-first@1.1.1:
   resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d"
   integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=
 
-electron-to-chromium@^1.4.17:
-  version "1.4.41"
-  resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.41.tgz#0b2e126796e7fafb9fd71e29304468b9d0af5d65"
-  integrity sha512-VQEXEJc+8rJIva85H8EPtB5Ux9g8TzkNGBanqphM9ZWMZ34elueKJ+5g+BPhz3Lk8gkujfQRcIZ+fpA0btUIuw==
-
-electron-to-chromium@^1.4.202:
-  version "1.4.206"
-  resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.206.tgz#580ff85b54d7ec0c05f20b1e37ea0becdd7b0ee4"
-  integrity sha512-h+Fadt1gIaQ06JaIiyqPsBjJ08fV5Q7md+V8bUvQW/9OvXfL2LRICTz2EcnnCP7QzrFTS6/27MRV6Bl9Yn97zA==
+electron-to-chromium@^1.4.17, electron-to-chromium@^1.4.251:
+  version "1.4.256"
+  resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.256.tgz#c735032f412505e8e0482f147a8ff10cfca45bf4"
+  integrity sha512-x+JnqyluoJv8I0U9gVe+Sk2st8vF0CzMt78SXxuoWCooLLY2k5VerIBdpvG7ql6GKI4dzNnPjmqgDJ76EdaAKw==
 
 emoji-regex@^7.0.1:
   version "7.0.3"
@@ -3240,6 +3284,35 @@ error-ex@^1.2.0, error-ex@^1.3.1:
   dependencies:
     is-arrayish "^0.2.1"
 
+es-abstract@^1.19.0, es-abstract@^1.19.5:
+  version "1.20.2"
+  resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.20.2.tgz#8495a07bc56d342a3b8ea3ab01bd986700c2ccb3"
+  integrity sha512-XxXQuVNrySBNlEkTYJoDNFe5+s2yIOpzq80sUHEdPdQr0S5nTLz4ZPPPswNIpKseDDUS5yghX1gfLIHQZ1iNuQ==
+  dependencies:
+    call-bind "^1.0.2"
+    es-to-primitive "^1.2.1"
+    function-bind "^1.1.1"
+    function.prototype.name "^1.1.5"
+    get-intrinsic "^1.1.2"
+    get-symbol-description "^1.0.0"
+    has "^1.0.3"
+    has-property-descriptors "^1.0.0"
+    has-symbols "^1.0.3"
+    internal-slot "^1.0.3"
+    is-callable "^1.2.4"
+    is-negative-zero "^2.0.2"
+    is-regex "^1.1.4"
+    is-shared-array-buffer "^1.0.2"
+    is-string "^1.0.7"
+    is-weakref "^1.0.2"
+    object-inspect "^1.12.2"
+    object-keys "^1.1.1"
+    object.assign "^4.1.4"
+    regexp.prototype.flags "^1.4.3"
+    string.prototype.trimend "^1.0.5"
+    string.prototype.trimstart "^1.0.5"
+    unbox-primitive "^1.0.2"
+
 es-abstract@^1.19.1:
   version "1.19.1"
   resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.19.1.tgz#d4885796876916959de78edaa0df456627115ec3"
@@ -3305,13 +3378,29 @@ escape-html@~1.0.3:
 escape-string-regexp@^1.0.5:
   version "1.0.5"
   resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4"
-  integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=
+  integrity sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==
 
 escape-string-regexp@^4.0.0:
   version "4.0.0"
   resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34"
   integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==
 
+eslint-plugin-inferno@^7.31.8:
+  version "7.31.8"
+  resolved "https://registry.yarnpkg.com/eslint-plugin-inferno/-/eslint-plugin-inferno-7.31.8.tgz#4f83ed446a14d428a791188cc10ea07080563f77"
+  integrity sha512-3ZfdlZB8qDzKgRo28MuNWXOwwpxPuDBvwl6SwgBmz9uRKrAUTvxWT3/GzVCoYvhFTLmgo6Z7UVKV5wvJucnNbw==
+  dependencies:
+    doctrine "^3.0.0"
+    estraverse "^5.3.0"
+    jsx-ast-utils "^3.3.3"
+    minimatch "^5.1.0"
+    object.entries "^1.1.5"
+    object.fromentries "^2.0.5"
+    object.hasown "^1.1.1"
+    object.values "^1.1.5"
+    prop-types "^15.8.1"
+    resolve "^2.0.0-next.4"
+
 eslint-plugin-prettier@^4.2.1:
   version "4.2.1"
   resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-4.2.1.tgz#651cbb88b1dab98bfd42f017a12fa6b2d993f94b"
@@ -3353,12 +3442,14 @@ eslint-visitor-keys@^3.3.0:
   integrity sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==
 
 eslint@^8.20.0:
-  version "8.20.0"
-  resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.20.0.tgz#048ac56aa18529967da8354a478be4ec0a2bc81b"
-  integrity sha512-d4ixhz5SKCa1D6SCPrivP7yYVi7nyD6A4vs6HIAul9ujBzcEmZVM3/0NN/yu5nKhmO1wjp5xQ46iRfmDGlOviA==
-  dependencies:
-    "@eslint/eslintrc" "^1.3.0"
-    "@humanwhocodes/config-array" "^0.9.2"
+  version "8.23.1"
+  resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.23.1.tgz#cfd7b3f7fdd07db8d16b4ac0516a29c8d8dca5dc"
+  integrity sha512-w7C1IXCc6fNqjpuYd0yPlcTKKmHlHHktRkzmBPZ+7cvNBQuiNjx0xaMTjAJGCafJhQkrFJooREv0CtrVzmHwqg==
+  dependencies:
+    "@eslint/eslintrc" "^1.3.2"
+    "@humanwhocodes/config-array" "^0.10.4"
+    "@humanwhocodes/gitignore-to-minimatch" "^1.0.2"
+    "@humanwhocodes/module-importer" "^1.0.1"
     ajv "^6.10.0"
     chalk "^4.0.0"
     cross-spawn "^7.0.2"
@@ -3368,18 +3459,21 @@ eslint@^8.20.0:
     eslint-scope "^7.1.1"
     eslint-utils "^3.0.0"
     eslint-visitor-keys "^3.3.0"
-    espree "^9.3.2"
+    espree "^9.4.0"
     esquery "^1.4.0"
     esutils "^2.0.2"
     fast-deep-equal "^3.1.3"
     file-entry-cache "^6.0.1"
-    functional-red-black-tree "^1.0.1"
+    find-up "^5.0.0"
     glob-parent "^6.0.1"
     globals "^13.15.0"
+    globby "^11.1.0"
+    grapheme-splitter "^1.0.4"
     ignore "^5.2.0"
     import-fresh "^3.0.0"
     imurmurhash "^0.1.4"
     is-glob "^4.0.0"
+    js-sdsl "^4.1.4"
     js-yaml "^4.1.0"
     json-stable-stringify-without-jsonify "^1.0.1"
     levn "^0.4.1"
@@ -3391,14 +3485,13 @@ eslint@^8.20.0:
     strip-ansi "^6.0.1"
     strip-json-comments "^3.1.0"
     text-table "^0.2.0"
-    v8-compile-cache "^2.0.3"
 
-espree@^9.3.2:
-  version "9.3.2"
-  resolved "https://registry.yarnpkg.com/espree/-/espree-9.3.2.tgz#f58f77bd334731182801ced3380a8cc859091596"
-  integrity sha512-D211tC7ZwouTIuY5x9XnS0E9sWNChB7IYKX/Xp5eQj3nFXhqmiUDB9q27y76oFl8jTg3pXcQx/bpxMfs3CIZbA==
+espree@^9.4.0:
+  version "9.4.0"
+  resolved "https://registry.yarnpkg.com/espree/-/espree-9.4.0.tgz#cd4bc3d6e9336c433265fc0aa016fc1aaf182f8a"
+  integrity sha512-DQmnRpLj7f6TgN/NYb0MTzJXL+vJF9h3pHy4JhCIs3zwcgez8xmGg3sXHcEO97BrmO2OSvCwMdfdlyl+E9KjOw==
   dependencies:
-    acorn "^8.7.1"
+    acorn "^8.8.0"
     acorn-jsx "^5.3.2"
     eslint-visitor-keys "^3.3.0"
 
@@ -3426,7 +3519,7 @@ estraverse@^4.1.1:
   resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d"
   integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==
 
-estraverse@^5.1.0, estraverse@^5.2.0:
+estraverse@^5.1.0, estraverse@^5.2.0, estraverse@^5.3.0:
   version "5.3.0"
   resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123"
   integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==
@@ -3598,7 +3691,7 @@ fast-diff@^1.1.2:
   resolved "https://registry.yarnpkg.com/fast-diff/-/fast-diff-1.2.0.tgz#73ee11982d86caaf7959828d519cfe927fac5f03"
   integrity sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w==
 
-fast-glob@^3.0.3, fast-glob@^3.2.9:
+fast-glob@^3.0.3:
   version "3.2.10"
   resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.10.tgz#2734f83baa7f43b7fd41e13bc34438f4ffe284ee"
   integrity sha512-s9nFhFnvR63wls6/kM88kQqDhMu0AfdjqouE2l5GVQPbqLgyFjjU5ry/r2yKsJxpb9Py1EYNqieFrmMaX4v++A==
@@ -3620,6 +3713,17 @@ fast-glob@^3.2.11:
     merge2 "^1.3.0"
     micromatch "^4.0.4"
 
+fast-glob@^3.2.9:
+  version "3.2.12"
+  resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.12.tgz#7f39ec99c2e6ab030337142da9e0c18f37afae80"
+  integrity sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==
+  dependencies:
+    "@nodelib/fs.stat" "^2.0.2"
+    "@nodelib/fs.walk" "^1.2.3"
+    glob-parent "^5.1.2"
+    merge2 "^1.3.0"
+    micromatch "^4.0.4"
+
 fast-json-stable-stringify@^2.0.0:
   version "2.1.0"
   resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633"
@@ -3628,7 +3732,7 @@ fast-json-stable-stringify@^2.0.0:
 fast-levenshtein@^2.0.6:
   version "2.0.6"
   resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917"
-  integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=
+  integrity sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==
 
 fastest-levenshtein@^1.0.12:
   version "1.0.12"
@@ -3732,6 +3836,14 @@ find-up@^4.0.0:
     locate-path "^5.0.0"
     path-exists "^4.0.0"
 
+find-up@^5.0.0:
+  version "5.0.0"
+  resolved "https://registry.yarnpkg.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc"
+  integrity sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==
+  dependencies:
+    locate-path "^6.0.0"
+    path-exists "^4.0.0"
+
 flat-cache@^3.0.4:
   version "3.0.4"
   resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-3.0.4.tgz#61b0338302b2fe9f957dcc32fc2a87f1c3048b11"
@@ -3741,9 +3853,9 @@ flat-cache@^3.0.4:
     rimraf "^3.0.2"
 
 flatted@^3.1.0:
-  version "3.2.4"
-  resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.4.tgz#28d9969ea90661b5134259f312ab6aa7929ac5e2"
-  integrity sha512-8/sOawo8tJ4QOBX8YlQBMxL8+RLZfxMQOif9o0KUKTNTjMYElWPE0r/m5VNFxTRd0NSw8qSy8dajrwX4RYI1Hw==
+  version "3.2.7"
+  resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.7.tgz#609f39207cb614b89d0765b477cb2d437fbf9787"
+  integrity sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==
 
 flush-write-stream@^1.0.0:
   version "1.1.1"
@@ -3863,10 +3975,20 @@ function-bind@^1.1.1:
   resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d"
   integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==
 
-functional-red-black-tree@^1.0.1:
-  version "1.0.1"
-  resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327"
-  integrity sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=
+function.prototype.name@^1.1.5:
+  version "1.1.5"
+  resolved "https://registry.yarnpkg.com/function.prototype.name/-/function.prototype.name-1.1.5.tgz#cce0505fe1ffb80503e6f9e46cc64e46a12a9621"
+  integrity sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA==
+  dependencies:
+    call-bind "^1.0.2"
+    define-properties "^1.1.3"
+    es-abstract "^1.19.0"
+    functions-have-names "^1.2.2"
+
+functions-have-names@^1.2.2:
+  version "1.2.3"
+  resolved "https://registry.yarnpkg.com/functions-have-names/-/functions-have-names-1.2.3.tgz#0404fe4ee2ba2f607f0e0ec3c80bae994133b834"
+  integrity sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==
 
 fuse.js@^6.5.3:
   version "6.5.3"
@@ -3933,6 +4055,15 @@ get-intrinsic@^1.0.2, get-intrinsic@^1.1.0, get-intrinsic@^1.1.1:
     has "^1.0.3"
     has-symbols "^1.0.1"
 
+get-intrinsic@^1.1.2:
+  version "1.1.3"
+  resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.1.3.tgz#063c84329ad93e83893c7f4f243ef63ffa351385"
+  integrity sha512-QJVz1Tj7MS099PevUG5jvnt9tSkXN8K14dxQlikJuPt4uD9hHAHjLyLBiLR5zELelBdD9QNRAXZzsJx0WaDL9A==
+  dependencies:
+    function-bind "^1.1.1"
+    has "^1.0.3"
+    has-symbols "^1.0.3"
+
 get-stream@^3.0.0:
   version "3.0.0"
   resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-3.0.0.tgz#8e943d1358dc37555054ecbe2edb05aa174ede14"
@@ -4100,6 +4231,11 @@ graceful-fs@~4.1.11:
   resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.15.tgz#ffb703e1066e8a0eeaa4c8b80ba9253eeefbfb00"
   integrity sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA==
 
+grapheme-splitter@^1.0.4:
+  version "1.0.4"
+  resolved "https://registry.yarnpkg.com/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz#9cf3a665c6247479896834af35cf1dbb4400767e"
+  integrity sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==
+
 handle-thing@^2.0.0:
   version "2.0.1"
   resolved "https://registry.yarnpkg.com/handle-thing/-/handle-thing-2.0.1.tgz#857f79ce359580c340d43081cc648970d0bb234e"
@@ -4123,21 +4259,38 @@ has-bigints@^1.0.1:
   resolved "https://registry.yarnpkg.com/has-bigints/-/has-bigints-1.0.1.tgz#64fe6acb020673e3b78db035a5af69aa9d07b113"
   integrity sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA==
 
+has-bigints@^1.0.2:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/has-bigints/-/has-bigints-1.0.2.tgz#0871bd3e3d51626f6ca0966668ba35d5602d6eaa"
+  integrity sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==
+
 has-flag@^3.0.0:
   version "3.0.0"
   resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd"
-  integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0=
+  integrity sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==
 
 has-flag@^4.0.0:
   version "4.0.0"
   resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b"
   integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==
 
+has-property-descriptors@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz#610708600606d36961ed04c196193b6a607fa861"
+  integrity sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==
+  dependencies:
+    get-intrinsic "^1.1.1"
+
 has-symbols@^1.0.1, has-symbols@^1.0.2:
   version "1.0.2"
   resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.2.tgz#165d3070c00309752a1236a479331e3ac56f1423"
   integrity sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==
 
+has-symbols@^1.0.3:
+  version "1.0.3"
+  resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.3.tgz#bb7b2c4349251dce87b125f7bdf874aa7c8b39f8"
+  integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==
+
 has-tostringtag@^1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/has-tostringtag/-/has-tostringtag-1.0.0.tgz#7e133818a7d394734f941e73c3d3f9291e658b25"
@@ -4157,17 +4310,12 @@ has@^1.0.3:
   dependencies:
     function-bind "^1.1.1"
 
-history@^4.10.1:
-  version "4.10.1"
-  resolved "https://registry.yarnpkg.com/history/-/history-4.10.1.tgz#33371a65e3a83b267434e2b3f3b1b4c58aad4cf3"
-  integrity sha512-36nwAD620w12kuzPAsyINPWJqlNbij+hpK1k9XRloDtym8mxzGYl2c17LnV6IAGB2Dmg4tEa7G7DlawS0+qjew==
+history@^5.3.0:
+  version "5.3.0"
+  resolved "https://registry.yarnpkg.com/history/-/history-5.3.0.tgz#1548abaa245ba47992f063a0783db91ef201c73b"
+  integrity sha512-ZqaKwjjrAYUYfLG+htGaIIZ4nioX2L70ZUMIFysS3xvBsSG4x/n1V6TXV3N8ZYNuFGlDirFg32T7B6WOUPDYcQ==
   dependencies:
-    "@babel/runtime" "^7.1.2"
-    loose-envify "^1.2.0"
-    resolve-pathname "^3.0.0"
-    tiny-invariant "^1.0.2"
-    tiny-warning "^1.0.0"
-    value-equal "^1.0.1"
+    "@babel/runtime" "^7.7.6"
 
 hoist-non-inferno-statics@^1.1.3:
   version "1.1.3"
@@ -4305,9 +4453,9 @@ husky@^8.0.1:
   integrity sha512-xs7/chUH/CKdOCs7Zy0Aev9e/dKOMZf3K1Az1nar3tzlv0jfqnYtu235bstsWTmXOR0EfINrPa97yy4Lz6RiKw==
 
 i18next@^21.8.14:
-  version "21.8.14"
-  resolved "https://registry.yarnpkg.com/i18next/-/i18next-21.8.14.tgz#03a3a669ef4520aadd9d152c80596f600e287c6a"
-  integrity sha512-4Yi+DtexvMm/Yw3Q9fllzY12SgLk+Mcmar+rCAccsOPul/2UmnBzoHbTGn/L48IPkFcmrNaH7xTLboBWIbH6pw==
+  version "21.9.2"
+  resolved "https://registry.yarnpkg.com/i18next/-/i18next-21.9.2.tgz#3f7c5594393eb27117c1db4c38f5ec766e68de0e"
+  integrity sha512-00fVrLQOwy45nm3OtC9l1WiLK3nJlIYSljgCt0qzTaAy65aciMdRy9GsuW+a2AtKtdg9/njUGfRH30LRupV7ZQ==
   dependencies:
     "@babel/runtime" "^7.17.2"
 
@@ -4348,9 +4496,9 @@ ignore@^5.1.1, ignore@^5.2.0:
   integrity sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==
 
 immutable@^4.0.0:
-  version "4.0.0"
-  resolved "https://registry.yarnpkg.com/immutable/-/immutable-4.0.0.tgz#b86f78de6adef3608395efb269a91462797e2c23"
-  integrity sha512-zIE9hX70qew5qTUjSS7wi1iwj/l7+m54KWU247nhM3v806UdGj1yDndXj+IOYxxtW9zyLI+xqFNZjTuDaLUqFw==
+  version "4.1.0"
+  resolved "https://registry.yarnpkg.com/immutable/-/immutable-4.1.0.tgz#f795787f0db780183307b9eb2091fcac1f6fafef"
+  integrity sha512-oNkuqVTA8jqG1Q6c+UglTOD1xhC1BtjKI7XkCXRkZHrN5m18/XsnUp8Q89GkQO/z+0WjonSvl0FLhDYftp46nQ==
 
 import-fresh@^2.0.0:
   version "2.0.0"
@@ -4438,7 +4586,7 @@ import-sort@^6.0.0:
 imurmurhash@^0.1.4:
   version "0.1.4"
   resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea"
-  integrity sha1-khi5srkoojixPcT7a21XbyMUU+o=
+  integrity sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==
 
 indent-string@^4.0.0:
   version "4.0.0"
@@ -4450,19 +4598,19 @@ infer-owner@^1.0.4:
   resolved "https://registry.yarnpkg.com/infer-owner/-/infer-owner-1.0.4.tgz#c4cefcaa8e51051c2a40ba2ce8a3d27295af9467"
   integrity sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==
 
-inferno-clone-vnode@^7.4.2:
-  version "7.4.11"
-  resolved "https://registry.yarnpkg.com/inferno-clone-vnode/-/inferno-clone-vnode-7.4.11.tgz#1bcce4ff6ac9ea2986766b17aee1d1a22b158558"
-  integrity sha512-6/newyzWO/lrwcA9q5DBAfslccPWqhpgrDQg/wWiHKZwRBe1kJjtiALsR+/1wLq1Z+YO3L1MPnlEnFu+nltUbA==
+inferno-clone-vnode@^8.0.3:
+  version "8.0.3"
+  resolved "https://registry.yarnpkg.com/inferno-clone-vnode/-/inferno-clone-vnode-8.0.3.tgz#01feac1f06226f59cbdcc07d513d4d23fbbe3d03"
+  integrity sha512-WPwbxwKHgT6rO++RLvJvF5pPuwxPwpH6dupDorWqkRmRwEyzszvtUJG5C2VSA3LEhH2iyV4+wC28hhtRwHN6+w==
   dependencies:
-    inferno "7.4.11"
+    inferno "8.0.3"
 
-inferno-create-element@^7.4.11, inferno-create-element@^7.4.2:
-  version "7.4.11"
-  resolved "https://registry.yarnpkg.com/inferno-create-element/-/inferno-create-element-7.4.11.tgz#1690b3ad28b61be93765b11b409dc9a9a35bf63a"
-  integrity sha512-kE6XIx2hPAd5qpDli2iGjNXgubvuyxdLvoiW71WnSzIIxA+Uxa/s8lY8m03VyHHVypFV3n329ZY5dFvKc7UQMg==
+inferno-create-element@^8.0.3:
+  version "8.0.3"
+  resolved "https://registry.yarnpkg.com/inferno-create-element/-/inferno-create-element-8.0.3.tgz#883961e497b4ab7359c1ef19a1f5388fe81137d6"
+  integrity sha512-eQ7GLpC1lGB8+BaeFvNHo6Dl3fKfbv3kobWgw5qYttRPeMaufIfn0lEkhzx2anx1Hv99INwPl+9jbpybciQLTQ==
   dependencies:
-    inferno "7.4.11"
+    inferno "8.0.3"
 
 inferno-helmet@^5.2.1:
   version "5.2.1"
@@ -4473,46 +4621,46 @@ inferno-helmet@^5.2.1:
     inferno-side-effect "^1.1.5"
     object-assign "^4.1.1"
 
-inferno-hydrate@^7.4.11:
-  version "7.4.11"
-  resolved "https://registry.yarnpkg.com/inferno-hydrate/-/inferno-hydrate-7.4.11.tgz#275027585268055f7d741c21978d7b5065169a3a"
-  integrity sha512-hF9Ke4GHAkj8GQrMXBZPfsUqhq6WjkoDCAfXhPBuF1Wiceqyy8KerOOXEnuocHky77fuEXq0AzVnQcC064Bkfw==
+inferno-hydrate@^8.0.3:
+  version "8.0.3"
+  resolved "https://registry.yarnpkg.com/inferno-hydrate/-/inferno-hydrate-8.0.3.tgz#75c3c53300a7b7b550dfa93f363956201d86f71f"
+  integrity sha512-7q1BDuVIy3XCmyKlAJhe+s1CLJhl3+gfFcBpjCq9iA7J/Z3kwo/6xjwJQbcQHNrCr/XZvTM+2uVwfxiX6wgYCQ==
   dependencies:
-    inferno "7.4.11"
+    inferno "8.0.3"
 
-inferno-i18next-dess@^0.0.1:
-  version "0.0.1"
-  resolved "https://registry.yarnpkg.com/inferno-i18next-dess/-/inferno-i18next-dess-0.0.1.tgz#48ae6bb4c3a617e59ff8dc97b9ed70f2ef762206"
-  integrity sha512-z/6UnuWFMyBivfR3SI9AmgA0/JvXARqtG/9BQryaLPenlG7iAb4cN2TeSt0mHIgFOo01QHVWZ8dqn7jZRijp2Q==
+inferno-i18next-dess@0.0.2:
+  version "0.0.2"
+  resolved "https://registry.yarnpkg.com/inferno-i18next-dess/-/inferno-i18next-dess-0.0.2.tgz#4cd4e3e2e1d900212b399a85dbfd4015e887039b"
+  integrity sha512-TkpBTZzfqgK7O8gIJ7gLB9CvP1bEOfO8OA7vUfJpd2kgGom9eoj6xbAMUPk5BNH6nBN5Y+mCaG/dInQjW5Jkug==
   dependencies:
     html-parse-stringify2 "^2.0.1"
-    inferno "^7.4.2"
-    inferno-clone-vnode "^7.4.2"
-    inferno-create-element "^7.4.2"
-    inferno-shared "^7.4.2"
-    inferno-vnode-flags "^7.4.2"
-
-inferno-router@^7.4.11:
-  version "7.4.11"
-  resolved "https://registry.yarnpkg.com/inferno-router/-/inferno-router-7.4.11.tgz#80bd47bf7c059a219cad9f3857585023fd55781f"
-  integrity sha512-7H5OTpoxGjRl2wtF1exFdlFVmO3k6EhNm0yMSfk34DOEEDqiIpy0QiWx0Q0nn7Mm2Wddgdt31RAl69/k/GuBYA==
-  dependencies:
-    history "^4.10.1"
+    inferno "^8.0.3"
+    inferno-clone-vnode "^8.0.3"
+    inferno-create-element "^8.0.3"
+    inferno-shared "^8.0.3"
+    inferno-vnode-flags "^8.0.3"
+
+inferno-router@^8.0.3:
+  version "8.0.3"
+  resolved "https://registry.yarnpkg.com/inferno-router/-/inferno-router-8.0.3.tgz#ef08da8b0cb158b8920147b260a4f84c64bc3877"
+  integrity sha512-bWkrFfIBDued0LfS/x6Bot3+J3VwGImH3ukSdIFe/GLFPBljDbvayc1JotriJtNFKA1E+1T/OZyw1E6UqIcDaA==
+  dependencies:
+    history "^5.3.0"
     hoist-non-inferno-statics "^1.1.3"
-    inferno "7.4.11"
+    inferno "8.0.3"
     path-to-regexp-es6 "1.7.0"
 
-inferno-server@^7.4.11:
-  version "7.4.11"
-  resolved "https://registry.yarnpkg.com/inferno-server/-/inferno-server-7.4.11.tgz#05ec0f7ceea4a30da412baaccbbfeca258db27cf"
-  integrity sha512-SUnkCqZNWOIrjRVoVk/E1/70O1f1ImkCX9H2gDPbS0uc3GDxuzIeCgn0rpcc0XV9KzZJ2LTGxuBtEoQQOjUn2Q==
+inferno-server@^8.0.3:
+  version "8.0.3"
+  resolved "https://registry.yarnpkg.com/inferno-server/-/inferno-server-8.0.3.tgz#4030ac5f20d9ddf191163daa10d3ef4e8b0c4593"
+  integrity sha512-YG9f9e0KsVGV6qByWCkFoGI80SckqIptHtTHMoVHsAbw3krzX414zxwA5H9OTnkubJJsSHBjVtFxaq6hHT/YcA==
   dependencies:
-    inferno "7.4.11"
+    inferno "8.0.3"
 
-inferno-shared@7.4.11, inferno-shared@^7.4.2:
-  version "7.4.11"
-  resolved "https://registry.yarnpkg.com/inferno-shared/-/inferno-shared-7.4.11.tgz#6e06e0668c4dad5f8b1fd4c688e1072faa6f1e87"
-  integrity sha512-pN725bDSTxkQmRS3e/3H02/xAqgHl+xgddCMjPm8M0etRdRcVCisi3NGPhzSbDDmiftrxhY31exs7+dwsngcDA==
+inferno-shared@^8.0.3:
+  version "8.0.3"
+  resolved "https://registry.yarnpkg.com/inferno-shared/-/inferno-shared-8.0.3.tgz#74c0cdd523937763d01945b50b65690a7064a957"
+  integrity sha512-+1ZV0DtBnYFfb5WF2FXSnCYjejGGl2BZhN5hMNRwfColRKd08N2r2i0EyLSDlTM1yB5n7BW2YdOegMf3YsV4yQ==
 
 inferno-side-effect@^1.1.5:
   version "1.1.5"
@@ -4523,18 +4671,18 @@ inferno-side-effect@^1.1.5:
     npm "^5.8.0"
     shallowequal "^1.0.1"
 
-inferno-vnode-flags@7.4.11, inferno-vnode-flags@^7.4.2:
-  version "7.4.11"
-  resolved "https://registry.yarnpkg.com/inferno-vnode-flags/-/inferno-vnode-flags-7.4.11.tgz#642a835a8dde514f244296b27c176a2b8704fbac"
-  integrity sha512-L7lslEQCq3IfwgT/b9zhuMf8fv6KXCNXXHZevk/WYxnqJsOWGDcKpJn0zkzXfvmj0otbB149iLUQVBq3oe2sfA==
+inferno-vnode-flags@8.0.3, inferno-vnode-flags@^8.0.3:
+  version "8.0.3"
+  resolved "https://registry.yarnpkg.com/inferno-vnode-flags/-/inferno-vnode-flags-8.0.3.tgz#1bd78745033719596211155ae673bd648b266a78"
+  integrity sha512-r+wJZNliJWvtcwBd66L3fMSp+M0rinHLilZeLiAX87+ZVPoXKiTDLVmTRI+1j8dm1XmC3IuAHijVfwELnEmoCQ==
 
-inferno@7.4.11, inferno@^7.4.11, inferno@^7.4.2:
-  version "7.4.11"
-  resolved "https://registry.yarnpkg.com/inferno/-/inferno-7.4.11.tgz#fcee308cf0e4a2f6066d96da74e3b425583bfc66"
-  integrity sha512-N+cs33ESWI8fdToCd98yMRYl7jkLnCkJskxov3FKKlaKOvk3PRlAttbhmUaYdWXlRvt2WeXi+J4MbzNj3V6G0w==
+inferno@8.0.3, inferno@^8.0.3:
+  version "8.0.3"
+  resolved "https://registry.yarnpkg.com/inferno/-/inferno-8.0.3.tgz#99124f050a9c60995dbdeef8ac258a936df7e62e"
+  integrity sha512-EZJyGfnlNoX4rQj9oX09ZqPmG8Ejrzqb/Y5O1D+QBN84VJ8uQGTqhZdmz3n7mhUnSyHg7VLt+rlbLnlTI4bs4g==
   dependencies:
-    inferno-shared "7.4.11"
-    inferno-vnode-flags "7.4.11"
+    csstype "^3.1.0"
+    inferno-vnode-flags "8.0.3"
     opencollective-postinstall "^2.0.3"
 
 inflight@^1.0.4, inflight@~1.0.6:
@@ -4676,10 +4824,10 @@ is-cidr@~1.0.0:
   dependencies:
     cidr-regex "1.0.6"
 
-is-core-module@^2.8.0:
-  version "2.8.1"
-  resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.8.1.tgz#f59fdfca701d5879d0a6b100a40aa1560ce27211"
-  integrity sha512-SdNCUs284hr40hFTFP6l0IfZ/RSrMXF3qgoRHd3/79unUTvrFO/JoXwkGm+5J/Oe3E/b5GsnG330uUNgRpu1PA==
+is-core-module@^2.8.0, is-core-module@^2.9.0:
+  version "2.10.0"
+  resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.10.0.tgz#9012ede0a91c69587e647514e1d5277019e728ed"
+  integrity sha512-Erxj2n/LDAZ7H8WNJXd9tw38GYM3dv8rk8Zcs+jJuxYTW7sozH+SS8NtrSjVL1/vpLvWi1hxy96IzjJ3EHTJJg==
   dependencies:
     has "^1.0.3"
 
@@ -4703,7 +4851,7 @@ is-docker@^2.0.0, is-docker@^2.1.1:
 is-extglob@^2.1.1:
   version "2.1.1"
   resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2"
-  integrity sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=
+  integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==
 
 is-fullwidth-code-point@^1.0.0:
   version "1.0.0"
@@ -4742,7 +4890,7 @@ is-installed-globally@^0.1.0:
     global-dirs "^0.1.0"
     is-path-inside "^1.0.0"
 
-is-negative-zero@^2.0.1:
+is-negative-zero@^2.0.1, is-negative-zero@^2.0.2:
   version "2.0.2"
   resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.2.tgz#7bf6f03a28003b8b3965de3ac26f664d765f3150"
   integrity sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==
@@ -4835,6 +4983,13 @@ is-shared-array-buffer@^1.0.1:
   resolved "https://registry.yarnpkg.com/is-shared-array-buffer/-/is-shared-array-buffer-1.0.1.tgz#97b0c85fbdacb59c9c446fe653b82cf2b5b7cfe6"
   integrity sha512-IU0NmyknYZN0rChcKhRO1X8LYz5Isj/Fsqh8NJOSf+N/hCOTwy29F32Ik7a+QszE63IdvmwdTPDd6cZ5pg4cwA==
 
+is-shared-array-buffer@^1.0.2:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz#8f259c573b60b6a32d4058a1a07430c0a7344c79"
+  integrity sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==
+  dependencies:
+    call-bind "^1.0.2"
+
 is-stream@^1.0.0, is-stream@^1.1.0:
   version "1.1.0"
   resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44"
@@ -4869,7 +5024,7 @@ is-typedarray@~1.0.0:
   resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a"
   integrity sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=
 
-is-weakref@^1.0.1:
+is-weakref@^1.0.1, is-weakref@^1.0.2:
   version "1.0.2"
   resolved "https://registry.yarnpkg.com/is-weakref/-/is-weakref-1.0.2.tgz#9529f383a9338205e89765e0392efc2f100f06f2"
   integrity sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==
@@ -4896,7 +5051,7 @@ isarray@~1.0.0:
 isexe@^2.0.0:
   version "2.0.0"
   resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10"
-  integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=
+  integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==
 
 isobject@^3.0.1:
   version "3.0.1"
@@ -4925,6 +5080,11 @@ jest-worker@^27.4.1:
     merge-stream "^2.0.0"
     supports-color "^8.0.0"
 
+js-sdsl@^4.1.4:
+  version "4.1.4"
+  resolved "https://registry.yarnpkg.com/js-sdsl/-/js-sdsl-4.1.4.tgz#78793c90f80e8430b7d8dc94515b6c77d98a26a6"
+  integrity sha512-Y2/yD55y5jteOAmY50JbUZYwk3CP3wnLPEZnlR1w9oKhITrBEtAxwuWKebFf8hMrPMgbYwFoWK/lH2sBkErELw==
+
 "js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0:
   version "4.0.0"
   resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499"
@@ -4988,7 +5148,7 @@ json-schema@0.4.0:
 json-stable-stringify-without-jsonify@^1.0.1:
   version "1.0.1"
   resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651"
-  integrity sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=
+  integrity sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==
 
 json-stringify-safe@~5.0.1:
   version "5.0.1"
@@ -5022,6 +5182,14 @@ jsprim@^1.2.2:
     json-schema "0.4.0"
     verror "1.10.0"
 
+jsx-ast-utils@^3.3.3:
+  version "3.3.3"
+  resolved "https://registry.yarnpkg.com/jsx-ast-utils/-/jsx-ast-utils-3.3.3.tgz#76b3e6e6cece5c69d49a5792c3d01bd1a0cdc7ea"
+  integrity sha512-fYQHZTZ8jSfmWZ0iyzfwiU4WDX4HpHbMCZ3gPlWYiCl3BoeOTsqKBqnTVfH2rYT7eP5c3sVbeSPHnnJOaTrWiw==
+  dependencies:
+    array-includes "^3.1.5"
+    object.assign "^4.1.3"
+
 jwt-decode@^3.1.2:
   version "3.1.2"
   resolved "https://registry.yarnpkg.com/jwt-decode/-/jwt-decode-3.1.2.tgz#3fb319f3675a2df0c2895c8f5e9fa4b67b04ed59"
@@ -5201,6 +5369,13 @@ locate-path@^5.0.0:
   dependencies:
     p-locate "^4.1.0"
 
+locate-path@^6.0.0:
+  version "6.0.0"
+  resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-6.0.0.tgz#55321eb309febbc59c4801d931a72452a681d286"
+  integrity sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==
+  dependencies:
+    p-locate "^5.0.0"
+
 lock-verify@^2.0.2:
   version "2.2.1"
   resolved "https://registry.yarnpkg.com/lock-verify/-/lock-verify-2.2.1.tgz#81107948c51ed16f97b96ff8b60675affb243fc1"
@@ -5243,7 +5418,7 @@ lodash.clonedeep@~4.5.0:
 lodash.debounce@^4.0.8:
   version "4.0.8"
   resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af"
-  integrity sha1-gteb/zCmfEAF/9XiUVMArZyk168=
+  integrity sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==
 
 lodash.merge@^4.6.2:
   version "4.6.2"
@@ -5285,7 +5460,7 @@ log-update@^4.0.0:
     slice-ansi "^4.0.0"
     wrap-ansi "^6.2.0"
 
-loose-envify@^1.2.0:
+loose-envify@^1.4.0:
   version "1.4.0"
   resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf"
   integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==
@@ -5483,7 +5658,7 @@ methods@~1.1.2:
   resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee"
   integrity sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=
 
-micromatch@^4.0.2, micromatch@^4.0.4:
+micromatch@^4.0.2:
   version "4.0.4"
   resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.4.tgz#896d519dfe9db25fce94ceb7a500919bf881ebf9"
   integrity sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==
@@ -5491,7 +5666,7 @@ micromatch@^4.0.2, micromatch@^4.0.4:
     braces "^3.0.1"
     picomatch "^2.2.3"
 
-micromatch@^4.0.5:
+micromatch@^4.0.4, micromatch@^4.0.5:
   version "4.0.5"
   resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.5.tgz#bc8999a7cbbf77cdc89f132f6e467051b49090c6"
   integrity sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==
@@ -5562,20 +5737,20 @@ minimalistic-assert@^1.0.0:
   resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7"
   integrity sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==
 
-minimatch@^3.0.4:
-  version "3.0.4"
-  resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083"
-  integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==
-  dependencies:
-    brace-expansion "^1.1.7"
-
-minimatch@^3.1.2:
+minimatch@^3.0.4, minimatch@^3.1.2:
   version "3.1.2"
   resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b"
   integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==
   dependencies:
     brace-expansion "^1.1.7"
 
+minimatch@^5.1.0:
+  version "5.1.0"
+  resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.1.0.tgz#1717b464f4971b144f6aabe8f2d0b8e4511e09c7"
+  integrity sha512-9TPBGGak4nHfGZsPBohm9AWg6NoT7QTCehS3BIJABslyZbzxfV78QM2Y6+i741OPZIafFAaiiEMh5OyIrJPgtg==
+  dependencies:
+    brace-expansion "^2.0.1"
+
 minimist@^1.2.0, minimist@^1.2.5:
   version "1.2.5"
   resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602"
@@ -5704,7 +5879,7 @@ nanoid@^3.3.1:
 natural-compare@^1.4.0:
   version "1.4.0"
   resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7"
-  integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=
+  integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==
 
 negotiator@0.6.2:
   version "0.6.2"
@@ -5731,9 +5906,9 @@ node-fetch-npm@^2.0.2:
     safe-buffer "^5.1.1"
 
 node-fetch@^2.6.1:
-  version "2.6.6"
-  resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.6.tgz#1751a7c01834e8e1697758732e9efb6eeadfaf89"
-  integrity sha512-Z8/6vRlTUChSdIgMa51jxQ4lrw/Jy5SOW10ObaA47/RElsAN2c5Pn8bTgFGWn/ibwzXTE8qwr1Yzx28vsecXEA==
+  version "2.6.7"
+  resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.7.tgz#24de9fba827e3b4ae44dc8b20256a379160052ad"
+  integrity sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==
   dependencies:
     whatwg-url "^5.0.0"
 
@@ -5777,12 +5952,7 @@ node-gyp@^4.0.0:
     tar "^4.4.8"
     which "1"
 
-node-releases@^2.0.1:
-  version "2.0.1"
-  resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.1.tgz#3d1d395f204f1f2f29a54358b9fb678765ad2fc5"
-  integrity sha512-CqyzN6z7Q6aMeF/ktcMVTzhAHCEpf8SOarwpzpf8pNBY2k5/oM34UHldUwp8VKI7uxct2HxSRdJjBaZeESzcxA==
-
-node-releases@^2.0.6:
+node-releases@^2.0.1, node-releases@^2.0.6:
   version "2.0.6"
   resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.6.tgz#8a7088c63a55e493845683ebf3c828d8c51c5503"
   integrity sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg==
@@ -6150,6 +6320,34 @@ object.assign@^4.1.0, object.assign@^4.1.2:
     has-symbols "^1.0.1"
     object-keys "^1.1.1"
 
+object.assign@^4.1.3, object.assign@^4.1.4:
+  version "4.1.4"
+  resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.4.tgz#9673c7c7c351ab8c4d0b516f4343ebf4dfb7799f"
+  integrity sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==
+  dependencies:
+    call-bind "^1.0.2"
+    define-properties "^1.1.4"
+    has-symbols "^1.0.3"
+    object-keys "^1.1.1"
+
+object.entries@^1.1.5:
+  version "1.1.5"
+  resolved "https://registry.yarnpkg.com/object.entries/-/object.entries-1.1.5.tgz#e1acdd17c4de2cd96d5a08487cfb9db84d881861"
+  integrity sha512-TyxmjUoZggd4OrrU1W66FMDG6CuqJxsFvymeyXI51+vQLN67zYfZseptRge703kKQdo4uccgAKebXFcRCzk4+g==
+  dependencies:
+    call-bind "^1.0.2"
+    define-properties "^1.1.3"
+    es-abstract "^1.19.1"
+
+object.fromentries@^2.0.5:
+  version "2.0.5"
+  resolved "https://registry.yarnpkg.com/object.fromentries/-/object.fromentries-2.0.5.tgz#7b37b205109c21e741e605727fe8b0ad5fa08251"
+  integrity sha512-CAyG5mWQRRiBU57Re4FKoTBjXfDoNwdFVH2Y1tS9PqCsfUTymAohOkEMSG3aRNKmv4lV3O7p1et7c187q6bynw==
+  dependencies:
+    call-bind "^1.0.2"
+    define-properties "^1.1.3"
+    es-abstract "^1.19.1"
+
 object.getownpropertydescriptors@^2.0.3:
   version "2.1.3"
   resolved "https://registry.yarnpkg.com/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.3.tgz#b223cf38e17fefb97a63c10c91df72ccb386df9e"
@@ -6159,6 +6357,23 @@ object.getownpropertydescriptors@^2.0.3:
     define-properties "^1.1.3"
     es-abstract "^1.19.1"
 
+object.hasown@^1.1.1:
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/object.hasown/-/object.hasown-1.1.1.tgz#ad1eecc60d03f49460600430d97f23882cf592a3"
+  integrity sha512-LYLe4tivNQzq4JdaWW6WO3HMZZJWzkkH8fnI6EebWl0VZth2wL2Lovm74ep2/gZzlaTdV62JZHEqHQ2yVn8Q/A==
+  dependencies:
+    define-properties "^1.1.4"
+    es-abstract "^1.19.5"
+
+object.values@^1.1.5:
+  version "1.1.5"
+  resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.1.5.tgz#959f63e3ce9ef108720333082131e4a459b716ac"
+  integrity sha512-QUZRW0ilQ3PnPpbNtgdNV1PDbEqLIiSFB3l+EnGtBQ/8SUTLj1PZwtQHABZtLgwpJZTSZhuGLOGk57Drx2IvYg==
+  dependencies:
+    call-bind "^1.0.2"
+    define-properties "^1.1.3"
+    es-abstract "^1.19.1"
+
 obuf@^1.0.0, obuf@^1.1.2:
   version "1.1.2"
   resolved "https://registry.yarnpkg.com/obuf/-/obuf-1.1.2.tgz#09bea3343d41859ebd446292d11c9d4db619084e"
@@ -6274,6 +6489,13 @@ p-limit@^2.0.0, p-limit@^2.2.0:
   dependencies:
     p-try "^2.0.0"
 
+p-limit@^3.0.2:
+  version "3.1.0"
+  resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b"
+  integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==
+  dependencies:
+    yocto-queue "^0.1.0"
+
 p-locate@^2.0.0:
   version "2.0.0"
   resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-2.0.0.tgz#20a0103b222a70c8fd39cc2e580680f3dde5ec43"
@@ -6295,6 +6517,13 @@ p-locate@^4.1.0:
   dependencies:
     p-limit "^2.2.0"
 
+p-locate@^5.0.0:
+  version "5.0.0"
+  resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-5.0.0.tgz#83c8315c6785005e3bd021839411c9e110e6d834"
+  integrity sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==
+  dependencies:
+    p-limit "^3.0.2"
+
 p-map@^2.0.0:
   version "2.1.0"
   resolved "https://registry.yarnpkg.com/p-map/-/p-map-2.1.0.tgz#310928feef9c9ecc65b68b17693018a665cea175"
@@ -6635,9 +6864,9 @@ prettier-plugin-import-sort@^0.0.7:
     import-sort-parser-typescript "^6.0.0"
 
 prettier-plugin-organize-imports@^3.0.0:
-  version "3.0.0"
-  resolved "https://registry.yarnpkg.com/prettier-plugin-organize-imports/-/prettier-plugin-organize-imports-3.0.0.tgz#39739423cc7069f2a3bfa58d14f8a502ff163df2"
-  integrity sha512-juSCJs5TMOqGGPaN/A/1xzWFzRPH2LG1LPLCr64dzKaVnPafJdtgDNmDVlU+8A4LbQzVJg0DTvgA8swBuIUhlg==
+  version "3.1.1"
+  resolved "https://registry.yarnpkg.com/prettier-plugin-organize-imports/-/prettier-plugin-organize-imports-3.1.1.tgz#e581cb6fa528cf72f7d95b807ce38c3e51c3c27c"
+  integrity sha512-6bHIQzybqA644h0WGUW3gpWEVbMBvzui5wCMRBi7qA++d5ov2xjjfDk8pxJJ/ardfZrGAwizKMq/fQMFdJ+0Zw==
 
 prettier-plugin-packagejson@^2.2.18:
   version "2.2.18"
@@ -6676,6 +6905,15 @@ promzard@^0.3.0:
   dependencies:
     read "1"
 
+prop-types@^15.8.1:
+  version "15.8.1"
+  resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5"
+  integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==
+  dependencies:
+    loose-envify "^1.4.0"
+    object-assign "^4.1.1"
+    react-is "^16.13.1"
+
 proto-list@~1.2.1:
   version "1.2.4"
   resolved "https://registry.yarnpkg.com/proto-list/-/proto-list-1.2.4.tgz#212d5bfe1318306a420f6402b8e26ff39647a849"
@@ -6818,6 +7056,11 @@ rc@^1.0.1, rc@^1.1.6:
     minimist "^1.2.0"
     strip-json-comments "~2.0.1"
 
+react-is@^16.13.1:
+  version "16.13.1"
+  resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"
+  integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==
+
 read-cmd-shim@^1.0.1, read-cmd-shim@~1.0.1:
   version "1.0.5"
   resolved "https://registry.yarnpkg.com/read-cmd-shim/-/read-cmd-shim-1.0.5.tgz#87e43eba50098ba5a32d0ceb583ab8e43b961c16"
@@ -6989,6 +7232,15 @@ regexp.prototype.flags@^1.2.0:
     call-bind "^1.0.2"
     define-properties "^1.1.3"
 
+regexp.prototype.flags@^1.4.3:
+  version "1.4.3"
+  resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz#87cab30f80f66660181a3bb7bf5981a872b367ac"
+  integrity sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA==
+  dependencies:
+    call-bind "^1.0.2"
+    define-properties "^1.1.3"
+    functions-have-names "^1.2.2"
+
 regexpp@^3.2.0:
   version "3.2.0"
   resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-3.2.0.tgz#0425a2768d8f23bad70ca4b90461fa2f1213e1b2"
@@ -7135,12 +7387,7 @@ resolve-from@^5.0.0:
   resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-5.0.0.tgz#c35225843df8f776df21c57557bc087e9dfdfc69"
   integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==
 
-resolve-pathname@^3.0.0:
-  version "3.0.0"
-  resolved "https://registry.yarnpkg.com/resolve-pathname/-/resolve-pathname-3.0.0.tgz#99d02224d3cf263689becbb393bc560313025dcd"
-  integrity sha512-C7rARubxI8bXFNB/hqcp/4iUeIXJhJZvFPFPiSPRnhU5UPxzMFIl+2E6yY6c4k9giDJAhtV+enfA+G89N6Csng==
-
-resolve@^1.10.0, resolve@^1.14.2, resolve@^1.8.1, resolve@^1.9.0:
+resolve@^1.10.0, resolve@^1.8.1, resolve@^1.9.0:
   version "1.21.0"
   resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.21.0.tgz#b51adc97f3472e6a5cf4444d34bc9d6b9037591f"
   integrity sha512-3wCbTpk5WJlyE4mSOtDLhqQmGFi0/TD9VPwmiolnk8U0wRgMEktqCXd3vy5buTO3tljvalNvKrjHEfrd2WpEKA==
@@ -7149,6 +7396,24 @@ resolve@^1.10.0, resolve@^1.14.2, resolve@^1.8.1, resolve@^1.9.0:
     path-parse "^1.0.7"
     supports-preserve-symlinks-flag "^1.0.0"
 
+resolve@^1.14.2:
+  version "1.22.1"
+  resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.1.tgz#27cb2ebb53f91abb49470a928bba7558066ac177"
+  integrity sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==
+  dependencies:
+    is-core-module "^2.9.0"
+    path-parse "^1.0.7"
+    supports-preserve-symlinks-flag "^1.0.0"
+
+resolve@^2.0.0-next.4:
+  version "2.0.0-next.4"
+  resolved "https://registry.yarnpkg.com/resolve/-/resolve-2.0.0-next.4.tgz#3d37a113d6429f496ec4752d2a2e58efb1fd4660"
+  integrity sha512-iMDbmAWtfU+MHpxt/I5iWI7cY6YVEZUQ3MBgPQ++XD1PELuJHIl82xBmObyP2KyQmkNB2dsqF7seoQQiAn5yDQ==
+  dependencies:
+    is-core-module "^2.9.0"
+    path-parse "^1.0.7"
+    supports-preserve-symlinks-flag "^1.0.0"
+
 restore-cursor@^3.1.0:
   version "3.1.0"
   resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-3.1.0.tgz#39f67c54b3a7a58cea5236d95cf0034239631f7e"
@@ -7260,9 +7525,9 @@ sass-loader@^13.0.2:
     neo-async "^2.6.2"
 
 sass@^1.54.0:
-  version "1.54.0"
-  resolved "https://registry.yarnpkg.com/sass/-/sass-1.54.0.tgz#24873673265e2a4fe3d3a997f714971db2fba1f4"
-  integrity sha512-C4zp79GCXZfK0yoHZg+GxF818/aclhp9F48XBu/+bm9vXEVAYov9iU3FBVRMq3Hx3OA4jfKL+p2K9180mEh0xQ==
+  version "1.54.9"
+  resolved "https://registry.yarnpkg.com/sass/-/sass-1.54.9.tgz#b05f14ed572869218d1a76961de60cd647221762"
+  integrity sha512-xb1hjASzEH+0L0WI9oFjqhRi51t/gagWnxLiwUNMltA0Ab6jIDkAacgKiGYKM9Jhy109osM7woEEai6SXeJo5Q==
   dependencies:
     chokidar ">=3.0.0 <4.0.0"
     immutable "^4.0.0"
@@ -7301,10 +7566,10 @@ select-hose@^2.0.0:
   resolved "https://registry.yarnpkg.com/select-hose/-/select-hose-2.0.0.tgz#625d8658f865af43ec962bfc376a37359a4994ca"
   integrity sha1-Yl2GWPhlr0Psliv8N2o3NZpJlMo=
 
-selfsigned@^2.0.1:
-  version "2.0.1"
-  resolved "https://registry.yarnpkg.com/selfsigned/-/selfsigned-2.0.1.tgz#8b2df7fa56bf014d19b6007655fff209c0ef0a56"
-  integrity sha512-LmME957M1zOsUhG+67rAjKfiWFox3SBxE/yymatMZsAx+oMrJ0YQ8AToOnyCm7xbeg2ep37IHLxdu0o2MavQOQ==
+selfsigned@^2.1.1:
+  version "2.1.1"
+  resolved "https://registry.yarnpkg.com/selfsigned/-/selfsigned-2.1.1.tgz#18a7613d714c0cd3385c48af0075abf3f266af61"
+  integrity sha512-GSL3aowiF7wa/WtSFwnUrludWFoNhftq8bUkH9pkzjpN2XSPOAYEgg6e0sS9s0rZwgJzJiQRPU18A6clnoW5wQ==
   dependencies:
     node-forge "^1"
 
@@ -7320,11 +7585,6 @@ semver-diff@^2.0.0:
   resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7"
   integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==
 
-semver@7.0.0:
-  version "7.0.0"
-  resolved "https://registry.yarnpkg.com/semver/-/semver-7.0.0.tgz#5f3ca35761e47e05b206c6daff2cf814f0316b8e"
-  integrity sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A==
-
 semver@^6.0.0, semver@^6.1.1, semver@^6.1.2, semver@^6.3.0:
   version "6.3.0"
   resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d"
@@ -7603,12 +7863,7 @@ sortpack@^2.3.0:
   resolved "https://registry.yarnpkg.com/sortpack/-/sortpack-2.3.0.tgz#90d4688e61a91fa1da61e1b0f04dab327b94a3e8"
   integrity sha512-BkqYsA2ksQkVlk2xrwEZoWaz8y26ZMLEkrgdUjrkU5GDysCBoFmDt76D+P8hfJsu9AGdsqqK8XrEpH37ZjG7yw==
 
-"source-map-js@>=0.6.2 <2.0.0":
-  version "1.0.1"
-  resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.0.1.tgz#a1741c131e3c77d048252adfa24e23b908670caf"
-  integrity sha512-4+TN2b3tqOCd/kaGRJ/sTYA0tR0mdXx26ipdolxcwtJVqEnqNYvlCAt1q3ypy4QMlYus+Zh34RNtYLoq2oQ4IA==
-
-source-map-js@^1.0.2:
+"source-map-js@>=0.6.2 <2.0.0", source-map-js@^1.0.2:
   version "1.0.2"
   resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.0.2.tgz#adbc361d9c62df380125e7f161f71c826f1e490c"
   integrity sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==
@@ -7817,6 +8072,15 @@ string.prototype.trimend@^1.0.4:
     call-bind "^1.0.2"
     define-properties "^1.1.3"
 
+string.prototype.trimend@^1.0.5:
+  version "1.0.5"
+  resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.5.tgz#914a65baaab25fbdd4ee291ca7dde57e869cb8d0"
+  integrity sha512-I7RGvmjV4pJ7O3kdf+LXFpVfdNOxtCW/2C8f6jNiW4+PQchwxkCDzlk1/7p+Wl4bqFIZeF47qAHXLuHHWKAxog==
+  dependencies:
+    call-bind "^1.0.2"
+    define-properties "^1.1.4"
+    es-abstract "^1.19.5"
+
 string.prototype.trimstart@^1.0.4:
   version "1.0.4"
   resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz#b36399af4ab2999b4c9c648bd7a3fb2bb26feeed"
@@ -7825,6 +8089,15 @@ string.prototype.trimstart@^1.0.4:
     call-bind "^1.0.2"
     define-properties "^1.1.3"
 
+string.prototype.trimstart@^1.0.5:
+  version "1.0.5"
+  resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.5.tgz#5466d93ba58cfa2134839f81d7f42437e8c01fef"
+  integrity sha512-THx16TJCGlsN0o6dl2o6ncWUsdgnLRSA23rRE5pyGBw/mLr3Ej/R2LaqCtgP8VNMGZsvMWnf9ooZPyY2bHvUFg==
+  dependencies:
+    call-bind "^1.0.2"
+    define-properties "^1.1.4"
+    es-abstract "^1.19.5"
+
 string_decoder@^1.1.1:
   version "1.3.0"
   resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e"
@@ -7986,9 +8259,9 @@ terser-webpack-plugin@^5.1.3:
     terser "^5.7.2"
 
 terser@^5.14.2:
-  version "5.14.2"
-  resolved "https://registry.yarnpkg.com/terser/-/terser-5.14.2.tgz#9ac9f22b06994d736174f4091aa368db896f1c10"
-  integrity sha512-oL0rGeM/WFQCUd0y2QrWxYnq7tfSuKBiqTjRPWrRgB46WD/kiwHwF8T23z78H6Q6kGCuuHcPB+KULHRdxvVGQA==
+  version "5.15.0"
+  resolved "https://registry.yarnpkg.com/terser/-/terser-5.15.0.tgz#e16967894eeba6e1091509ec83f0c60e179f2425"
+  integrity sha512-L1BJiXVmheAQQy+as0oF3Pwtlo4s3Wi1X2zNZ2NxOB4wx9bdS9Vk67XQENLFdLYGCK/Z2di53mTj/hBafR+dTA==
   dependencies:
     "@jridgewell/source-map" "^0.3.2"
     acorn "^8.5.0"
@@ -8032,21 +8305,11 @@ timed-out@^4.0.0:
   resolved "https://registry.yarnpkg.com/timed-out/-/timed-out-4.0.1.tgz#f32eacac5a175bea25d7fab565ab3ed8741ef56f"
   integrity sha1-8y6srFoXW+ol1/q1Zas+2HQe9W8=
 
-tiny-invariant@^1.0.2:
-  version "1.2.0"
-  resolved "https://registry.yarnpkg.com/tiny-invariant/-/tiny-invariant-1.2.0.tgz#a1141f86b672a9148c72e978a19a73b9b94a15a9"
-  integrity sha512-1Uhn/aqw5C6RI4KejVeTg6mIS7IqxnLJ8Mv2tV5rTc0qWobay7pDUz6Wi392Cnc8ak1H0F2cjoRzb2/AW4+Fvg==
-
 tiny-relative-date@^1.3.0:
   version "1.3.0"
   resolved "https://registry.yarnpkg.com/tiny-relative-date/-/tiny-relative-date-1.3.0.tgz#fa08aad501ed730f31cc043181d995c39a935e07"
   integrity sha512-MOQHpzllWxDCHHaDno30hhLfbouoYlOI8YlMNtvKe1zXbjEVhbcEovQxvZrPvtiYW630GQDoMMarCnjfyfHA+A==
 
-tiny-warning@^1.0.0:
-  version "1.0.3"
-  resolved "https://registry.yarnpkg.com/tiny-warning/-/tiny-warning-1.0.3.tgz#94a30db453df4c643d0fd566060d60a875d84754"
-  integrity sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==
-
 tippy.js@^6.3.7:
   version "6.3.7"
   resolved "https://registry.yarnpkg.com/tippy.js/-/tippy.js-6.3.7.tgz#8ccfb651d642010ed9a32ff29b0e9e19c5b8c61c"
@@ -8057,7 +8320,7 @@ tippy.js@^6.3.7:
 to-fast-properties@^2.0.0:
   version "2.0.0"
   resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e"
-  integrity sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=
+  integrity sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==
 
 to-regex-range@^5.0.1:
   version "5.0.1"
@@ -8087,7 +8350,7 @@ tough-cookie@~2.5.0:
 tr46@~0.0.3:
   version "0.0.3"
   resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a"
-  integrity sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=
+  integrity sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==
 
 tributejs@^5.1.3:
   version "5.1.3"
@@ -8159,9 +8422,9 @@ typescript@^3.2.4:
   integrity sha512-w6fIxVE/H1PkLKcCPsFqKE7Kv7QUwhU8qQY2MueZXWx5cPZdwFupLgKK3vntcK98BtNHZtAF4LA/yl2a7k8R6Q==
 
 typescript@^4.7.4:
-  version "4.7.4"
-  resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.7.4.tgz#1a88596d1cf47d59507a1bcdfb5b9dfe4d488235"
-  integrity sha512-C0WQT0gezHuw6AdY1M2jxUO83Rjf0HP7Sk1DtXj6j1EwkQNZrHAg2XPWlq62oqEhYvONq5pkC2Y9oPljWToLmQ==
+  version "4.8.3"
+  resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.8.3.tgz#d59344522c4bc464a65a730ac695007fdb66dd88"
+  integrity sha512-goMHfm00nWPa8UvR/CPSvykqf6dVV8x/dp0c5mFTMTIu0u0FlGWRioyy7Nn0PGAdHxpJZnuO/ut+PpQ8UiHAig==
 
 uc.micro@^1.0.1, uc.micro@^1.0.5:
   version "1.0.6"
@@ -8188,6 +8451,16 @@ unbox-primitive@^1.0.1:
     has-symbols "^1.0.2"
     which-boxed-primitive "^1.0.2"
 
+unbox-primitive@^1.0.2:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/unbox-primitive/-/unbox-primitive-1.0.2.tgz#29032021057d5e6cdbd08c5129c226dff8ed6f9e"
+  integrity sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==
+  dependencies:
+    call-bind "^1.0.2"
+    has-bigints "^1.0.2"
+    has-symbols "^1.0.3"
+    which-boxed-primitive "^1.0.2"
+
 unicode-canonical-property-names-ecmascript@^2.0.0:
   version "2.0.0"
   resolved "https://registry.yarnpkg.com/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz#301acdc525631670d39f6146e0e77ff6bbdebddc"
@@ -8242,10 +8515,10 @@ unzip-response@^2.0.1:
   resolved "https://registry.yarnpkg.com/unzip-response/-/unzip-response-2.0.1.tgz#d2f0f737d16b0615e72a6935ed04214572d56f97"
   integrity sha1-0vD3N9FrBhXnKmk17QQhRXLVb5c=
 
-update-browserslist-db@^1.0.5:
-  version "1.0.5"
-  resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.5.tgz#be06a5eedd62f107b7c19eb5bcefb194411abf38"
-  integrity sha512-dteFFpCyvuDdr9S/ff1ISkKt/9YZxKjI9WlRR99c180GaztJtRa/fn18FdxGVKVsnPY7/a/FDN68mcvUmP4U7Q==
+update-browserslist-db@^1.0.9:
+  version "1.0.9"
+  resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.9.tgz#2924d3927367a38d5c555413a7ce138fc95fcb18"
+  integrity sha512-/xsqn21EGVdXI3EXSum1Yckj3ZVZugqyOZQ/CxYPBD/R+ko9NSUScf8tFF4dOKY+2pvSSJA/S+5B8s4Zr4kyvg==
   dependencies:
     escalade "^3.1.1"
     picocolors "^1.0.0"
@@ -8312,11 +8585,6 @@ uuid@^8.3.2:
   resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2"
   integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==
 
-v8-compile-cache@^2.0.3:
-  version "2.3.0"
-  resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz#2de19618c66dc247dcfb6f99338035d8245a2cee"
-  integrity sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==
-
 validate-npm-package-license@^3.0.1, validate-npm-package-license@^3.0.3:
   version "3.0.4"
   resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz#fc91f6b9c7ba15c857f4cb2c5defeec39d4f410a"
@@ -8332,11 +8600,6 @@ validate-npm-package-name@^3.0.0, validate-npm-package-name@~3.0.0:
   dependencies:
     builtins "^1.0.3"
 
-value-equal@^1.0.1:
-  version "1.0.1"
-  resolved "https://registry.yarnpkg.com/value-equal/-/value-equal-1.0.1.tgz#1e0b794c734c5c0cade179c437d356d931a34d6c"
-  integrity sha512-NOJ6JZCAWr0zlxZt+xqCHNTEKOsrks2HQd4MqhP1qy4z1SkbEP467eNx6TgDKXMvUOb+OENfJCZwM+16n7fRfw==
-
 vary@~1.1.2:
   version "1.1.2"
   resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc"
@@ -8381,7 +8644,7 @@ wcwidth@^1.0.0:
 webidl-conversions@^3.0.0:
   version "3.0.1"
   resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871"
-  integrity sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE=
+  integrity sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==
 
 webpack-cli@^4.10.0:
   version "4.10.0"
@@ -8412,10 +8675,10 @@ webpack-dev-middleware@^5.3.1:
     range-parser "^1.2.1"
     schema-utils "^4.0.0"
 
-webpack-dev-server@4.9.3:
-  version "4.9.3"
-  resolved "https://registry.yarnpkg.com/webpack-dev-server/-/webpack-dev-server-4.9.3.tgz#2360a5d6d532acb5410a668417ad549ee3b8a3c9"
-  integrity sha512-3qp/eoboZG5/6QgiZ3llN8TUzkSpYg1Ko9khWX1h40MIEUNS2mDoIa8aXsPfskER+GbTvs/IJZ1QTBBhhuetSw==
+webpack-dev-server@4.11.1:
+  version "4.11.1"
+  resolved "https://registry.yarnpkg.com/webpack-dev-server/-/webpack-dev-server-4.11.1.tgz#ae07f0d71ca0438cf88446f09029b92ce81380b5"
+  integrity sha512-lILVz9tAUy1zGFwieuaQtYiadImb5M3d+H+L1zDYalYoDl0cksAB1UNyuE5MMWJrG6zR1tXkCP2fitl7yoUJiw==
   dependencies:
     "@types/bonjour" "^3.5.9"
     "@types/connect-history-api-fallback" "^1.3.5"
@@ -8440,7 +8703,7 @@ webpack-dev-server@4.9.3:
     p-retry "^4.5.0"
     rimraf "^3.0.2"
     schema-utils "^4.0.0"
-    selfsigned "^2.0.1"
+    selfsigned "^2.1.1"
     serve-index "^1.9.1"
     sockjs "^0.3.24"
     spdy "^4.0.2"
@@ -8517,7 +8780,7 @@ websocket-ts@^1.1.1:
 whatwg-url@^5.0.0:
   version "5.0.0"
   resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d"
-  integrity sha1-lmRU6HZUYuN2RNNib2dCzotwll0=
+  integrity sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==
   dependencies:
     tr46 "~0.0.3"
     webidl-conversions "^3.0.0"
@@ -8727,3 +8990,8 @@ yargs@^8.0.2:
     which-module "^2.0.0"
     y18n "^3.2.1"
     yargs-parser "^7.0.0"
+
+yocto-queue@^0.1.0:
+  version "0.1.0"
+  resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b"
+  integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==