1 import { debounce } from "@utils/helpers";
2 import classNames from "classnames";
3 import { NoOptionI18nKeys } from "i18next";
4 import { Component, linkEvent } from "inferno";
6 BlockCommunityResponse,
15 } from "lemmy-js-client";
16 import { i18n, languages } from "../../i18next";
17 import { UserService } from "../../services";
18 import { HttpService, RequestState } from "../../services/HttpService";
21 capitalizeFirstLetter,
40 import { HtmlTags } from "../common/html-tags";
41 import { Icon, Spinner } from "../common/icon";
42 import { ImageUploadForm } from "../common/image-upload-form";
43 import { LanguageSelect } from "../common/language-select";
44 import { ListingTypeSelect } from "../common/listing-type-select";
45 import { MarkdownTextArea } from "../common/markdown-textarea";
46 import { SearchableSelect } from "../common/searchable-select";
47 import { SortSelect } from "../common/sort-select";
48 import Tabs from "../common/tabs";
49 import { CommunityLink } from "../community/community-link";
50 import { PersonListing } from "./person-listing";
52 interface SettingsState {
53 saveRes: RequestState<LoginResponse>;
54 changePasswordRes: RequestState<LoginResponse>;
55 deleteAccountRes: RequestState<DeleteAccountResponse>;
56 // TODO redo these forms
57 saveUserSettingsForm: {
60 default_sort_type?: SortType;
61 default_listing_type?: ListingType;
62 interface_language?: string;
65 display_name?: string;
68 matrix_user_id?: string;
69 show_avatars?: boolean;
70 show_scores?: boolean;
71 send_notifications_to_email?: boolean;
72 bot_account?: boolean;
73 show_bot_accounts?: boolean;
74 show_read_posts?: boolean;
75 show_new_post_notifs?: boolean;
76 discussion_languages?: number[];
77 generate_totp_2fa?: boolean;
80 new_password?: string;
81 new_password_verify?: string;
82 old_password?: string;
87 personBlocks: PersonBlockView[];
88 communityBlocks: CommunityBlockView[];
91 deleteAccountShowConfirm: boolean;
92 siteRes: GetSiteResponse;
93 searchCommunityLoading: boolean;
94 searchCommunityOptions: Choice[];
95 searchPersonLoading: boolean;
96 searchPersonOptions: Choice[];
99 type FilterType = "user" | "community";
108 filterType: FilterType;
110 onSearch: (text: string) => void;
111 onChange: (choice: Choice) => void;
114 <div className="mb-3 row">
116 className="col-md-4 col-form-label"
117 htmlFor={`block-${filterType}-filter`}
119 {i18n.t(`block_${filterType}` as NoOptionI18nKeys)}
121 <div className="col-md-8">
123 id={`block-${filterType}-filter`}
125 { label: emDash, value: "0", disabled: true } as Choice,
135 export class Settings extends Component<any, SettingsState> {
136 private isoData = setIsoData(this.context);
137 state: SettingsState = {
138 saveRes: { state: "empty" },
139 deleteAccountRes: { state: "empty" },
140 changePasswordRes: { state: "empty" },
141 saveUserSettingsForm: {},
142 changePasswordForm: {},
143 deleteAccountShowConfirm: false,
144 deleteAccountForm: {},
147 currentTab: "settings",
148 siteRes: this.isoData.site_res,
150 searchCommunityLoading: false,
151 searchCommunityOptions: [],
152 searchPersonLoading: false,
153 searchPersonOptions: [],
156 constructor(props: any, context: any) {
157 super(props, context);
159 this.handleSortTypeChange = this.handleSortTypeChange.bind(this);
160 this.handleListingTypeChange = this.handleListingTypeChange.bind(this);
161 this.handleBioChange = this.handleBioChange.bind(this);
162 this.handleDiscussionLanguageChange =
163 this.handleDiscussionLanguageChange.bind(this);
165 this.handleAvatarUpload = this.handleAvatarUpload.bind(this);
166 this.handleAvatarRemove = this.handleAvatarRemove.bind(this);
168 this.handleBannerUpload = this.handleBannerUpload.bind(this);
169 this.handleBannerRemove = this.handleBannerRemove.bind(this);
170 this.userSettings = this.userSettings.bind(this);
171 this.blockCards = this.blockCards.bind(this);
173 this.handleBlockPerson = this.handleBlockPerson.bind(this);
174 this.handleBlockCommunity = this.handleBlockCommunity.bind(this);
176 const mui = UserService.Instance.myUserInfo;
183 default_listing_type,
189 show_new_post_notifs,
190 send_notifications_to_email,
201 } = mui.local_user_view;
205 personBlocks: mui.person_blocks,
206 communityBlocks: mui.community_blocks,
207 saveUserSettingsForm: {
208 ...this.state.saveUserSettingsForm,
210 theme: theme ?? "browser",
212 default_listing_type,
214 discussion_languages: mui.discussion_languages,
223 show_new_post_notifs,
226 send_notifications_to_email,
233 async componentDidMount() {
235 this.setState({ themeList: await fetchThemeList() });
238 get documentTitle(): string {
239 return i18n.t("settings");
244 <div className="person-settings container-lg">
246 title={this.documentTitle}
247 path={this.context.router.route.match.url}
248 description={this.documentTitle}
249 image={this.state.saveUserSettingsForm.avatar}
255 label: i18n.t("settings"),
256 getNode: this.userSettings,
260 label: i18n.t("blocks"),
261 getNode: this.blockCards,
269 userSettings(isSelected) {
272 className={classNames("tab-pane show", {
276 id="settings-tab-pane"
278 <div className="row">
279 <div className="col-12 col-md-6">
280 <div className="card border-secondary mb-3">
281 <div className="card-body">{this.saveUserSettingsHtmlForm()}</div>
284 <div className="col-12 col-md-6">
285 <div className="card border-secondary mb-3">
286 <div className="card-body">{this.changePasswordHtmlForm()}</div>
294 blockCards(isSelected) {
297 className={classNames("tab-pane", {
303 <div className="row">
304 <div className="col-12 col-md-6">
305 <div className="card border-secondary mb-3">
306 <div className="card-body">{this.blockUserCard()}</div>
309 <div className="col-12 col-md-6">
310 <div className="card border-secondary mb-3">
311 <div className="card-body">{this.blockCommunityCard()}</div>
319 changePasswordHtmlForm() {
322 <h5>{i18n.t("change_password")}</h5>
323 <form onSubmit={linkEvent(this, this.handleChangePasswordSubmit)}>
324 <div className="mb-3 row">
325 <label className="col-sm-5 col-form-label" htmlFor="user-password">
326 {i18n.t("new_password")}
328 <div className="col-sm-7">
332 className="form-control"
333 value={this.state.changePasswordForm.new_password}
334 autoComplete="new-password"
336 onInput={linkEvent(this, this.handleNewPasswordChange)}
340 <div className="mb-3 row">
342 className="col-sm-5 col-form-label"
343 htmlFor="user-verify-password"
345 {i18n.t("verify_password")}
347 <div className="col-sm-7">
350 id="user-verify-password"
351 className="form-control"
352 value={this.state.changePasswordForm.new_password_verify}
353 autoComplete="new-password"
355 onInput={linkEvent(this, this.handleNewPasswordVerifyChange)}
359 <div className="mb-3 row">
361 className="col-sm-5 col-form-label"
362 htmlFor="user-old-password"
364 {i18n.t("old_password")}
366 <div className="col-sm-7">
369 id="user-old-password"
370 className="form-control"
371 value={this.state.changePasswordForm.old_password}
372 autoComplete="new-password"
374 onInput={linkEvent(this, this.handleOldPasswordChange)}
378 <div className="input-group mb-3">
381 className="btn d-block btn-secondary me-4 w-100"
383 {this.state.changePasswordRes.state === "loading" ? (
386 capitalizeFirstLetter(i18n.t("save"))
396 const { searchPersonLoading, searchPersonOptions } = this.state;
402 loading={searchPersonLoading}
403 onChange={this.handleBlockPerson}
404 onSearch={this.handlePersonSearch}
405 options={searchPersonOptions}
407 {this.blockedUsersList()}
415 <h5>{i18n.t("blocked_users")}</h5>
416 <ul className="list-unstyled mb-0">
417 {this.state.personBlocks.map(pb => (
418 <li key={pb.target.id}>
420 <PersonListing person={pb.target} />
422 className="btn btn-sm"
424 { ctx: this, recipientId: pb.target.id },
425 this.handleUnblockPerson
427 data-tippy-content={i18n.t("unblock_user")}
429 <Icon icon="x" classes="icon-inline" />
439 blockCommunityCard() {
440 const { searchCommunityLoading, searchCommunityOptions } = this.state;
445 filterType="community"
446 loading={searchCommunityLoading}
447 onChange={this.handleBlockCommunity}
448 onSearch={this.handleCommunitySearch}
449 options={searchCommunityOptions}
451 {this.blockedCommunitiesList()}
456 blockedCommunitiesList() {
459 <h5>{i18n.t("blocked_communities")}</h5>
460 <ul className="list-unstyled mb-0">
461 {this.state.communityBlocks.map(cb => (
462 <li key={cb.community.id}>
464 <CommunityLink community={cb.community} />
466 className="btn btn-sm"
468 { ctx: this, communityId: cb.community.id },
469 this.handleUnblockCommunity
471 data-tippy-content={i18n.t("unblock_community")}
473 <Icon icon="x" classes="icon-inline" />
483 saveUserSettingsHtmlForm() {
484 const selectedLangs = this.state.saveUserSettingsForm.discussion_languages;
488 <h5>{i18n.t("settings")}</h5>
489 <form onSubmit={linkEvent(this, this.handleSaveSettingsSubmit)}>
490 <div className="mb-3 row">
491 <label className="col-sm-3 col-form-label" htmlFor="display-name">
492 {i18n.t("display_name")}
494 <div className="col-sm-9">
498 className="form-control"
499 placeholder={i18n.t("optional")}
500 value={this.state.saveUserSettingsForm.display_name}
501 onInput={linkEvent(this, this.handleDisplayNameChange)}
502 pattern="^(?!@)(.+)$"
507 <div className="mb-3 row">
508 <label className="col-sm-3 col-form-label" htmlFor="user-bio">
511 <div className="col-sm-9">
513 initialContent={this.state.saveUserSettingsForm.bio}
514 onContentChange={this.handleBioChange}
516 hideNavigationWarnings
517 allLanguages={this.state.siteRes.all_languages}
518 siteLanguages={this.state.siteRes.discussion_languages}
522 <div className="mb-3 row">
523 <label className="col-sm-3 col-form-label" htmlFor="user-email">
526 <div className="col-sm-9">
530 className="form-control"
531 placeholder={i18n.t("optional")}
532 value={this.state.saveUserSettingsForm.email}
533 onInput={linkEvent(this, this.handleEmailChange)}
538 <div className="mb-3 row">
539 <label className="col-sm-3 col-form-label" htmlFor="matrix-user-id">
540 <a href={elementUrl} rel={relTags}>
541 {i18n.t("matrix_user_id")}
544 <div className="col-sm-9">
548 className="form-control"
549 placeholder="@user:example.com"
550 value={this.state.saveUserSettingsForm.matrix_user_id}
551 onInput={linkEvent(this, this.handleMatrixUserIdChange)}
552 pattern="^@[A-Za-z0-9._=-]+:[A-Za-z0-9.-]+\.[A-Za-z]{2,}$"
556 <div className="mb-3 row">
557 <label className="col-sm-3 col-form-label">
560 <div className="col-sm-9">
562 uploadTitle={i18n.t("upload_avatar")}
563 imageSrc={this.state.saveUserSettingsForm.avatar}
564 onUpload={this.handleAvatarUpload}
565 onRemove={this.handleAvatarRemove}
570 <div className="mb-3 row">
571 <label className="col-sm-3 col-form-label">
574 <div className="col-sm-9">
576 uploadTitle={i18n.t("upload_banner")}
577 imageSrc={this.state.saveUserSettingsForm.banner}
578 onUpload={this.handleBannerUpload}
579 onRemove={this.handleBannerRemove}
583 <div className="mb-3 row">
584 <label className="col-sm-3 form-label" htmlFor="user-language">
585 {i18n.t("interface_language")}
587 <div className="col-sm-9">
590 value={this.state.saveUserSettingsForm.interface_language}
591 onChange={linkEvent(this, this.handleInterfaceLangChange)}
592 className="form-select d-inline-block w-auto"
594 <option disabled aria-hidden="true">
595 {i18n.t("interface_language")}
597 <option value="browser">{i18n.t("browser_default")}</option>
598 <option disabled aria-hidden="true">
602 .sort((a, b) => a.code.localeCompare(b.code))
604 <option key={lang.code} value={lang.code}>
612 allLanguages={this.state.siteRes.all_languages}
613 siteLanguages={this.state.siteRes.discussion_languages}
614 selectedLanguageIds={selectedLangs}
616 showLanguageWarning={true}
618 onChange={this.handleDiscussionLanguageChange}
620 <div className="mb-3 row">
621 <label className="col-sm-3 col-form-label" htmlFor="user-theme">
624 <div className="col-sm-9">
627 value={this.state.saveUserSettingsForm.theme}
628 onChange={linkEvent(this, this.handleThemeChange)}
629 className="form-select d-inline-block w-auto"
631 <option disabled aria-hidden="true">
634 <option value="browser">{i18n.t("browser_default")}</option>
635 {this.state.themeList.map(theme => (
636 <option key={theme} value={theme}>
643 <form className="mb-3 row">
644 <label className="col-sm-3 col-form-label">{i18n.t("type")}</label>
645 <div className="col-sm-9">
648 this.state.saveUserSettingsForm.default_listing_type ??
651 showLocal={showLocal(this.isoData)}
653 onChange={this.handleListingTypeChange}
657 <form className="mb-3 row">
658 <label className="col-sm-3 col-form-label">
659 {i18n.t("sort_type")}
661 <div className="col-sm-9">
664 this.state.saveUserSettingsForm.default_sort_type ?? "Active"
666 onChange={this.handleSortTypeChange}
670 <div className="input-group mb-3">
671 <div className="form-check">
673 className="form-check-input"
676 checked={this.state.saveUserSettingsForm.show_nsfw}
677 onChange={linkEvent(this, this.handleShowNsfwChange)}
679 <label className="form-check-label" htmlFor="user-show-nsfw">
680 {i18n.t("show_nsfw")}
684 <div className="input-group mb-3">
685 <div className="form-check">
687 className="form-check-input"
688 id="user-show-scores"
690 checked={this.state.saveUserSettingsForm.show_scores}
691 onChange={linkEvent(this, this.handleShowScoresChange)}
693 <label className="form-check-label" htmlFor="user-show-scores">
694 {i18n.t("show_scores")}
698 <div className="input-group mb-3">
699 <div className="form-check">
701 className="form-check-input"
702 id="user-show-avatars"
704 checked={this.state.saveUserSettingsForm.show_avatars}
705 onChange={linkEvent(this, this.handleShowAvatarsChange)}
707 <label className="form-check-label" htmlFor="user-show-avatars">
708 {i18n.t("show_avatars")}
712 <div className="input-group mb-3">
713 <div className="form-check">
715 className="form-check-input"
716 id="user-bot-account"
718 checked={this.state.saveUserSettingsForm.bot_account}
719 onChange={linkEvent(this, this.handleBotAccount)}
721 <label className="form-check-label" htmlFor="user-bot-account">
722 {i18n.t("bot_account")}
726 <div className="input-group mb-3">
727 <div className="form-check">
729 className="form-check-input"
730 id="user-show-bot-accounts"
732 checked={this.state.saveUserSettingsForm.show_bot_accounts}
733 onChange={linkEvent(this, this.handleShowBotAccounts)}
736 className="form-check-label"
737 htmlFor="user-show-bot-accounts"
739 {i18n.t("show_bot_accounts")}
743 <div className="input-group mb-3">
744 <div className="form-check">
746 className="form-check-input"
747 id="user-show-read-posts"
749 checked={this.state.saveUserSettingsForm.show_read_posts}
750 onChange={linkEvent(this, this.handleReadPosts)}
753 className="form-check-label"
754 htmlFor="user-show-read-posts"
756 {i18n.t("show_read_posts")}
760 <div className="input-group mb-3">
761 <div className="form-check">
763 className="form-check-input"
764 id="user-show-new-post-notifs"
766 checked={this.state.saveUserSettingsForm.show_new_post_notifs}
767 onChange={linkEvent(this, this.handleShowNewPostNotifs)}
770 className="form-check-label"
771 htmlFor="user-show-new-post-notifs"
773 {i18n.t("show_new_post_notifs")}
777 <div className="input-group mb-3">
778 <div className="form-check">
780 className="form-check-input"
781 id="user-send-notifications-to-email"
783 disabled={!this.state.saveUserSettingsForm.email}
785 this.state.saveUserSettingsForm.send_notifications_to_email
789 this.handleSendNotificationsToEmailChange
793 className="form-check-label"
794 htmlFor="user-send-notifications-to-email"
796 {i18n.t("send_notifications_to_email")}
801 <div className="input-group mb-3">
802 <button type="submit" className="btn d-block btn-secondary me-4">
803 {this.state.saveRes.state === "loading" ? (
806 capitalizeFirstLetter(i18n.t("save"))
811 <div className="input-group mb-3">
813 className="btn d-block btn-danger"
816 this.handleDeleteAccountShowConfirmToggle
819 {i18n.t("delete_account")}
821 {this.state.deleteAccountShowConfirm && (
823 <div className="my-2 alert alert-danger" role="alert">
824 {i18n.t("delete_account_confirm")}
828 value={this.state.deleteAccountForm.password}
829 autoComplete="new-password"
833 this.handleDeleteAccountPasswordChange
835 className="form-control my-2"
838 className="btn btn-danger me-4"
839 disabled={!this.state.deleteAccountForm.password}
840 onClick={linkEvent(this, this.handleDeleteAccount)}
842 {this.state.deleteAccountRes.state === "loading" ? (
845 capitalizeFirstLetter(i18n.t("delete"))
849 className="btn btn-secondary"
852 this.handleDeleteAccountShowConfirmToggle
867 UserService.Instance.myUserInfo?.local_user_view.local_user.totp_2fa_url;
872 <div className="input-group mb-3">
873 <div className="form-check">
875 className="form-check-input"
876 id="user-generate-totp"
878 checked={this.state.saveUserSettingsForm.generate_totp_2fa}
879 onChange={linkEvent(this, this.handleGenerateTotp)}
881 <label className="form-check-label" htmlFor="user-generate-totp">
882 {i18n.t("set_up_two_factor")}
891 <a className="btn btn-secondary mb-2" href={totpUrl}>
892 {i18n.t("two_factor_link")}
895 <div className="input-group mb-3">
896 <div className="form-check">
898 className="form-check-input"
899 id="user-remove-totp"
902 this.state.saveUserSettingsForm.generate_totp_2fa == false
904 onChange={linkEvent(this, this.handleRemoveTotp)}
906 <label className="form-check-label" htmlFor="user-remove-totp">
907 {i18n.t("remove_two_factor")}
917 handlePersonSearch = debounce(async (text: string) => {
918 this.setState({ searchPersonLoading: true });
920 const searchPersonOptions: Choice[] = [];
922 if (text.length > 0) {
923 searchPersonOptions.push(...(await fetchUsers(text)).map(personToChoice));
927 searchPersonLoading: false,
932 handleCommunitySearch = debounce(async (text: string) => {
933 this.setState({ searchCommunityLoading: true });
935 const searchCommunityOptions: Choice[] = [];
937 if (text.length > 0) {
938 searchCommunityOptions.push(
939 ...(await fetchCommunities(text)).map(communityToChoice)
944 searchCommunityLoading: false,
945 searchCommunityOptions,
949 async handleBlockPerson({ value }: Choice) {
951 const res = await HttpService.client.blockPerson({
952 person_id: Number(value),
954 auth: myAuthRequired(),
956 this.personBlock(res);
960 async handleUnblockPerson({
967 const res = await HttpService.client.blockPerson({
968 person_id: recipientId,
970 auth: myAuthRequired(),
972 ctx.personBlock(res);
975 async handleBlockCommunity({ value }: Choice) {
977 const res = await HttpService.client.blockCommunity({
978 community_id: Number(value),
980 auth: myAuthRequired(),
982 this.communityBlock(res);
986 async handleUnblockCommunity(i: { ctx: Settings; communityId: number }) {
987 const auth = myAuth();
989 const res = await HttpService.client.blockCommunity({
990 community_id: i.communityId,
992 auth: myAuthRequired(),
994 i.ctx.communityBlock(res);
998 handleShowNsfwChange(i: Settings, event: any) {
1000 s => ((s.saveUserSettingsForm.show_nsfw = event.target.checked), s)
1004 handleShowAvatarsChange(i: Settings, event: any) {
1005 const mui = UserService.Instance.myUserInfo;
1007 mui.local_user_view.local_user.show_avatars = event.target.checked;
1010 s => ((s.saveUserSettingsForm.show_avatars = event.target.checked), s)
1014 handleBotAccount(i: Settings, event: any) {
1016 s => ((s.saveUserSettingsForm.bot_account = event.target.checked), s)
1020 handleShowBotAccounts(i: Settings, event: any) {
1023 (s.saveUserSettingsForm.show_bot_accounts = event.target.checked), s
1028 handleReadPosts(i: Settings, event: any) {
1030 s => ((s.saveUserSettingsForm.show_read_posts = event.target.checked), s)
1034 handleShowNewPostNotifs(i: Settings, event: any) {
1037 (s.saveUserSettingsForm.show_new_post_notifs = event.target.checked), s
1042 handleShowScoresChange(i: Settings, event: any) {
1043 const mui = UserService.Instance.myUserInfo;
1045 mui.local_user_view.local_user.show_scores = event.target.checked;
1048 s => ((s.saveUserSettingsForm.show_scores = event.target.checked), s)
1052 handleGenerateTotp(i: Settings, event: any) {
1053 // Coerce false to undefined here, so it won't generate it.
1054 const checked: boolean | undefined = event.target.checked || undefined;
1056 toast(i18n.t("two_factor_setup_instructions"));
1058 i.setState(s => ((s.saveUserSettingsForm.generate_totp_2fa = checked), s));
1061 handleRemoveTotp(i: Settings, event: any) {
1062 // Coerce true to undefined here, so it won't generate it.
1063 const checked: boolean | undefined = !event.target.checked && undefined;
1064 i.setState(s => ((s.saveUserSettingsForm.generate_totp_2fa = checked), s));
1067 handleSendNotificationsToEmailChange(i: Settings, event: any) {
1070 (s.saveUserSettingsForm.send_notifications_to_email =
1071 event.target.checked),
1077 handleThemeChange(i: Settings, event: any) {
1078 i.setState(s => ((s.saveUserSettingsForm.theme = event.target.value), s));
1079 setTheme(event.target.value, true);
1082 handleInterfaceLangChange(i: Settings, event: any) {
1083 const newLang = event.target.value ?? "browser";
1084 i18n.changeLanguage(newLang === "browser" ? navigator.languages : newLang);
1087 s => ((s.saveUserSettingsForm.interface_language = event.target.value), s)
1091 handleDiscussionLanguageChange(val: number[]) {
1093 s => ((s.saveUserSettingsForm.discussion_languages = val), s)
1097 handleSortTypeChange(val: SortType) {
1098 this.setState(s => ((s.saveUserSettingsForm.default_sort_type = val), s));
1101 handleListingTypeChange(val: ListingType) {
1103 s => ((s.saveUserSettingsForm.default_listing_type = val), s)
1107 handleEmailChange(i: Settings, event: any) {
1108 i.setState(s => ((s.saveUserSettingsForm.email = event.target.value), s));
1111 handleBioChange(val: string) {
1112 this.setState(s => ((s.saveUserSettingsForm.bio = val), s));
1115 handleAvatarUpload(url: string) {
1116 this.setState(s => ((s.saveUserSettingsForm.avatar = url), s));
1119 handleAvatarRemove() {
1120 this.setState(s => ((s.saveUserSettingsForm.avatar = ""), s));
1123 handleBannerUpload(url: string) {
1124 this.setState(s => ((s.saveUserSettingsForm.banner = url), s));
1127 handleBannerRemove() {
1128 this.setState(s => ((s.saveUserSettingsForm.banner = ""), s));
1131 handleDisplayNameChange(i: Settings, event: any) {
1133 s => ((s.saveUserSettingsForm.display_name = event.target.value), s)
1137 handleMatrixUserIdChange(i: Settings, event: any) {
1139 s => ((s.saveUserSettingsForm.matrix_user_id = event.target.value), s)
1143 handleNewPasswordChange(i: Settings, event: any) {
1144 const newPass: string | undefined =
1145 event.target.value == "" ? undefined : event.target.value;
1146 i.setState(s => ((s.changePasswordForm.new_password = newPass), s));
1149 handleNewPasswordVerifyChange(i: Settings, event: any) {
1150 const newPassVerify: string | undefined =
1151 event.target.value == "" ? undefined : event.target.value;
1153 s => ((s.changePasswordForm.new_password_verify = newPassVerify), s)
1157 handleOldPasswordChange(i: Settings, event: any) {
1158 const oldPass: string | undefined =
1159 event.target.value == "" ? undefined : event.target.value;
1160 i.setState(s => ((s.changePasswordForm.old_password = oldPass), s));
1163 async handleSaveSettingsSubmit(i: Settings, event: any) {
1164 event.preventDefault();
1165 i.setState({ saveRes: { state: "loading" } });
1167 const saveRes = await HttpService.client.saveUserSettings({
1168 ...i.state.saveUserSettingsForm,
1169 auth: myAuthRequired(),
1171 if (saveRes.state === "success") {
1172 UserService.Instance.login(saveRes.data);
1174 toast(i18n.t("saved"));
1175 window.scrollTo(0, 0);
1178 i.setState({ saveRes });
1181 async handleChangePasswordSubmit(i: Settings, event: any) {
1182 event.preventDefault();
1183 const { new_password, new_password_verify, old_password } =
1184 i.state.changePasswordForm;
1186 if (new_password && old_password && new_password_verify) {
1187 i.setState({ changePasswordRes: { state: "loading" } });
1188 const changePasswordRes = await HttpService.client.changePassword({
1190 new_password_verify,
1192 auth: myAuthRequired(),
1194 if (changePasswordRes.state === "success") {
1195 UserService.Instance.login(changePasswordRes.data);
1196 window.scrollTo(0, 0);
1197 toast(i18n.t("password_changed"));
1200 i.setState({ changePasswordRes });
1204 handleDeleteAccountShowConfirmToggle(i: Settings) {
1205 i.setState({ deleteAccountShowConfirm: !i.state.deleteAccountShowConfirm });
1208 handleDeleteAccountPasswordChange(i: Settings, event: any) {
1209 i.setState(s => ((s.deleteAccountForm.password = event.target.value), s));
1212 async handleDeleteAccount(i: Settings) {
1213 const password = i.state.deleteAccountForm.password;
1215 i.setState({ deleteAccountRes: { state: "loading" } });
1216 const deleteAccountRes = await HttpService.client.deleteAccount({
1218 auth: myAuthRequired(),
1220 if (deleteAccountRes.state === "success") {
1221 UserService.Instance.logout();
1222 this.context.router.history.replace("/");
1225 i.setState({ deleteAccountRes });
1229 handleSwitchTab(i: { ctx: Settings; tab: string }) {
1230 i.ctx.setState({ currentTab: i.tab });
1233 personBlock(res: RequestState<BlockPersonResponse>) {
1234 if (res.state === "success") {
1235 updatePersonBlock(res.data);
1236 const mui = UserService.Instance.myUserInfo;
1238 this.setState({ personBlocks: mui.person_blocks });
1243 communityBlock(res: RequestState<BlockCommunityResponse>) {
1244 if (res.state === "success") {
1245 updateCommunityBlock(res.data);
1246 const mui = UserService.Instance.myUserInfo;
1248 this.setState({ communityBlocks: mui.community_blocks });