]> Untitled Git - lemmy-ui.git/blob - src/shared/components/person/settings.tsx
Fix language names (#580)
[lemmy-ui.git] / src / shared / components / person / settings.tsx
1 import { Component, linkEvent } from "inferno";
2 import {
3   BlockCommunity,
4   BlockCommunityResponse,
5   BlockPerson,
6   BlockPersonResponse,
7   ChangePassword,
8   CommunityBlockView,
9   CommunityView,
10   DeleteAccount,
11   GetSiteResponse,
12   ListingType,
13   LoginResponse,
14   PersonBlockView,
15   PersonViewSafe,
16   SaveUserSettings,
17   SortType,
18   UserOperation,
19 } from "lemmy-js-client";
20 import { Subscription } from "rxjs";
21 import { i18n, languages } from "../../i18next";
22 import { UserService, WebSocketService } from "../../services";
23 import {
24   authField,
25   capitalizeFirstLetter,
26   choicesConfig,
27   communitySelectName,
28   communityToChoice,
29   debounce,
30   elementUrl,
31   fetchCommunities,
32   fetchUsers,
33   getLanguages,
34   isBrowser,
35   personSelectName,
36   personToChoice,
37   setIsoData,
38   setTheme,
39   setupTippy,
40   showLocal,
41   themes,
42   toast,
43   updateCommunityBlock,
44   updatePersonBlock,
45   wsClient,
46   wsJsonToRes,
47   wsSubscribe,
48   wsUserOp,
49 } from "../../utils";
50 import { HtmlTags } from "../common/html-tags";
51 import { Icon, Spinner } from "../common/icon";
52 import { ImageUploadForm } from "../common/image-upload-form";
53 import { ListingTypeSelect } from "../common/listing-type-select";
54 import { MarkdownTextArea } from "../common/markdown-textarea";
55 import { SortSelect } from "../common/sort-select";
56 import { CommunityLink } from "../community/community-link";
57 import { PersonListing } from "./person-listing";
58
59 var Choices: any;
60 if (isBrowser()) {
61   Choices = require("choices.js");
62 }
63
64 interface SettingsState {
65   saveUserSettingsForm: SaveUserSettings;
66   changePasswordForm: ChangePassword;
67   saveUserSettingsLoading: boolean;
68   changePasswordLoading: boolean;
69   deleteAccountLoading: boolean;
70   deleteAccountShowConfirm: boolean;
71   deleteAccountForm: DeleteAccount;
72   personBlocks: PersonBlockView[];
73   blockPersonId: number;
74   blockPerson?: PersonViewSafe;
75   communityBlocks: CommunityBlockView[];
76   blockCommunityId: number;
77   blockCommunity?: CommunityView;
78   currentTab: string;
79   siteRes: GetSiteResponse;
80 }
81
82 export class Settings extends Component<any, SettingsState> {
83   private isoData = setIsoData(this.context);
84   private blockPersonChoices: any;
85   private blockCommunityChoices: any;
86   private subscription: Subscription;
87   private emptyState: SettingsState = {
88     saveUserSettingsForm: {
89       auth: authField(false),
90     },
91     changePasswordForm: {
92       new_password: null,
93       new_password_verify: null,
94       old_password: null,
95       auth: authField(false),
96     },
97     saveUserSettingsLoading: null,
98     changePasswordLoading: false,
99     deleteAccountLoading: null,
100     deleteAccountShowConfirm: false,
101     deleteAccountForm: {
102       password: null,
103       auth: authField(false),
104     },
105     personBlocks: [],
106     blockPersonId: 0,
107     communityBlocks: [],
108     blockCommunityId: 0,
109     currentTab: "settings",
110     siteRes: this.isoData.site_res,
111   };
112
113   constructor(props: any, context: any) {
114     super(props, context);
115
116     this.state = this.emptyState;
117     this.handleSortTypeChange = this.handleSortTypeChange.bind(this);
118     this.handleListingTypeChange = this.handleListingTypeChange.bind(this);
119     this.handleBioChange = this.handleBioChange.bind(this);
120
121     this.handleAvatarUpload = this.handleAvatarUpload.bind(this);
122     this.handleAvatarRemove = this.handleAvatarRemove.bind(this);
123
124     this.handleBannerUpload = this.handleBannerUpload.bind(this);
125     this.handleBannerRemove = this.handleBannerRemove.bind(this);
126
127     this.parseMessage = this.parseMessage.bind(this);
128     this.subscription = wsSubscribe(this.parseMessage);
129
130     this.setUserInfo();
131   }
132
133   componentDidMount() {
134     setupTippy();
135   }
136
137   componentWillUnmount() {
138     this.subscription.unsubscribe();
139   }
140
141   get documentTitle(): string {
142     return i18n.t("settings");
143   }
144
145   render() {
146     return (
147       <div class="container">
148         <>
149           <HtmlTags
150             title={this.documentTitle}
151             path={this.context.router.route.match.url}
152             description={this.documentTitle}
153             image={this.state.saveUserSettingsForm.avatar}
154           />
155           <ul class="nav nav-tabs mb-2">
156             <li class="nav-item">
157               <button
158                 class={`nav-link btn ${
159                   this.state.currentTab == "settings" && "active"
160                 }`}
161                 onClick={linkEvent(
162                   { ctx: this, tab: "settings" },
163                   this.handleSwitchTab
164                 )}
165               >
166                 {i18n.t("settings")}
167               </button>
168             </li>
169             <li class="nav-item">
170               <button
171                 class={`nav-link btn ${
172                   this.state.currentTab == "blocks" && "active"
173                 }`}
174                 onClick={linkEvent(
175                   { ctx: this, tab: "blocks" },
176                   this.handleSwitchTab
177                 )}
178               >
179                 {i18n.t("blocks")}
180               </button>
181             </li>
182           </ul>
183           {this.state.currentTab == "settings" && this.userSettings()}
184           {this.state.currentTab == "blocks" && this.blockCards()}
185         </>
186       </div>
187     );
188   }
189
190   userSettings() {
191     return (
192       <div class="row">
193         <div class="col-12 col-md-6">
194           <div class="card border-secondary mb-3">
195             <div class="card-body">{this.saveUserSettingsHtmlForm()}</div>
196           </div>
197         </div>
198         <div class="col-12 col-md-6">
199           <div class="card border-secondary mb-3">
200             <div class="card-body">{this.changePasswordHtmlForm()}</div>
201           </div>
202         </div>
203       </div>
204     );
205   }
206
207   blockCards() {
208     return (
209       <div class="row">
210         <div class="col-12 col-md-6">
211           <div class="card border-secondary mb-3">
212             <div class="card-body">{this.blockUserCard()}</div>
213           </div>
214         </div>
215         <div class="col-12 col-md-6">
216           <div class="card border-secondary mb-3">
217             <div class="card-body">{this.blockCommunityCard()}</div>
218           </div>
219         </div>
220       </div>
221     );
222   }
223
224   changePasswordHtmlForm() {
225     return (
226       <>
227         <h5>{i18n.t("change_password")}</h5>
228         <form onSubmit={linkEvent(this, this.handleChangePasswordSubmit)}>
229           <div class="form-group row">
230             <label class="col-sm-5 col-form-label" htmlFor="user-password">
231               {i18n.t("new_password")}
232             </label>
233             <div class="col-sm-7">
234               <input
235                 type="password"
236                 id="user-password"
237                 class="form-control"
238                 value={this.state.changePasswordForm.new_password}
239                 autoComplete="new-password"
240                 maxLength={60}
241                 onInput={linkEvent(this, this.handleNewPasswordChange)}
242               />
243             </div>
244           </div>
245           <div class="form-group row">
246             <label
247               class="col-sm-5 col-form-label"
248               htmlFor="user-verify-password"
249             >
250               {i18n.t("verify_password")}
251             </label>
252             <div class="col-sm-7">
253               <input
254                 type="password"
255                 id="user-verify-password"
256                 class="form-control"
257                 value={this.state.changePasswordForm.new_password_verify}
258                 autoComplete="new-password"
259                 maxLength={60}
260                 onInput={linkEvent(this, this.handleNewPasswordVerifyChange)}
261               />
262             </div>
263           </div>
264           <div class="form-group row">
265             <label class="col-sm-5 col-form-label" htmlFor="user-old-password">
266               {i18n.t("old_password")}
267             </label>
268             <div class="col-sm-7">
269               <input
270                 type="password"
271                 id="user-old-password"
272                 class="form-control"
273                 value={this.state.changePasswordForm.old_password}
274                 autoComplete="new-password"
275                 maxLength={60}
276                 onInput={linkEvent(this, this.handleOldPasswordChange)}
277               />
278             </div>
279           </div>
280           <div class="form-group">
281             <button type="submit" class="btn btn-block btn-secondary mr-4">
282               {this.state.changePasswordLoading ? (
283                 <Spinner />
284               ) : (
285                 capitalizeFirstLetter(i18n.t("save"))
286               )}
287             </button>
288           </div>
289         </form>
290       </>
291     );
292   }
293
294   blockUserCard() {
295     return (
296       <div>
297         {this.blockUserForm()}
298         {this.blockedUsersList()}
299       </div>
300     );
301   }
302
303   blockedUsersList() {
304     return (
305       <>
306         <h5>{i18n.t("blocked_users")}</h5>
307         <ul class="list-unstyled mb-0">
308           {this.state.personBlocks.map(pb => (
309             <li>
310               <span>
311                 <PersonListing person={pb.target} />
312                 <button
313                   className="btn btn-sm"
314                   onClick={linkEvent(
315                     { ctx: this, recipientId: pb.target.id },
316                     this.handleUnblockPerson
317                   )}
318                   data-tippy-content={i18n.t("unblock_user")}
319                 >
320                   <Icon icon="x" classes="icon-inline" />
321                 </button>
322               </span>
323             </li>
324           ))}
325         </ul>
326       </>
327     );
328   }
329
330   blockUserForm() {
331     return (
332       <div class="form-group row">
333         <label class="col-md-4 col-form-label" htmlFor="block-person-filter">
334           {i18n.t("block_user")}
335         </label>
336         <div class="col-md-8">
337           <select
338             class="form-control"
339             id="block-person-filter"
340             value={this.state.blockPersonId}
341           >
342             <option value="0">—</option>
343             {this.state.blockPerson && (
344               <option value={this.state.blockPerson.person.id}>
345                 {personSelectName(this.state.blockPerson)}
346               </option>
347             )}
348           </select>
349         </div>
350       </div>
351     );
352   }
353
354   blockCommunityCard() {
355     return (
356       <div>
357         {this.blockCommunityForm()}
358         {this.blockedCommunitiesList()}
359       </div>
360     );
361   }
362
363   blockedCommunitiesList() {
364     return (
365       <>
366         <h5>{i18n.t("blocked_communities")}</h5>
367         <ul class="list-unstyled mb-0">
368           {this.state.communityBlocks.map(cb => (
369             <li>
370               <span>
371                 <CommunityLink community={cb.community} />
372                 <button
373                   className="btn btn-sm"
374                   onClick={linkEvent(
375                     { ctx: this, communityId: cb.community.id },
376                     this.handleUnblockCommunity
377                   )}
378                   data-tippy-content={i18n.t("unblock_community")}
379                 >
380                   <Icon icon="x" classes="icon-inline" />
381                 </button>
382               </span>
383             </li>
384           ))}
385         </ul>
386       </>
387     );
388   }
389
390   blockCommunityForm() {
391     return (
392       <div class="form-group row">
393         <label class="col-md-4 col-form-label" htmlFor="block-community-filter">
394           {i18n.t("block_community")}
395         </label>
396         <div class="col-md-8">
397           <select
398             class="form-control"
399             id="block-community-filter"
400             value={this.state.blockCommunityId}
401           >
402             <option value="0">—</option>
403             {this.state.blockCommunity && (
404               <option value={this.state.blockCommunity.community.id}>
405                 {communitySelectName(this.state.blockCommunity)}
406               </option>
407             )}
408           </select>
409         </div>
410       </div>
411     );
412   }
413
414   saveUserSettingsHtmlForm() {
415     return (
416       <>
417         <h5>{i18n.t("settings")}</h5>
418         <form onSubmit={linkEvent(this, this.handleSaveSettingsSubmit)}>
419           <div class="form-group row">
420             <label class="col-sm-5 col-form-label" htmlFor="display-name">
421               {i18n.t("display_name")}
422             </label>
423             <div class="col-sm-7">
424               <input
425                 id="display-name"
426                 type="text"
427                 class="form-control"
428                 placeholder={i18n.t("optional")}
429                 value={this.state.saveUserSettingsForm.display_name}
430                 onInput={linkEvent(this, this.handleDisplayNameChange)}
431                 pattern="^(?!@)(.+)$"
432                 minLength={3}
433               />
434             </div>
435           </div>
436           <div class="form-group row">
437             <label class="col-sm-3 col-form-label" htmlFor="user-bio">
438               {i18n.t("bio")}
439             </label>
440             <div class="col-sm-9">
441               <MarkdownTextArea
442                 initialContent={this.state.saveUserSettingsForm.bio}
443                 onContentChange={this.handleBioChange}
444                 maxLength={300}
445                 hideNavigationWarnings
446               />
447             </div>
448           </div>
449           <div class="form-group row">
450             <label class="col-sm-3 col-form-label" htmlFor="user-email">
451               {i18n.t("email")}
452             </label>
453             <div class="col-sm-9">
454               <input
455                 type="email"
456                 id="user-email"
457                 class="form-control"
458                 placeholder={i18n.t("optional")}
459                 value={this.state.saveUserSettingsForm.email}
460                 onInput={linkEvent(this, this.handleEmailChange)}
461                 minLength={3}
462               />
463             </div>
464           </div>
465           <div class="form-group row">
466             <label class="col-sm-5 col-form-label" htmlFor="matrix-user-id">
467               <a href={elementUrl} rel="noopener">
468                 {i18n.t("matrix_user_id")}
469               </a>
470             </label>
471             <div class="col-sm-7">
472               <input
473                 id="matrix-user-id"
474                 type="text"
475                 class="form-control"
476                 placeholder="@user:example.com"
477                 value={this.state.saveUserSettingsForm.matrix_user_id}
478                 onInput={linkEvent(this, this.handleMatrixUserIdChange)}
479                 pattern="^@[A-Za-z0-9._=-]+:[A-Za-z0-9.-]+\.[A-Za-z]{2,}$"
480               />
481             </div>
482           </div>
483           <div class="form-group row">
484             <label class="col-sm-3">{i18n.t("avatar")}</label>
485             <div class="col-sm-9">
486               <ImageUploadForm
487                 uploadTitle={i18n.t("upload_avatar")}
488                 imageSrc={this.state.saveUserSettingsForm.avatar}
489                 onUpload={this.handleAvatarUpload}
490                 onRemove={this.handleAvatarRemove}
491                 rounded
492               />
493             </div>
494           </div>
495           <div class="form-group row">
496             <label class="col-sm-3">{i18n.t("banner")}</label>
497             <div class="col-sm-9">
498               <ImageUploadForm
499                 uploadTitle={i18n.t("upload_banner")}
500                 imageSrc={this.state.saveUserSettingsForm.banner}
501                 onUpload={this.handleBannerUpload}
502                 onRemove={this.handleBannerRemove}
503               />
504             </div>
505           </div>
506           <div class="form-group row">
507             <label class="col-sm-3" htmlFor="user-language">
508               {i18n.t("language")}
509             </label>
510             <div class="col-sm-9">
511               <select
512                 id="user-language"
513                 value={this.state.saveUserSettingsForm.lang}
514                 onChange={linkEvent(this, this.handleLangChange)}
515                 class="custom-select w-auto"
516               >
517                 <option disabled aria-hidden="true">
518                   {i18n.t("language")}
519                 </option>
520                 <option value="browser">{i18n.t("browser_default")}</option>
521                 <option disabled aria-hidden="true">
522                   â”€â”€
523                 </option>
524                 {languages
525                   .sort((a, b) => a.code.localeCompare(b.code))
526                   .map(lang => (
527                     <option value={lang.code}>{lang.name}</option>
528                   ))}
529               </select>
530             </div>
531           </div>
532           <div class="form-group row">
533             <label class="col-sm-3" htmlFor="user-theme">
534               {i18n.t("theme")}
535             </label>
536             <div class="col-sm-9">
537               <select
538                 id="user-theme"
539                 value={this.state.saveUserSettingsForm.theme}
540                 onChange={linkEvent(this, this.handleThemeChange)}
541                 class="custom-select w-auto"
542               >
543                 <option disabled aria-hidden="true">
544                   {i18n.t("theme")}
545                 </option>
546                 <option value="browser">{i18n.t("browser_default")}</option>
547                 {themes.map(theme => (
548                   <option value={theme}>{theme}</option>
549                 ))}
550               </select>
551             </div>
552           </div>
553           <form className="form-group row">
554             <label class="col-sm-3">{i18n.t("type")}</label>
555             <div class="col-sm-9">
556               <ListingTypeSelect
557                 type_={
558                   Object.values(ListingType)[
559                     this.state.saveUserSettingsForm.default_listing_type
560                   ]
561                 }
562                 showLocal={showLocal(this.isoData)}
563                 onChange={this.handleListingTypeChange}
564               />
565             </div>
566           </form>
567           <form className="form-group row">
568             <label class="col-sm-3">{i18n.t("sort_type")}</label>
569             <div class="col-sm-9">
570               <SortSelect
571                 sort={
572                   Object.values(SortType)[
573                     this.state.saveUserSettingsForm.default_sort_type
574                   ]
575                 }
576                 onChange={this.handleSortTypeChange}
577               />
578             </div>
579           </form>
580           {this.state.siteRes.site_view.site.enable_nsfw && (
581             <div class="form-group">
582               <div class="form-check">
583                 <input
584                   class="form-check-input"
585                   id="user-show-nsfw"
586                   type="checkbox"
587                   checked={this.state.saveUserSettingsForm.show_nsfw}
588                   onChange={linkEvent(this, this.handleShowNsfwChange)}
589                 />
590                 <label class="form-check-label" htmlFor="user-show-nsfw">
591                   {i18n.t("show_nsfw")}
592                 </label>
593               </div>
594             </div>
595           )}
596           <div class="form-group">
597             <div class="form-check">
598               <input
599                 class="form-check-input"
600                 id="user-show-scores"
601                 type="checkbox"
602                 checked={this.state.saveUserSettingsForm.show_scores}
603                 onChange={linkEvent(this, this.handleShowScoresChange)}
604               />
605               <label class="form-check-label" htmlFor="user-show-scores">
606                 {i18n.t("show_scores")}
607               </label>
608             </div>
609           </div>
610           <div class="form-group">
611             <div class="form-check">
612               <input
613                 class="form-check-input"
614                 id="user-show-avatars"
615                 type="checkbox"
616                 checked={this.state.saveUserSettingsForm.show_avatars}
617                 onChange={linkEvent(this, this.handleShowAvatarsChange)}
618               />
619               <label class="form-check-label" htmlFor="user-show-avatars">
620                 {i18n.t("show_avatars")}
621               </label>
622             </div>
623           </div>
624           <div class="form-group">
625             <div class="form-check">
626               <input
627                 class="form-check-input"
628                 id="user-bot-account"
629                 type="checkbox"
630                 checked={this.state.saveUserSettingsForm.bot_account}
631                 onChange={linkEvent(this, this.handleBotAccount)}
632               />
633               <label class="form-check-label" htmlFor="user-bot-account">
634                 {i18n.t("bot_account")}
635               </label>
636             </div>
637           </div>
638           <div class="form-group">
639             <div class="form-check">
640               <input
641                 class="form-check-input"
642                 id="user-show-bot-accounts"
643                 type="checkbox"
644                 checked={this.state.saveUserSettingsForm.show_bot_accounts}
645                 onChange={linkEvent(this, this.handleShowBotAccounts)}
646               />
647               <label class="form-check-label" htmlFor="user-show-bot-accounts">
648                 {i18n.t("show_bot_accounts")}
649               </label>
650             </div>
651           </div>
652           <div class="form-group">
653             <div class="form-check">
654               <input
655                 class="form-check-input"
656                 id="user-show-read-posts"
657                 type="checkbox"
658                 checked={this.state.saveUserSettingsForm.show_read_posts}
659                 onChange={linkEvent(this, this.handleReadPosts)}
660               />
661               <label class="form-check-label" htmlFor="user-show-read-posts">
662                 {i18n.t("show_read_posts")}
663               </label>
664             </div>
665           </div>
666           <div class="form-group">
667             <div class="form-check">
668               <input
669                 class="form-check-input"
670                 id="user-show-new-post-notifs"
671                 type="checkbox"
672                 checked={this.state.saveUserSettingsForm.show_new_post_notifs}
673                 onChange={linkEvent(this, this.handleShowNewPostNotifs)}
674               />
675               <label
676                 class="form-check-label"
677                 htmlFor="user-show-new-post-notifs"
678               >
679                 {i18n.t("show_new_post_notifs")}
680               </label>
681             </div>
682           </div>
683           <div class="form-group">
684             <div class="form-check">
685               <input
686                 class="form-check-input"
687                 id="user-send-notifications-to-email"
688                 type="checkbox"
689                 disabled={!this.state.saveUserSettingsForm.email}
690                 checked={
691                   this.state.saveUserSettingsForm.send_notifications_to_email
692                 }
693                 onChange={linkEvent(
694                   this,
695                   this.handleSendNotificationsToEmailChange
696                 )}
697               />
698               <label
699                 class="form-check-label"
700                 htmlFor="user-send-notifications-to-email"
701               >
702                 {i18n.t("send_notifications_to_email")}
703               </label>
704             </div>
705           </div>
706           <div class="form-group">
707             <button type="submit" class="btn btn-block btn-secondary mr-4">
708               {this.state.saveUserSettingsLoading ? (
709                 <Spinner />
710               ) : (
711                 capitalizeFirstLetter(i18n.t("save"))
712               )}
713             </button>
714           </div>
715           <hr />
716           <div class="form-group">
717             <button
718               class="btn btn-block btn-danger"
719               onClick={linkEvent(
720                 this,
721                 this.handleDeleteAccountShowConfirmToggle
722               )}
723             >
724               {i18n.t("delete_account")}
725             </button>
726             {this.state.deleteAccountShowConfirm && (
727               <>
728                 <div class="my-2 alert alert-danger" role="alert">
729                   {i18n.t("delete_account_confirm")}
730                 </div>
731                 <input
732                   type="password"
733                   value={this.state.deleteAccountForm.password}
734                   autoComplete="new-password"
735                   maxLength={60}
736                   onInput={linkEvent(
737                     this,
738                     this.handleDeleteAccountPasswordChange
739                   )}
740                   class="form-control my-2"
741                 />
742                 <button
743                   class="btn btn-danger mr-4"
744                   disabled={!this.state.deleteAccountForm.password}
745                   onClick={linkEvent(this, this.handleDeleteAccount)}
746                 >
747                   {this.state.deleteAccountLoading ? (
748                     <Spinner />
749                   ) : (
750                     capitalizeFirstLetter(i18n.t("delete"))
751                   )}
752                 </button>
753                 <button
754                   class="btn btn-secondary"
755                   onClick={linkEvent(
756                     this,
757                     this.handleDeleteAccountShowConfirmToggle
758                   )}
759                 >
760                   {i18n.t("cancel")}
761                 </button>
762               </>
763             )}
764           </div>
765         </form>
766       </>
767     );
768   }
769
770   setupBlockPersonChoices() {
771     if (isBrowser()) {
772       let selectId: any = document.getElementById("block-person-filter");
773       if (selectId) {
774         this.blockPersonChoices = new Choices(selectId, choicesConfig);
775         this.blockPersonChoices.passedElement.element.addEventListener(
776           "choice",
777           (e: any) => {
778             this.handleBlockPerson(Number(e.detail.choice.value));
779           },
780           false
781         );
782         this.blockPersonChoices.passedElement.element.addEventListener(
783           "search",
784           debounce(async (e: any) => {
785             try {
786               let persons = (await fetchUsers(e.detail.value)).users;
787               let choices = persons.map(pvs => personToChoice(pvs));
788               this.blockPersonChoices.setChoices(
789                 choices,
790                 "value",
791                 "label",
792                 true
793               );
794             } catch (err) {
795               console.error(err);
796             }
797           }, 400),
798           false
799         );
800       }
801     }
802   }
803
804   setupBlockCommunityChoices() {
805     if (isBrowser()) {
806       let selectId: any = document.getElementById("block-community-filter");
807       if (selectId) {
808         this.blockCommunityChoices = new Choices(selectId, choicesConfig);
809         this.blockCommunityChoices.passedElement.element.addEventListener(
810           "choice",
811           (e: any) => {
812             this.handleBlockCommunity(Number(e.detail.choice.value));
813           },
814           false
815         );
816         this.blockCommunityChoices.passedElement.element.addEventListener(
817           "search",
818           debounce(async (e: any) => {
819             try {
820               let communities = (await fetchCommunities(e.detail.value))
821                 .communities;
822               let choices = communities.map(cv => communityToChoice(cv));
823               this.blockCommunityChoices.setChoices(
824                 choices,
825                 "value",
826                 "label",
827                 true
828               );
829             } catch (err) {
830               console.log(err);
831             }
832           }, 400),
833           false
834         );
835       }
836     }
837   }
838
839   handleBlockPerson(personId: number) {
840     if (personId != 0) {
841       let blockUserForm: BlockPerson = {
842         person_id: personId,
843         block: true,
844         auth: authField(),
845       };
846       WebSocketService.Instance.send(wsClient.blockPerson(blockUserForm));
847     }
848   }
849
850   handleUnblockPerson(i: { ctx: Settings; recipientId: number }) {
851     let blockUserForm: BlockPerson = {
852       person_id: i.recipientId,
853       block: false,
854       auth: authField(),
855     };
856     WebSocketService.Instance.send(wsClient.blockPerson(blockUserForm));
857   }
858
859   handleBlockCommunity(community_id: number) {
860     if (community_id != 0) {
861       let blockCommunityForm: BlockCommunity = {
862         community_id,
863         block: true,
864         auth: authField(),
865       };
866       WebSocketService.Instance.send(
867         wsClient.blockCommunity(blockCommunityForm)
868       );
869     }
870   }
871
872   handleUnblockCommunity(i: { ctx: Settings; communityId: number }) {
873     let blockCommunityForm: BlockCommunity = {
874       community_id: i.communityId,
875       block: false,
876       auth: authField(),
877     };
878     WebSocketService.Instance.send(wsClient.blockCommunity(blockCommunityForm));
879   }
880
881   handleShowNsfwChange(i: Settings, event: any) {
882     i.state.saveUserSettingsForm.show_nsfw = event.target.checked;
883     i.setState(i.state);
884   }
885
886   handleShowAvatarsChange(i: Settings, event: any) {
887     i.state.saveUserSettingsForm.show_avatars = event.target.checked;
888     UserService.Instance.myUserInfo.local_user_view.local_user.show_avatars =
889       event.target.checked; // Just for instant updates
890     i.setState(i.state);
891   }
892
893   handleBotAccount(i: Settings, event: any) {
894     i.state.saveUserSettingsForm.bot_account = event.target.checked;
895     i.setState(i.state);
896   }
897
898   handleShowBotAccounts(i: Settings, event: any) {
899     i.state.saveUserSettingsForm.show_bot_accounts = event.target.checked;
900     i.setState(i.state);
901   }
902
903   handleReadPosts(i: Settings, event: any) {
904     i.state.saveUserSettingsForm.show_read_posts = event.target.checked;
905     i.setState(i.state);
906   }
907
908   handleShowNewPostNotifs(i: Settings, event: any) {
909     i.state.saveUserSettingsForm.show_new_post_notifs = event.target.checked;
910     i.setState(i.state);
911   }
912
913   handleShowScoresChange(i: Settings, event: any) {
914     i.state.saveUserSettingsForm.show_scores = event.target.checked;
915     UserService.Instance.myUserInfo.local_user_view.local_user.show_scores =
916       event.target.checked; // Just for instant updates
917     i.setState(i.state);
918   }
919
920   handleSendNotificationsToEmailChange(i: Settings, event: any) {
921     i.state.saveUserSettingsForm.send_notifications_to_email =
922       event.target.checked;
923     i.setState(i.state);
924   }
925
926   handleThemeChange(i: Settings, event: any) {
927     i.state.saveUserSettingsForm.theme = event.target.value;
928     setTheme(event.target.value, true);
929     i.setState(i.state);
930   }
931
932   handleLangChange(i: Settings, event: any) {
933     i.state.saveUserSettingsForm.lang = event.target.value;
934     i18n.changeLanguage(getLanguages(i.state.saveUserSettingsForm.lang)[0]);
935     i.setState(i.state);
936   }
937
938   handleSortTypeChange(val: SortType) {
939     this.state.saveUserSettingsForm.default_sort_type =
940       Object.keys(SortType).indexOf(val);
941     this.setState(this.state);
942   }
943
944   handleListingTypeChange(val: ListingType) {
945     this.state.saveUserSettingsForm.default_listing_type =
946       Object.keys(ListingType).indexOf(val);
947     this.setState(this.state);
948   }
949
950   handleEmailChange(i: Settings, event: any) {
951     i.state.saveUserSettingsForm.email = event.target.value;
952     i.setState(i.state);
953   }
954
955   handleBioChange(val: string) {
956     this.state.saveUserSettingsForm.bio = val;
957     this.setState(this.state);
958   }
959
960   handleAvatarUpload(url: string) {
961     this.state.saveUserSettingsForm.avatar = url;
962     this.setState(this.state);
963   }
964
965   handleAvatarRemove() {
966     this.state.saveUserSettingsForm.avatar = "";
967     this.setState(this.state);
968   }
969
970   handleBannerUpload(url: string) {
971     this.state.saveUserSettingsForm.banner = url;
972     this.setState(this.state);
973   }
974
975   handleBannerRemove() {
976     this.state.saveUserSettingsForm.banner = "";
977     this.setState(this.state);
978   }
979
980   handleDisplayNameChange(i: Settings, event: any) {
981     i.state.saveUserSettingsForm.display_name = event.target.value;
982     i.setState(i.state);
983   }
984
985   handleMatrixUserIdChange(i: Settings, event: any) {
986     i.state.saveUserSettingsForm.matrix_user_id = event.target.value;
987     if (
988       i.state.saveUserSettingsForm.matrix_user_id == "" &&
989       !UserService.Instance.myUserInfo.local_user_view.person.matrix_user_id
990     ) {
991       i.state.saveUserSettingsForm.matrix_user_id = undefined;
992     }
993     i.setState(i.state);
994   }
995
996   handleNewPasswordChange(i: Settings, event: any) {
997     i.state.changePasswordForm.new_password = event.target.value;
998     if (i.state.changePasswordForm.new_password == "") {
999       i.state.changePasswordForm.new_password = undefined;
1000     }
1001     i.setState(i.state);
1002   }
1003
1004   handleNewPasswordVerifyChange(i: Settings, event: any) {
1005     i.state.changePasswordForm.new_password_verify = event.target.value;
1006     if (i.state.changePasswordForm.new_password_verify == "") {
1007       i.state.changePasswordForm.new_password_verify = undefined;
1008     }
1009     i.setState(i.state);
1010   }
1011
1012   handleOldPasswordChange(i: Settings, event: any) {
1013     i.state.changePasswordForm.old_password = event.target.value;
1014     if (i.state.changePasswordForm.old_password == "") {
1015       i.state.changePasswordForm.old_password = undefined;
1016     }
1017     i.setState(i.state);
1018   }
1019
1020   handleSaveSettingsSubmit(i: Settings, event: any) {
1021     event.preventDefault();
1022     i.state.saveUserSettingsLoading = true;
1023     i.setState(i.state);
1024
1025     WebSocketService.Instance.send(
1026       wsClient.saveUserSettings(i.state.saveUserSettingsForm)
1027     );
1028   }
1029
1030   handleChangePasswordSubmit(i: Settings, event: any) {
1031     event.preventDefault();
1032     i.state.changePasswordLoading = true;
1033     i.setState(i.state);
1034
1035     WebSocketService.Instance.send(
1036       wsClient.changePassword(i.state.changePasswordForm)
1037     );
1038   }
1039
1040   handleDeleteAccountShowConfirmToggle(i: Settings, event: any) {
1041     event.preventDefault();
1042     i.state.deleteAccountShowConfirm = !i.state.deleteAccountShowConfirm;
1043     i.setState(i.state);
1044   }
1045
1046   handleDeleteAccountPasswordChange(i: Settings, event: any) {
1047     i.state.deleteAccountForm.password = event.target.value;
1048     i.setState(i.state);
1049   }
1050
1051   handleDeleteAccount(i: Settings, event: any) {
1052     event.preventDefault();
1053     i.state.deleteAccountLoading = true;
1054     i.setState(i.state);
1055
1056     WebSocketService.Instance.send(
1057       wsClient.deleteAccount(i.state.deleteAccountForm)
1058     );
1059   }
1060
1061   handleSwitchTab(i: { ctx: Settings; tab: string }) {
1062     i.ctx.setState({ currentTab: i.tab });
1063
1064     if (i.ctx.state.currentTab == "blocks") {
1065       i.ctx.setupBlockPersonChoices();
1066       i.ctx.setupBlockCommunityChoices();
1067     }
1068   }
1069
1070   setUserInfo() {
1071     let luv = UserService.Instance.myUserInfo.local_user_view;
1072     this.state.saveUserSettingsForm.show_nsfw = luv.local_user.show_nsfw;
1073     this.state.saveUserSettingsForm.theme = luv.local_user.theme
1074       ? luv.local_user.theme
1075       : "browser";
1076     this.state.saveUserSettingsForm.default_sort_type =
1077       luv.local_user.default_sort_type;
1078     this.state.saveUserSettingsForm.default_listing_type =
1079       luv.local_user.default_listing_type;
1080     this.state.saveUserSettingsForm.lang = luv.local_user.lang;
1081     this.state.saveUserSettingsForm.avatar = luv.person.avatar;
1082     this.state.saveUserSettingsForm.banner = luv.person.banner;
1083     this.state.saveUserSettingsForm.display_name = luv.person.display_name;
1084     this.state.saveUserSettingsForm.show_avatars = luv.local_user.show_avatars;
1085     this.state.saveUserSettingsForm.bot_account = luv.person.bot_account;
1086     this.state.saveUserSettingsForm.show_bot_accounts =
1087       luv.local_user.show_bot_accounts;
1088     this.state.saveUserSettingsForm.show_scores = luv.local_user.show_scores;
1089     this.state.saveUserSettingsForm.show_read_posts =
1090       luv.local_user.show_read_posts;
1091     this.state.saveUserSettingsForm.show_new_post_notifs =
1092       luv.local_user.show_new_post_notifs;
1093     this.state.saveUserSettingsForm.email = luv.local_user.email;
1094     this.state.saveUserSettingsForm.bio = luv.person.bio;
1095     this.state.saveUserSettingsForm.send_notifications_to_email =
1096       luv.local_user.send_notifications_to_email;
1097     this.state.saveUserSettingsForm.matrix_user_id = luv.person.matrix_user_id;
1098     this.state.personBlocks = UserService.Instance.myUserInfo.person_blocks;
1099     this.state.communityBlocks =
1100       UserService.Instance.myUserInfo.community_blocks;
1101   }
1102
1103   parseMessage(msg: any) {
1104     let op = wsUserOp(msg);
1105     console.log(msg);
1106     if (msg.error) {
1107       this.setState({
1108         saveUserSettingsLoading: false,
1109         changePasswordLoading: false,
1110         deleteAccountLoading: false,
1111       });
1112       toast(i18n.t(msg.error), "danger");
1113       return;
1114     } else if (op == UserOperation.SaveUserSettings) {
1115       let data = wsJsonToRes<LoginResponse>(msg).data;
1116       UserService.Instance.login(data);
1117       this.state.saveUserSettingsLoading = false;
1118       this.setState(this.state);
1119
1120       window.scrollTo(0, 0);
1121     } else if (op == UserOperation.ChangePassword) {
1122       let data = wsJsonToRes<LoginResponse>(msg).data;
1123       UserService.Instance.login(data);
1124       this.state.changePasswordLoading = false;
1125       this.setState(this.state);
1126       window.scrollTo(0, 0);
1127       toast(i18n.t("password_changed"));
1128     } else if (op == UserOperation.DeleteAccount) {
1129       this.setState({
1130         deleteAccountLoading: false,
1131         deleteAccountShowConfirm: false,
1132       });
1133       UserService.Instance.logout();
1134       window.location.href = "/";
1135       location.reload();
1136     } else if (op == UserOperation.BlockPerson) {
1137       let data = wsJsonToRes<BlockPersonResponse>(msg).data;
1138       this.setState({ personBlocks: updatePersonBlock(data) });
1139     } else if (op == UserOperation.BlockCommunity) {
1140       let data = wsJsonToRes<BlockCommunityResponse>(msg).data;
1141       this.setState({ communityBlocks: updateCommunityBlock(data) });
1142     }
1143   }
1144 }