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