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