1 import { debounce } from "@utils/helpers";
2 import { NoOptionI18nKeys } from "i18next";
3 import { Component, linkEvent } from "inferno";
5 BlockCommunityResponse,
14 } from "lemmy-js-client";
15 import { i18n, languages } from "../../i18next";
16 import { UserService } from "../../services";
17 import { HttpService, RequestState } from "../../services/HttpService";
20 capitalizeFirstLetter,
39 import { HtmlTags } from "../common/html-tags";
40 import { Icon, Spinner } from "../common/icon";
41 import { ImageUploadForm } from "../common/image-upload-form";
42 import { LanguageSelect } from "../common/language-select";
43 import { ListingTypeSelect } from "../common/listing-type-select";
44 import { MarkdownTextArea } from "../common/markdown-textarea";
45 import { SearchableSelect } from "../common/searchable-select";
46 import { SortSelect } from "../common/sort-select";
47 import Tabs from "../common/tabs";
48 import { CommunityLink } from "../community/community-link";
49 import { PersonListing } from "./person-listing";
51 interface SettingsState {
52 saveRes: RequestState<LoginResponse>;
53 changePasswordRes: RequestState<LoginResponse>;
54 deleteAccountRes: RequestState<DeleteAccountResponse>;
55 // TODO redo these forms
56 saveUserSettingsForm: {
59 default_sort_type?: SortType;
60 default_listing_type?: ListingType;
61 interface_language?: string;
64 display_name?: string;
67 matrix_user_id?: string;
68 show_avatars?: boolean;
69 show_scores?: boolean;
70 send_notifications_to_email?: boolean;
71 bot_account?: boolean;
72 show_bot_accounts?: boolean;
73 show_read_posts?: boolean;
74 show_new_post_notifs?: boolean;
75 discussion_languages?: number[];
76 generate_totp_2fa?: boolean;
79 new_password?: string;
80 new_password_verify?: string;
81 old_password?: string;
86 personBlocks: PersonBlockView[];
87 communityBlocks: CommunityBlockView[];
90 deleteAccountShowConfirm: boolean;
91 siteRes: GetSiteResponse;
92 searchCommunityLoading: boolean;
93 searchCommunityOptions: Choice[];
94 searchPersonLoading: boolean;
95 searchPersonOptions: Choice[];
98 type FilterType = "user" | "community";
107 filterType: FilterType;
109 onSearch: (text: string) => void;
110 onChange: (choice: Choice) => void;
113 <div className="mb-3 row">
115 className="col-md-4 col-form-label"
116 htmlFor={`block-${filterType}-filter`}
118 {i18n.t(`block_${filterType}` as NoOptionI18nKeys)}
120 <div className="col-md-8">
122 id={`block-${filterType}-filter`}
124 { label: emDash, value: "0", disabled: true } as Choice,
134 export class Settings extends Component<any, SettingsState> {
135 private isoData = setIsoData(this.context);
136 state: SettingsState = {
137 saveRes: { state: "empty" },
138 deleteAccountRes: { state: "empty" },
139 changePasswordRes: { state: "empty" },
140 saveUserSettingsForm: {},
141 changePasswordForm: {},
142 deleteAccountShowConfirm: false,
143 deleteAccountForm: {},
146 currentTab: "settings",
147 siteRes: this.isoData.site_res,
149 searchCommunityLoading: false,
150 searchCommunityOptions: [],
151 searchPersonLoading: false,
152 searchPersonOptions: [],
155 constructor(props: any, context: any) {
156 super(props, context);
158 this.handleSortTypeChange = this.handleSortTypeChange.bind(this);
159 this.handleListingTypeChange = this.handleListingTypeChange.bind(this);
160 this.handleBioChange = this.handleBioChange.bind(this);
161 this.handleDiscussionLanguageChange =
162 this.handleDiscussionLanguageChange.bind(this);
164 this.handleAvatarUpload = this.handleAvatarUpload.bind(this);
165 this.handleAvatarRemove = this.handleAvatarRemove.bind(this);
167 this.handleBannerUpload = this.handleBannerUpload.bind(this);
168 this.handleBannerRemove = this.handleBannerRemove.bind(this);
169 this.userSettings = this.userSettings.bind(this);
170 this.blockCards = this.blockCards.bind(this);
172 this.handleBlockPerson = this.handleBlockPerson.bind(this);
173 this.handleBlockCommunity = this.handleBlockCommunity.bind(this);
175 const mui = UserService.Instance.myUserInfo;
182 default_listing_type,
188 show_new_post_notifs,
189 send_notifications_to_email,
200 } = mui.local_user_view;
204 personBlocks: mui.person_blocks,
205 communityBlocks: mui.community_blocks,
206 saveUserSettingsForm: {
207 ...this.state.saveUserSettingsForm,
209 theme: theme ?? "browser",
211 default_listing_type,
213 discussion_languages: mui.discussion_languages,
222 show_new_post_notifs,
225 send_notifications_to_email,
232 async componentDidMount() {
234 this.setState({ themeList: await fetchThemeList() });
237 get documentTitle(): string {
238 return i18n.t("settings");
243 <div className="person-settings container-lg">
245 title={this.documentTitle}
246 path={this.context.router.route.match.url}
247 description={this.documentTitle}
248 image={this.state.saveUserSettingsForm.avatar}
254 label: i18n.t("settings"),
255 getNode: this.userSettings,
259 label: i18n.t("blocks"),
260 getNode: this.blockCards,
270 <div className="row">
271 <div className="col-12 col-md-6">
272 <div className="card border-secondary mb-3">
273 <div className="card-body">{this.saveUserSettingsHtmlForm()}</div>
276 <div className="col-12 col-md-6">
277 <div className="card border-secondary mb-3">
278 <div className="card-body">{this.changePasswordHtmlForm()}</div>
287 <div className="row">
288 <div className="col-12 col-md-6">
289 <div className="card border-secondary mb-3">
290 <div className="card-body">{this.blockUserCard()}</div>
293 <div className="col-12 col-md-6">
294 <div className="card border-secondary mb-3">
295 <div className="card-body">{this.blockCommunityCard()}</div>
302 changePasswordHtmlForm() {
305 <h5>{i18n.t("change_password")}</h5>
306 <form onSubmit={linkEvent(this, this.handleChangePasswordSubmit)}>
307 <div className="mb-3 row">
308 <label className="col-sm-5 col-form-label" htmlFor="user-password">
309 {i18n.t("new_password")}
311 <div className="col-sm-7">
315 className="form-control"
316 value={this.state.changePasswordForm.new_password}
317 autoComplete="new-password"
319 onInput={linkEvent(this, this.handleNewPasswordChange)}
323 <div className="mb-3 row">
325 className="col-sm-5 col-form-label"
326 htmlFor="user-verify-password"
328 {i18n.t("verify_password")}
330 <div className="col-sm-7">
333 id="user-verify-password"
334 className="form-control"
335 value={this.state.changePasswordForm.new_password_verify}
336 autoComplete="new-password"
338 onInput={linkEvent(this, this.handleNewPasswordVerifyChange)}
342 <div className="mb-3 row">
344 className="col-sm-5 col-form-label"
345 htmlFor="user-old-password"
347 {i18n.t("old_password")}
349 <div className="col-sm-7">
352 id="user-old-password"
353 className="form-control"
354 value={this.state.changePasswordForm.old_password}
355 autoComplete="new-password"
357 onInput={linkEvent(this, this.handleOldPasswordChange)}
361 <div className="input-group mb-3">
364 className="btn d-block btn-secondary me-4 w-100"
366 {this.state.changePasswordRes.state === "loading" ? (
369 capitalizeFirstLetter(i18n.t("save"))
379 const { searchPersonLoading, searchPersonOptions } = this.state;
385 loading={searchPersonLoading}
386 onChange={this.handleBlockPerson}
387 onSearch={this.handlePersonSearch}
388 options={searchPersonOptions}
390 {this.blockedUsersList()}
398 <h5>{i18n.t("blocked_users")}</h5>
399 <ul className="list-unstyled mb-0">
400 {this.state.personBlocks.map(pb => (
401 <li key={pb.target.id}>
403 <PersonListing person={pb.target} />
405 className="btn btn-sm"
407 { ctx: this, recipientId: pb.target.id },
408 this.handleUnblockPerson
410 data-tippy-content={i18n.t("unblock_user")}
412 <Icon icon="x" classes="icon-inline" />
422 blockCommunityCard() {
423 const { searchCommunityLoading, searchCommunityOptions } = this.state;
428 filterType="community"
429 loading={searchCommunityLoading}
430 onChange={this.handleBlockCommunity}
431 onSearch={this.handleCommunitySearch}
432 options={searchCommunityOptions}
434 {this.blockedCommunitiesList()}
439 blockedCommunitiesList() {
442 <h5>{i18n.t("blocked_communities")}</h5>
443 <ul className="list-unstyled mb-0">
444 {this.state.communityBlocks.map(cb => (
445 <li key={cb.community.id}>
447 <CommunityLink community={cb.community} />
449 className="btn btn-sm"
451 { ctx: this, communityId: cb.community.id },
452 this.handleUnblockCommunity
454 data-tippy-content={i18n.t("unblock_community")}
456 <Icon icon="x" classes="icon-inline" />
466 saveUserSettingsHtmlForm() {
467 const selectedLangs = this.state.saveUserSettingsForm.discussion_languages;
471 <h5>{i18n.t("settings")}</h5>
472 <form onSubmit={linkEvent(this, this.handleSaveSettingsSubmit)}>
473 <div className="mb-3 row">
474 <label className="col-sm-3 col-form-label" htmlFor="display-name">
475 {i18n.t("display_name")}
477 <div className="col-sm-9">
481 className="form-control"
482 placeholder={i18n.t("optional")}
483 value={this.state.saveUserSettingsForm.display_name}
484 onInput={linkEvent(this, this.handleDisplayNameChange)}
485 pattern="^(?!@)(.+)$"
490 <div className="mb-3 row">
491 <label className="col-sm-3 col-form-label" htmlFor="user-bio">
494 <div className="col-sm-9">
496 initialContent={this.state.saveUserSettingsForm.bio}
497 onContentChange={this.handleBioChange}
499 hideNavigationWarnings
500 allLanguages={this.state.siteRes.all_languages}
501 siteLanguages={this.state.siteRes.discussion_languages}
505 <div className="mb-3 row">
506 <label className="col-sm-3 col-form-label" htmlFor="user-email">
509 <div className="col-sm-9">
513 className="form-control"
514 placeholder={i18n.t("optional")}
515 value={this.state.saveUserSettingsForm.email}
516 onInput={linkEvent(this, this.handleEmailChange)}
521 <div className="mb-3 row">
522 <label className="col-sm-3 col-form-label" htmlFor="matrix-user-id">
523 <a href={elementUrl} rel={relTags}>
524 {i18n.t("matrix_user_id")}
527 <div className="col-sm-9">
531 className="form-control"
532 placeholder="@user:example.com"
533 value={this.state.saveUserSettingsForm.matrix_user_id}
534 onInput={linkEvent(this, this.handleMatrixUserIdChange)}
535 pattern="^@[A-Za-z0-9._=-]+:[A-Za-z0-9.-]+\.[A-Za-z]{2,}$"
539 <div className="mb-3 row">
540 <label className="col-sm-3 col-form-label">
543 <div className="col-sm-9">
545 uploadTitle={i18n.t("upload_avatar")}
546 imageSrc={this.state.saveUserSettingsForm.avatar}
547 onUpload={this.handleAvatarUpload}
548 onRemove={this.handleAvatarRemove}
553 <div className="mb-3 row">
554 <label className="col-sm-3 col-form-label">
557 <div className="col-sm-9">
559 uploadTitle={i18n.t("upload_banner")}
560 imageSrc={this.state.saveUserSettingsForm.banner}
561 onUpload={this.handleBannerUpload}
562 onRemove={this.handleBannerRemove}
566 <div className="mb-3 row">
567 <label className="col-sm-3 form-label" htmlFor="user-language">
568 {i18n.t("interface_language")}
570 <div className="col-sm-9">
573 value={this.state.saveUserSettingsForm.interface_language}
574 onChange={linkEvent(this, this.handleInterfaceLangChange)}
575 className="form-select d-inline-block w-auto"
577 <option disabled aria-hidden="true">
578 {i18n.t("interface_language")}
580 <option value="browser">{i18n.t("browser_default")}</option>
581 <option disabled aria-hidden="true">
585 .sort((a, b) => a.code.localeCompare(b.code))
587 <option key={lang.code} value={lang.code}>
595 allLanguages={this.state.siteRes.all_languages}
596 siteLanguages={this.state.siteRes.discussion_languages}
597 selectedLanguageIds={selectedLangs}
599 showLanguageWarning={true}
601 onChange={this.handleDiscussionLanguageChange}
603 <div className="mb-3 row">
604 <label className="col-sm-3 col-form-label" htmlFor="user-theme">
607 <div className="col-sm-9">
610 value={this.state.saveUserSettingsForm.theme}
611 onChange={linkEvent(this, this.handleThemeChange)}
612 className="form-select d-inline-block w-auto"
614 <option disabled aria-hidden="true">
617 <option value="browser">{i18n.t("browser_default")}</option>
618 {this.state.themeList.map(theme => (
619 <option key={theme} value={theme}>
626 <form className="mb-3 row">
627 <label className="col-sm-3 col-form-label">{i18n.t("type")}</label>
628 <div className="col-sm-9">
631 this.state.saveUserSettingsForm.default_listing_type ??
634 showLocal={showLocal(this.isoData)}
636 onChange={this.handleListingTypeChange}
640 <form className="mb-3 row">
641 <label className="col-sm-3 col-form-label">
642 {i18n.t("sort_type")}
644 <div className="col-sm-9">
647 this.state.saveUserSettingsForm.default_sort_type ?? "Active"
649 onChange={this.handleSortTypeChange}
653 <div className="input-group mb-3">
654 <div className="form-check">
656 className="form-check-input"
659 checked={this.state.saveUserSettingsForm.show_nsfw}
660 onChange={linkEvent(this, this.handleShowNsfwChange)}
662 <label className="form-check-label" htmlFor="user-show-nsfw">
663 {i18n.t("show_nsfw")}
667 <div className="input-group mb-3">
668 <div className="form-check">
670 className="form-check-input"
671 id="user-show-scores"
673 checked={this.state.saveUserSettingsForm.show_scores}
674 onChange={linkEvent(this, this.handleShowScoresChange)}
676 <label className="form-check-label" htmlFor="user-show-scores">
677 {i18n.t("show_scores")}
681 <div className="input-group mb-3">
682 <div className="form-check">
684 className="form-check-input"
685 id="user-show-avatars"
687 checked={this.state.saveUserSettingsForm.show_avatars}
688 onChange={linkEvent(this, this.handleShowAvatarsChange)}
690 <label className="form-check-label" htmlFor="user-show-avatars">
691 {i18n.t("show_avatars")}
695 <div className="input-group mb-3">
696 <div className="form-check">
698 className="form-check-input"
699 id="user-bot-account"
701 checked={this.state.saveUserSettingsForm.bot_account}
702 onChange={linkEvent(this, this.handleBotAccount)}
704 <label className="form-check-label" htmlFor="user-bot-account">
705 {i18n.t("bot_account")}
709 <div className="input-group mb-3">
710 <div className="form-check">
712 className="form-check-input"
713 id="user-show-bot-accounts"
715 checked={this.state.saveUserSettingsForm.show_bot_accounts}
716 onChange={linkEvent(this, this.handleShowBotAccounts)}
719 className="form-check-label"
720 htmlFor="user-show-bot-accounts"
722 {i18n.t("show_bot_accounts")}
726 <div className="input-group mb-3">
727 <div className="form-check">
729 className="form-check-input"
730 id="user-show-read-posts"
732 checked={this.state.saveUserSettingsForm.show_read_posts}
733 onChange={linkEvent(this, this.handleReadPosts)}
736 className="form-check-label"
737 htmlFor="user-show-read-posts"
739 {i18n.t("show_read_posts")}
743 <div className="input-group mb-3">
744 <div className="form-check">
746 className="form-check-input"
747 id="user-show-new-post-notifs"
749 checked={this.state.saveUserSettingsForm.show_new_post_notifs}
750 onChange={linkEvent(this, this.handleShowNewPostNotifs)}
753 className="form-check-label"
754 htmlFor="user-show-new-post-notifs"
756 {i18n.t("show_new_post_notifs")}
760 <div className="input-group mb-3">
761 <div className="form-check">
763 className="form-check-input"
764 id="user-send-notifications-to-email"
766 disabled={!this.state.saveUserSettingsForm.email}
768 this.state.saveUserSettingsForm.send_notifications_to_email
772 this.handleSendNotificationsToEmailChange
776 className="form-check-label"
777 htmlFor="user-send-notifications-to-email"
779 {i18n.t("send_notifications_to_email")}
784 <div className="input-group mb-3">
785 <button type="submit" className="btn d-block btn-secondary me-4">
786 {this.state.saveRes.state === "loading" ? (
789 capitalizeFirstLetter(i18n.t("save"))
794 <div className="input-group mb-3">
796 className="btn d-block btn-danger"
799 this.handleDeleteAccountShowConfirmToggle
802 {i18n.t("delete_account")}
804 {this.state.deleteAccountShowConfirm && (
806 <div className="my-2 alert alert-danger" role="alert">
807 {i18n.t("delete_account_confirm")}
811 value={this.state.deleteAccountForm.password}
812 autoComplete="new-password"
816 this.handleDeleteAccountPasswordChange
818 className="form-control my-2"
821 className="btn btn-danger me-4"
822 disabled={!this.state.deleteAccountForm.password}
823 onClick={linkEvent(this, this.handleDeleteAccount)}
825 {this.state.deleteAccountRes.state === "loading" ? (
828 capitalizeFirstLetter(i18n.t("delete"))
832 className="btn btn-secondary"
835 this.handleDeleteAccountShowConfirmToggle
850 UserService.Instance.myUserInfo?.local_user_view.local_user.totp_2fa_url;
855 <div className="input-group mb-3">
856 <div className="form-check">
858 className="form-check-input"
859 id="user-generate-totp"
861 checked={this.state.saveUserSettingsForm.generate_totp_2fa}
862 onChange={linkEvent(this, this.handleGenerateTotp)}
864 <label className="form-check-label" htmlFor="user-generate-totp">
865 {i18n.t("set_up_two_factor")}
874 <a className="btn btn-secondary mb-2" href={totpUrl}>
875 {i18n.t("two_factor_link")}
878 <div className="input-group mb-3">
879 <div className="form-check">
881 className="form-check-input"
882 id="user-remove-totp"
885 this.state.saveUserSettingsForm.generate_totp_2fa == false
887 onChange={linkEvent(this, this.handleRemoveTotp)}
889 <label className="form-check-label" htmlFor="user-remove-totp">
890 {i18n.t("remove_two_factor")}
900 handlePersonSearch = debounce(async (text: string) => {
901 this.setState({ searchPersonLoading: true });
903 const searchPersonOptions: Choice[] = [];
905 if (text.length > 0) {
906 searchPersonOptions.push(...(await fetchUsers(text)).map(personToChoice));
910 searchPersonLoading: false,
915 handleCommunitySearch = debounce(async (text: string) => {
916 this.setState({ searchCommunityLoading: true });
918 const searchCommunityOptions: Choice[] = [];
920 if (text.length > 0) {
921 searchCommunityOptions.push(
922 ...(await fetchCommunities(text)).map(communityToChoice)
927 searchCommunityLoading: false,
928 searchCommunityOptions,
932 async handleBlockPerson({ value }: Choice) {
934 const res = await HttpService.client.blockPerson({
935 person_id: Number(value),
937 auth: myAuthRequired(),
939 this.personBlock(res);
943 async handleUnblockPerson({
950 const res = await HttpService.client.blockPerson({
951 person_id: recipientId,
953 auth: myAuthRequired(),
955 ctx.personBlock(res);
958 async handleBlockCommunity({ value }: Choice) {
960 const res = await HttpService.client.blockCommunity({
961 community_id: Number(value),
963 auth: myAuthRequired(),
965 this.communityBlock(res);
969 async handleUnblockCommunity(i: { ctx: Settings; communityId: number }) {
970 const auth = myAuth();
972 const res = await HttpService.client.blockCommunity({
973 community_id: i.communityId,
975 auth: myAuthRequired(),
977 i.ctx.communityBlock(res);
981 handleShowNsfwChange(i: Settings, event: any) {
983 s => ((s.saveUserSettingsForm.show_nsfw = event.target.checked), s)
987 handleShowAvatarsChange(i: Settings, event: any) {
988 const mui = UserService.Instance.myUserInfo;
990 mui.local_user_view.local_user.show_avatars = event.target.checked;
993 s => ((s.saveUserSettingsForm.show_avatars = event.target.checked), s)
997 handleBotAccount(i: Settings, event: any) {
999 s => ((s.saveUserSettingsForm.bot_account = event.target.checked), s)
1003 handleShowBotAccounts(i: Settings, event: any) {
1006 (s.saveUserSettingsForm.show_bot_accounts = event.target.checked), s
1011 handleReadPosts(i: Settings, event: any) {
1013 s => ((s.saveUserSettingsForm.show_read_posts = event.target.checked), s)
1017 handleShowNewPostNotifs(i: Settings, event: any) {
1020 (s.saveUserSettingsForm.show_new_post_notifs = event.target.checked), s
1025 handleShowScoresChange(i: Settings, event: any) {
1026 const mui = UserService.Instance.myUserInfo;
1028 mui.local_user_view.local_user.show_scores = event.target.checked;
1031 s => ((s.saveUserSettingsForm.show_scores = event.target.checked), s)
1035 handleGenerateTotp(i: Settings, event: any) {
1036 // Coerce false to undefined here, so it won't generate it.
1037 const checked: boolean | undefined = event.target.checked || undefined;
1039 toast(i18n.t("two_factor_setup_instructions"));
1041 i.setState(s => ((s.saveUserSettingsForm.generate_totp_2fa = checked), s));
1044 handleRemoveTotp(i: Settings, event: any) {
1045 // Coerce true to undefined here, so it won't generate it.
1046 const checked: boolean | undefined = !event.target.checked && undefined;
1047 i.setState(s => ((s.saveUserSettingsForm.generate_totp_2fa = checked), s));
1050 handleSendNotificationsToEmailChange(i: Settings, event: any) {
1053 (s.saveUserSettingsForm.send_notifications_to_email =
1054 event.target.checked),
1060 handleThemeChange(i: Settings, event: any) {
1061 i.setState(s => ((s.saveUserSettingsForm.theme = event.target.value), s));
1062 setTheme(event.target.value, true);
1065 handleInterfaceLangChange(i: Settings, event: any) {
1066 const newLang = event.target.value ?? "browser";
1067 i18n.changeLanguage(newLang === "browser" ? navigator.languages : newLang);
1070 s => ((s.saveUserSettingsForm.interface_language = event.target.value), s)
1074 handleDiscussionLanguageChange(val: number[]) {
1076 s => ((s.saveUserSettingsForm.discussion_languages = val), s)
1080 handleSortTypeChange(val: SortType) {
1081 this.setState(s => ((s.saveUserSettingsForm.default_sort_type = val), s));
1084 handleListingTypeChange(val: ListingType) {
1086 s => ((s.saveUserSettingsForm.default_listing_type = val), s)
1090 handleEmailChange(i: Settings, event: any) {
1091 i.setState(s => ((s.saveUserSettingsForm.email = event.target.value), s));
1094 handleBioChange(val: string) {
1095 this.setState(s => ((s.saveUserSettingsForm.bio = val), s));
1098 handleAvatarUpload(url: string) {
1099 this.setState(s => ((s.saveUserSettingsForm.avatar = url), s));
1102 handleAvatarRemove() {
1103 this.setState(s => ((s.saveUserSettingsForm.avatar = ""), s));
1106 handleBannerUpload(url: string) {
1107 this.setState(s => ((s.saveUserSettingsForm.banner = url), s));
1110 handleBannerRemove() {
1111 this.setState(s => ((s.saveUserSettingsForm.banner = ""), s));
1114 handleDisplayNameChange(i: Settings, event: any) {
1116 s => ((s.saveUserSettingsForm.display_name = event.target.value), s)
1120 handleMatrixUserIdChange(i: Settings, event: any) {
1122 s => ((s.saveUserSettingsForm.matrix_user_id = event.target.value), s)
1126 handleNewPasswordChange(i: Settings, event: any) {
1127 const newPass: string | undefined =
1128 event.target.value == "" ? undefined : event.target.value;
1129 i.setState(s => ((s.changePasswordForm.new_password = newPass), s));
1132 handleNewPasswordVerifyChange(i: Settings, event: any) {
1133 const newPassVerify: string | undefined =
1134 event.target.value == "" ? undefined : event.target.value;
1136 s => ((s.changePasswordForm.new_password_verify = newPassVerify), s)
1140 handleOldPasswordChange(i: Settings, event: any) {
1141 const oldPass: string | undefined =
1142 event.target.value == "" ? undefined : event.target.value;
1143 i.setState(s => ((s.changePasswordForm.old_password = oldPass), s));
1146 async handleSaveSettingsSubmit(i: Settings, event: any) {
1147 event.preventDefault();
1148 i.setState({ saveRes: { state: "loading" } });
1150 const saveRes = await HttpService.client.saveUserSettings({
1151 ...i.state.saveUserSettingsForm,
1152 auth: myAuthRequired(),
1154 if (saveRes.state === "success") {
1155 UserService.Instance.login(saveRes.data);
1157 toast(i18n.t("saved"));
1158 window.scrollTo(0, 0);
1161 i.setState({ saveRes });
1164 async handleChangePasswordSubmit(i: Settings, event: any) {
1165 event.preventDefault();
1166 const { new_password, new_password_verify, old_password } =
1167 i.state.changePasswordForm;
1169 if (new_password && old_password && new_password_verify) {
1170 i.setState({ changePasswordRes: { state: "loading" } });
1171 const changePasswordRes = await HttpService.client.changePassword({
1173 new_password_verify,
1175 auth: myAuthRequired(),
1177 if (changePasswordRes.state === "success") {
1178 UserService.Instance.login(changePasswordRes.data);
1179 window.scrollTo(0, 0);
1180 toast(i18n.t("password_changed"));
1183 i.setState({ changePasswordRes });
1187 handleDeleteAccountShowConfirmToggle(i: Settings) {
1188 i.setState({ deleteAccountShowConfirm: !i.state.deleteAccountShowConfirm });
1191 handleDeleteAccountPasswordChange(i: Settings, event: any) {
1192 i.setState(s => ((s.deleteAccountForm.password = event.target.value), s));
1195 async handleDeleteAccount(i: Settings) {
1196 const password = i.state.deleteAccountForm.password;
1198 i.setState({ deleteAccountRes: { state: "loading" } });
1199 const deleteAccountRes = await HttpService.client.deleteAccount({
1201 auth: myAuthRequired(),
1203 if (deleteAccountRes.state === "success") {
1204 UserService.Instance.logout();
1205 this.context.router.history.replace("/");
1208 i.setState({ deleteAccountRes });
1212 handleSwitchTab(i: { ctx: Settings; tab: string }) {
1213 i.ctx.setState({ currentTab: i.tab });
1216 personBlock(res: RequestState<BlockPersonResponse>) {
1217 if (res.state === "success") {
1218 updatePersonBlock(res.data);
1219 const mui = UserService.Instance.myUserInfo;
1221 this.setState({ personBlocks: mui.person_blocks });
1226 communityBlock(res: RequestState<BlockCommunityResponse>) {
1227 if (res.state === "success") {
1228 updateCommunityBlock(res.data);
1229 const mui = UserService.Instance.myUserInfo;
1231 this.setState({ communityBlocks: mui.community_blocks });