1 import { NoOptionI18nKeys } from "i18next";
2 import { Component, linkEvent } from "inferno";
5 BlockCommunityResponse,
20 } from "lemmy-js-client";
21 import { Subscription } from "rxjs";
22 import { i18n, languages } from "../../i18next";
23 import { UserService, WebSocketService } from "../../services";
26 capitalizeFirstLetter,
49 import { HtmlTags } from "../common/html-tags";
50 import { Icon, Spinner } from "../common/icon";
51 import { ImageUploadForm } from "../common/image-upload-form";
52 import { LanguageSelect } from "../common/language-select";
53 import { ListingTypeSelect } from "../common/listing-type-select";
54 import { MarkdownTextArea } from "../common/markdown-textarea";
55 import { SearchableSelect } from "../common/searchable-select";
56 import { SortSelect } from "../common/sort-select";
57 import Tabs from "../common/tabs";
58 import { CommunityLink } from "../community/community-link";
59 import { PersonListing } from "./person-listing";
61 interface SettingsState {
62 // TODO redo these forms
63 saveUserSettingsForm: {
66 default_sort_type?: SortType;
67 default_listing_type?: ListingType;
68 interface_language?: string;
71 display_name?: string;
74 matrix_user_id?: string;
75 show_avatars?: boolean;
76 show_scores?: boolean;
77 send_notifications_to_email?: boolean;
78 bot_account?: boolean;
79 show_bot_accounts?: boolean;
80 show_read_posts?: boolean;
81 show_new_post_notifs?: boolean;
82 discussion_languages?: number[];
83 generate_totp_2fa?: boolean;
86 new_password?: string;
87 new_password_verify?: string;
88 old_password?: string;
93 personBlocks: PersonBlockView[];
94 communityBlocks: CommunityBlockView[];
97 saveUserSettingsLoading: boolean;
98 changePasswordLoading: boolean;
99 deleteAccountLoading: boolean;
100 deleteAccountShowConfirm: boolean;
101 siteRes: GetSiteResponse;
102 searchCommunityLoading: boolean;
103 searchCommunityOptions: Choice[];
104 searchPersonLoading: boolean;
105 searchPersonOptions: Choice[];
108 type FilterType = "user" | "community";
117 filterType: FilterType;
119 onSearch: (text: string) => void;
120 onChange: (choice: Choice) => void;
123 <div className="form-group row">
125 className="col-md-4 col-form-label"
126 htmlFor={`block-${filterType}-filter`}
128 {i18n.t(`block_${filterType}` as NoOptionI18nKeys)}
130 <div className="col-md-8">
132 id={`block-${filterType}-filter`}
134 { label: emDash, value: "0", disabled: true } as Choice,
144 export class Settings extends Component<any, SettingsState> {
145 private isoData = setIsoData(this.context);
146 private subscription?: Subscription;
147 state: SettingsState = {
148 saveUserSettingsForm: {},
149 changePasswordForm: {},
150 saveUserSettingsLoading: false,
151 changePasswordLoading: false,
152 deleteAccountLoading: false,
153 deleteAccountShowConfirm: false,
154 deleteAccountForm: {},
157 currentTab: "settings",
158 siteRes: this.isoData.site_res,
160 searchCommunityLoading: false,
161 searchCommunityOptions: [],
162 searchPersonLoading: false,
163 searchPersonOptions: [],
166 constructor(props: any, context: any) {
167 super(props, context);
169 this.handleSortTypeChange = this.handleSortTypeChange.bind(this);
170 this.handleListingTypeChange = this.handleListingTypeChange.bind(this);
171 this.handleBioChange = this.handleBioChange.bind(this);
172 this.handleDiscussionLanguageChange =
173 this.handleDiscussionLanguageChange.bind(this);
175 this.handleAvatarUpload = this.handleAvatarUpload.bind(this);
176 this.handleAvatarRemove = this.handleAvatarRemove.bind(this);
178 this.handleBannerUpload = this.handleBannerUpload.bind(this);
179 this.handleBannerRemove = this.handleBannerRemove.bind(this);
180 this.userSettings = this.userSettings.bind(this);
181 this.blockCards = this.blockCards.bind(this);
183 this.parseMessage = this.parseMessage.bind(this);
184 this.subscription = wsSubscribe(this.parseMessage);
186 const mui = UserService.Instance.myUserInfo;
193 default_listing_type,
199 show_new_post_notifs,
200 send_notifications_to_email,
211 } = mui.local_user_view;
215 personBlocks: mui.person_blocks,
216 communityBlocks: mui.community_blocks,
217 saveUserSettingsForm: {
218 ...this.state.saveUserSettingsForm,
220 theme: theme ?? "browser",
222 default_listing_type,
224 discussion_languages: mui.discussion_languages,
233 show_new_post_notifs,
236 send_notifications_to_email,
243 async componentDidMount() {
245 this.setState({ themeList: await fetchThemeList() });
248 componentWillUnmount() {
249 this.subscription?.unsubscribe();
252 get documentTitle(): string {
253 return i18n.t("settings");
258 <div className="container-lg">
260 title={this.documentTitle}
261 path={this.context.router.route.match.url}
262 description={this.documentTitle}
263 image={this.state.saveUserSettingsForm.avatar}
269 label: i18n.t("settings"),
270 getNode: this.userSettings,
274 label: i18n.t("blocks"),
275 getNode: this.blockCards,
285 <div className="row">
286 <div className="col-12 col-md-6">
287 <div className="card border-secondary mb-3">
288 <div className="card-body">{this.saveUserSettingsHtmlForm()}</div>
291 <div className="col-12 col-md-6">
292 <div className="card border-secondary mb-3">
293 <div className="card-body">{this.changePasswordHtmlForm()}</div>
302 <div className="row">
303 <div className="col-12 col-md-6">
304 <div className="card border-secondary mb-3">
305 <div className="card-body">{this.blockUserCard()}</div>
308 <div className="col-12 col-md-6">
309 <div className="card border-secondary mb-3">
310 <div className="card-body">{this.blockCommunityCard()}</div>
317 changePasswordHtmlForm() {
320 <h5>{i18n.t("change_password")}</h5>
321 <form onSubmit={linkEvent(this, this.handleChangePasswordSubmit)}>
322 <div className="form-group row">
323 <label className="col-sm-5 col-form-label" htmlFor="user-password">
324 {i18n.t("new_password")}
326 <div className="col-sm-7">
330 className="form-control"
331 value={this.state.changePasswordForm.new_password}
332 autoComplete="new-password"
334 onInput={linkEvent(this, this.handleNewPasswordChange)}
338 <div className="form-group row">
340 className="col-sm-5 col-form-label"
341 htmlFor="user-verify-password"
343 {i18n.t("verify_password")}
345 <div className="col-sm-7">
348 id="user-verify-password"
349 className="form-control"
350 value={this.state.changePasswordForm.new_password_verify}
351 autoComplete="new-password"
353 onInput={linkEvent(this, this.handleNewPasswordVerifyChange)}
357 <div className="form-group row">
359 className="col-sm-5 col-form-label"
360 htmlFor="user-old-password"
362 {i18n.t("old_password")}
364 <div className="col-sm-7">
367 id="user-old-password"
368 className="form-control"
369 value={this.state.changePasswordForm.old_password}
370 autoComplete="new-password"
372 onInput={linkEvent(this, this.handleOldPasswordChange)}
376 <div className="form-group">
377 <button type="submit" className="btn btn-block btn-secondary mr-4">
378 {this.state.changePasswordLoading ? (
381 capitalizeFirstLetter(i18n.t("save"))
391 const { searchPersonLoading, searchPersonOptions } = this.state;
397 loading={searchPersonLoading}
398 onChange={this.handleBlockPerson}
399 onSearch={this.handlePersonSearch}
400 options={searchPersonOptions}
402 {this.blockedUsersList()}
410 <h5>{i18n.t("blocked_users")}</h5>
411 <ul className="list-unstyled mb-0">
412 {this.state.personBlocks.map(pb => (
413 <li key={pb.target.id}>
415 <PersonListing person={pb.target} />
417 className="btn btn-sm"
419 { ctx: this, recipientId: pb.target.id },
420 this.handleUnblockPerson
422 data-tippy-content={i18n.t("unblock_user")}
424 <Icon icon="x" classes="icon-inline" />
434 blockCommunityCard() {
435 const { searchCommunityLoading, searchCommunityOptions } = this.state;
440 filterType="community"
441 loading={searchCommunityLoading}
442 onChange={this.handleBlockCommunity}
443 onSearch={this.handleCommunitySearch}
444 options={searchCommunityOptions}
446 {this.blockedCommunitiesList()}
451 blockedCommunitiesList() {
454 <h5>{i18n.t("blocked_communities")}</h5>
455 <ul className="list-unstyled mb-0">
456 {this.state.communityBlocks.map(cb => (
457 <li key={cb.community.id}>
459 <CommunityLink community={cb.community} />
461 className="btn btn-sm"
463 { ctx: this, communityId: cb.community.id },
464 this.handleUnblockCommunity
466 data-tippy-content={i18n.t("unblock_community")}
468 <Icon icon="x" classes="icon-inline" />
478 saveUserSettingsHtmlForm() {
479 const selectedLangs = this.state.saveUserSettingsForm.discussion_languages;
483 <h5>{i18n.t("settings")}</h5>
484 <form onSubmit={linkEvent(this, this.handleSaveSettingsSubmit)}>
485 <div className="form-group row">
486 <label className="col-sm-5 col-form-label" htmlFor="display-name">
487 {i18n.t("display_name")}
489 <div className="col-sm-7">
493 className="form-control"
494 placeholder={i18n.t("optional")}
495 value={this.state.saveUserSettingsForm.display_name}
496 onInput={linkEvent(this, this.handleDisplayNameChange)}
497 pattern="^(?!@)(.+)$"
502 <div className="form-group row">
503 <label className="col-sm-3 col-form-label" htmlFor="user-bio">
506 <div className="col-sm-9">
508 initialContent={this.state.saveUserSettingsForm.bio}
509 onContentChange={this.handleBioChange}
511 hideNavigationWarnings
512 allLanguages={this.state.siteRes.all_languages}
513 siteLanguages={this.state.siteRes.discussion_languages}
517 <div className="form-group row">
518 <label className="col-sm-3 col-form-label" htmlFor="user-email">
521 <div className="col-sm-9">
525 className="form-control"
526 placeholder={i18n.t("optional")}
527 value={this.state.saveUserSettingsForm.email}
528 onInput={linkEvent(this, this.handleEmailChange)}
533 <div className="form-group row">
534 <label className="col-sm-5 col-form-label" htmlFor="matrix-user-id">
535 <a href={elementUrl} rel={relTags}>
536 {i18n.t("matrix_user_id")}
539 <div className="col-sm-7">
543 className="form-control"
544 placeholder="@user:example.com"
545 value={this.state.saveUserSettingsForm.matrix_user_id}
546 onInput={linkEvent(this, this.handleMatrixUserIdChange)}
547 pattern="^@[A-Za-z0-9._=-]+:[A-Za-z0-9.-]+\.[A-Za-z]{2,}$"
551 <div className="form-group row">
552 <label className="col-sm-3">{i18n.t("avatar")}</label>
553 <div className="col-sm-9">
555 uploadTitle={i18n.t("upload_avatar")}
556 imageSrc={this.state.saveUserSettingsForm.avatar}
557 onUpload={this.handleAvatarUpload}
558 onRemove={this.handleAvatarRemove}
563 <div className="form-group row">
564 <label className="col-sm-3">{i18n.t("banner")}</label>
565 <div className="col-sm-9">
567 uploadTitle={i18n.t("upload_banner")}
568 imageSrc={this.state.saveUserSettingsForm.banner}
569 onUpload={this.handleBannerUpload}
570 onRemove={this.handleBannerRemove}
574 <div className="form-group row">
575 <label className="col-sm-3" htmlFor="user-language">
576 {i18n.t("interface_language")}
578 <div className="col-sm-9">
581 value={this.state.saveUserSettingsForm.interface_language}
582 onChange={linkEvent(this, this.handleInterfaceLangChange)}
583 className="custom-select w-auto"
585 <option disabled aria-hidden="true">
586 {i18n.t("interface_language")}
588 <option value="browser">{i18n.t("browser_default")}</option>
589 <option disabled aria-hidden="true">
593 .sort((a, b) => a.code.localeCompare(b.code))
595 <option key={lang.code} value={lang.code}>
603 allLanguages={this.state.siteRes.all_languages}
604 siteLanguages={this.state.siteRes.discussion_languages}
605 selectedLanguageIds={selectedLangs}
607 showLanguageWarning={true}
609 onChange={this.handleDiscussionLanguageChange}
611 <div className="form-group row">
612 <label className="col-sm-3" htmlFor="user-theme">
615 <div className="col-sm-9">
618 value={this.state.saveUserSettingsForm.theme}
619 onChange={linkEvent(this, this.handleThemeChange)}
620 className="custom-select w-auto"
622 <option disabled aria-hidden="true">
625 <option value="browser">{i18n.t("browser_default")}</option>
626 {this.state.themeList.map(theme => (
627 <option key={theme} value={theme}>
634 <form className="form-group row">
635 <label className="col-sm-3">{i18n.t("type")}</label>
636 <div className="col-sm-9">
639 this.state.saveUserSettingsForm.default_listing_type ??
642 showLocal={showLocal(this.isoData)}
644 onChange={this.handleListingTypeChange}
648 <form className="form-group row">
649 <label className="col-sm-3">{i18n.t("sort_type")}</label>
650 <div className="col-sm-9">
653 this.state.saveUserSettingsForm.default_sort_type ?? "Active"
655 onChange={this.handleSortTypeChange}
659 {enableNsfw(this.state.siteRes) && (
660 <div className="form-group">
661 <div className="form-check">
663 className="form-check-input"
666 checked={this.state.saveUserSettingsForm.show_nsfw}
667 onChange={linkEvent(this, this.handleShowNsfwChange)}
669 <label className="form-check-label" htmlFor="user-show-nsfw">
670 {i18n.t("show_nsfw")}
675 <div className="form-group">
676 <div className="form-check">
678 className="form-check-input"
679 id="user-show-scores"
681 checked={this.state.saveUserSettingsForm.show_scores}
682 onChange={linkEvent(this, this.handleShowScoresChange)}
684 <label className="form-check-label" htmlFor="user-show-scores">
685 {i18n.t("show_scores")}
689 <div className="form-group">
690 <div className="form-check">
692 className="form-check-input"
693 id="user-show-avatars"
695 checked={this.state.saveUserSettingsForm.show_avatars}
696 onChange={linkEvent(this, this.handleShowAvatarsChange)}
698 <label className="form-check-label" htmlFor="user-show-avatars">
699 {i18n.t("show_avatars")}
703 <div className="form-group">
704 <div className="form-check">
706 className="form-check-input"
707 id="user-bot-account"
709 checked={this.state.saveUserSettingsForm.bot_account}
710 onChange={linkEvent(this, this.handleBotAccount)}
712 <label className="form-check-label" htmlFor="user-bot-account">
713 {i18n.t("bot_account")}
717 <div className="form-group">
718 <div className="form-check">
720 className="form-check-input"
721 id="user-show-bot-accounts"
723 checked={this.state.saveUserSettingsForm.show_bot_accounts}
724 onChange={linkEvent(this, this.handleShowBotAccounts)}
727 className="form-check-label"
728 htmlFor="user-show-bot-accounts"
730 {i18n.t("show_bot_accounts")}
734 <div className="form-group">
735 <div className="form-check">
737 className="form-check-input"
738 id="user-show-read-posts"
740 checked={this.state.saveUserSettingsForm.show_read_posts}
741 onChange={linkEvent(this, this.handleReadPosts)}
744 className="form-check-label"
745 htmlFor="user-show-read-posts"
747 {i18n.t("show_read_posts")}
751 <div className="form-group">
752 <div className="form-check">
754 className="form-check-input"
755 id="user-show-new-post-notifs"
757 checked={this.state.saveUserSettingsForm.show_new_post_notifs}
758 onChange={linkEvent(this, this.handleShowNewPostNotifs)}
761 className="form-check-label"
762 htmlFor="user-show-new-post-notifs"
764 {i18n.t("show_new_post_notifs")}
768 <div className="form-group">
769 <div className="form-check">
771 className="form-check-input"
772 id="user-send-notifications-to-email"
774 disabled={!this.state.saveUserSettingsForm.email}
776 this.state.saveUserSettingsForm.send_notifications_to_email
780 this.handleSendNotificationsToEmailChange
784 className="form-check-label"
785 htmlFor="user-send-notifications-to-email"
787 {i18n.t("send_notifications_to_email")}
792 <div className="form-group">
793 <button type="submit" className="btn btn-block btn-secondary mr-4">
794 {this.state.saveUserSettingsLoading ? (
797 capitalizeFirstLetter(i18n.t("save"))
802 <div className="form-group">
804 className="btn btn-block btn-danger"
807 this.handleDeleteAccountShowConfirmToggle
810 {i18n.t("delete_account")}
812 {this.state.deleteAccountShowConfirm && (
814 <div className="my-2 alert alert-danger" role="alert">
815 {i18n.t("delete_account_confirm")}
819 value={this.state.deleteAccountForm.password}
820 autoComplete="new-password"
824 this.handleDeleteAccountPasswordChange
826 className="form-control my-2"
829 className="btn btn-danger mr-4"
830 disabled={!this.state.deleteAccountForm.password}
831 onClick={linkEvent(this, this.handleDeleteAccount)}
833 {this.state.deleteAccountLoading ? (
836 capitalizeFirstLetter(i18n.t("delete"))
840 className="btn btn-secondary"
843 this.handleDeleteAccountShowConfirmToggle
858 UserService.Instance.myUserInfo?.local_user_view.local_user.totp_2fa_url;
863 <div className="form-group">
864 <div className="form-check">
866 className="form-check-input"
867 id="user-generate-totp"
869 checked={this.state.saveUserSettingsForm.generate_totp_2fa}
870 onChange={linkEvent(this, this.handleGenerateTotp)}
872 <label className="form-check-label" htmlFor="user-generate-totp">
873 {i18n.t("set_up_two_factor")}
882 <a className="btn btn-secondary mb-2" href={totpUrl}>
883 {i18n.t("two_factor_link")}
886 <div className="form-group">
887 <div className="form-check">
889 className="form-check-input"
890 id="user-remove-totp"
893 this.state.saveUserSettingsForm.generate_totp_2fa == false
895 onChange={linkEvent(this, this.handleRemoveTotp)}
897 <label className="form-check-label" htmlFor="user-remove-totp">
898 {i18n.t("remove_two_factor")}
908 handlePersonSearch = debounce(async (text: string) => {
909 this.setState({ searchPersonLoading: true });
911 const searchPersonOptions: Choice[] = [];
913 if (text.length > 0) {
914 searchPersonOptions.push(
915 ...(await fetchUsers(text)).users.map(personToChoice)
920 searchPersonLoading: false,
925 handleCommunitySearch = debounce(async (text: string) => {
926 this.setState({ searchCommunityLoading: true });
928 const searchCommunityOptions: Choice[] = [];
930 if (text.length > 0) {
931 searchCommunityOptions.push(
932 ...(await fetchCommunities(text)).communities.map(communityToChoice)
937 searchCommunityLoading: false,
938 searchCommunityOptions,
942 handleBlockPerson({ value }: Choice) {
943 const auth = myAuth();
944 if (auth && value !== "0") {
945 const blockUserForm: BlockPerson = {
946 person_id: Number(value),
951 WebSocketService.Instance.send(wsClient.blockPerson(blockUserForm));
955 handleUnblockPerson(i: { ctx: Settings; recipientId: number }) {
956 const auth = myAuth();
958 const blockUserForm: BlockPerson = {
959 person_id: i.recipientId,
963 WebSocketService.Instance.send(wsClient.blockPerson(blockUserForm));
967 handleBlockCommunity({ value }: Choice) {
968 const auth = myAuth();
969 if (auth && value !== "0") {
970 const blockCommunityForm: BlockCommunity = {
971 community_id: Number(value),
975 WebSocketService.Instance.send(
976 wsClient.blockCommunity(blockCommunityForm)
981 handleUnblockCommunity(i: { ctx: Settings; communityId: number }) {
982 const auth = myAuth();
984 const blockCommunityForm: BlockCommunity = {
985 community_id: i.communityId,
989 WebSocketService.Instance.send(
990 wsClient.blockCommunity(blockCommunityForm)
995 handleShowNsfwChange(i: Settings, event: any) {
996 i.state.saveUserSettingsForm.show_nsfw = event.target.checked;
1000 handleShowAvatarsChange(i: Settings, event: any) {
1001 i.state.saveUserSettingsForm.show_avatars = event.target.checked;
1002 const mui = UserService.Instance.myUserInfo;
1004 mui.local_user_view.local_user.show_avatars = event.target.checked;
1006 i.setState(i.state);
1009 handleBotAccount(i: Settings, event: any) {
1010 i.state.saveUserSettingsForm.bot_account = event.target.checked;
1011 i.setState(i.state);
1014 handleShowBotAccounts(i: Settings, event: any) {
1015 i.state.saveUserSettingsForm.show_bot_accounts = event.target.checked;
1016 i.setState(i.state);
1019 handleReadPosts(i: Settings, event: any) {
1020 i.state.saveUserSettingsForm.show_read_posts = event.target.checked;
1021 i.setState(i.state);
1024 handleShowNewPostNotifs(i: Settings, event: any) {
1025 i.state.saveUserSettingsForm.show_new_post_notifs = event.target.checked;
1026 i.setState(i.state);
1029 handleShowScoresChange(i: Settings, event: any) {
1030 i.state.saveUserSettingsForm.show_scores = event.target.checked;
1031 const mui = UserService.Instance.myUserInfo;
1033 mui.local_user_view.local_user.show_scores = event.target.checked;
1035 i.setState(i.state);
1038 handleGenerateTotp(i: Settings, event: any) {
1039 // Coerce false to undefined here, so it won't generate it.
1040 const checked: boolean | undefined = event.target.checked || undefined;
1042 toast(i18n.t("two_factor_setup_instructions"));
1044 i.state.saveUserSettingsForm.generate_totp_2fa = checked;
1045 i.setState(i.state);
1048 handleRemoveTotp(i: Settings, event: any) {
1049 // Coerce true to undefined here, so it won't generate it.
1050 const checked: boolean | undefined = !event.target.checked && undefined;
1051 i.state.saveUserSettingsForm.generate_totp_2fa = checked;
1052 i.setState(i.state);
1055 handleSendNotificationsToEmailChange(i: Settings, event: any) {
1056 i.state.saveUserSettingsForm.send_notifications_to_email =
1057 event.target.checked;
1058 i.setState(i.state);
1061 handleThemeChange(i: Settings, event: any) {
1062 i.state.saveUserSettingsForm.theme = event.target.value;
1063 setTheme(event.target.value, true);
1064 i.setState(i.state);
1067 handleInterfaceLangChange(i: Settings, event: any) {
1068 i.state.saveUserSettingsForm.interface_language = event.target.value;
1069 i18n.changeLanguage(
1070 getLanguages(i.state.saveUserSettingsForm.interface_language).at(0)
1072 i.setState(i.state);
1075 handleDiscussionLanguageChange(val: number[]) {
1077 s => ((s.saveUserSettingsForm.discussion_languages = val), s)
1081 handleSortTypeChange(val: SortType) {
1082 this.setState(s => ((s.saveUserSettingsForm.default_sort_type = val), s));
1085 handleListingTypeChange(val: ListingType) {
1087 s => ((s.saveUserSettingsForm.default_listing_type = val), s)
1091 handleEmailChange(i: Settings, event: any) {
1092 i.state.saveUserSettingsForm.email = event.target.value;
1093 i.setState(i.state);
1096 handleBioChange(val: string) {
1097 this.setState(s => ((s.saveUserSettingsForm.bio = val), s));
1100 handleAvatarUpload(url: string) {
1101 this.setState(s => ((s.saveUserSettingsForm.avatar = url), s));
1104 handleAvatarRemove() {
1105 this.setState(s => ((s.saveUserSettingsForm.avatar = ""), s));
1108 handleBannerUpload(url: string) {
1109 this.setState(s => ((s.saveUserSettingsForm.banner = url), s));
1112 handleBannerRemove() {
1113 this.setState(s => ((s.saveUserSettingsForm.banner = ""), s));
1116 handleDisplayNameChange(i: Settings, event: any) {
1117 i.state.saveUserSettingsForm.display_name = event.target.value;
1118 i.setState(i.state);
1121 handleMatrixUserIdChange(i: Settings, event: any) {
1122 i.state.saveUserSettingsForm.matrix_user_id = event.target.value;
1123 i.setState(i.state);
1126 handleNewPasswordChange(i: Settings, event: any) {
1127 i.state.changePasswordForm.new_password = event.target.value;
1128 if (i.state.changePasswordForm.new_password == "") {
1129 i.state.changePasswordForm.new_password = undefined;
1131 i.setState(i.state);
1134 handleNewPasswordVerifyChange(i: Settings, event: any) {
1135 i.state.changePasswordForm.new_password_verify = event.target.value;
1136 if (i.state.changePasswordForm.new_password_verify == "") {
1137 i.state.changePasswordForm.new_password_verify = undefined;
1139 i.setState(i.state);
1142 handleOldPasswordChange(i: Settings, event: any) {
1143 i.state.changePasswordForm.old_password = event.target.value;
1144 if (i.state.changePasswordForm.old_password == "") {
1145 i.state.changePasswordForm.old_password = undefined;
1147 i.setState(i.state);
1150 handleSaveSettingsSubmit(i: Settings, event: any) {
1151 event.preventDefault();
1152 i.setState({ saveUserSettingsLoading: true });
1153 const auth = myAuth();
1155 const form: SaveUserSettings = { ...i.state.saveUserSettingsForm, auth };
1156 WebSocketService.Instance.send(wsClient.saveUserSettings(form));
1160 handleChangePasswordSubmit(i: Settings, event: any) {
1161 event.preventDefault();
1162 i.setState({ changePasswordLoading: true });
1163 const auth = myAuth();
1164 const pForm = i.state.changePasswordForm;
1165 const new_password = pForm.new_password;
1166 const new_password_verify = pForm.new_password_verify;
1167 const old_password = pForm.old_password;
1168 if (auth && new_password && old_password && new_password_verify) {
1169 const form: ChangePassword = {
1171 new_password_verify,
1176 WebSocketService.Instance.send(wsClient.changePassword(form));
1180 handleDeleteAccountShowConfirmToggle(i: Settings, event: any) {
1181 event.preventDefault();
1182 i.setState({ deleteAccountShowConfirm: !i.state.deleteAccountShowConfirm });
1185 handleDeleteAccountPasswordChange(i: Settings, event: any) {
1186 i.state.deleteAccountForm.password = event.target.value;
1187 i.setState(i.state);
1190 handleDeleteAccount(i: Settings, event: any) {
1191 event.preventDefault();
1192 i.setState({ deleteAccountLoading: true });
1193 const auth = myAuth();
1194 const password = i.state.deleteAccountForm.password;
1195 if (auth && password) {
1196 const form: DeleteAccount = {
1200 WebSocketService.Instance.send(wsClient.deleteAccount(form));
1204 handleSwitchTab(i: { ctx: Settings; tab: string }) {
1205 i.ctx.setState({ currentTab: i.tab });
1208 parseMessage(msg: any) {
1209 const op = wsUserOp(msg);
1213 saveUserSettingsLoading: false,
1214 changePasswordLoading: false,
1215 deleteAccountLoading: false,
1217 toast(i18n.t(msg.error), "danger");
1219 } else if (op == UserOperation.SaveUserSettings) {
1220 this.setState({ saveUserSettingsLoading: false });
1221 toast(i18n.t("saved"));
1222 window.scrollTo(0, 0);
1223 } else if (op == UserOperation.ChangePassword) {
1224 const data = wsJsonToRes<LoginResponse>(msg);
1225 UserService.Instance.login(data);
1226 this.setState({ changePasswordLoading: false });
1227 window.scrollTo(0, 0);
1228 toast(i18n.t("password_changed"));
1229 } else if (op == UserOperation.DeleteAccount) {
1231 deleteAccountLoading: false,
1232 deleteAccountShowConfirm: false,
1234 UserService.Instance.logout();
1235 window.location.href = "/";
1236 } else if (op == UserOperation.BlockPerson) {
1237 const data = wsJsonToRes<BlockPersonResponse>(msg);
1238 updatePersonBlock(data);
1239 const mui = UserService.Instance.myUserInfo;
1241 this.setState({ personBlocks: mui.person_blocks });
1243 } else if (op == UserOperation.BlockCommunity) {
1244 const data = wsJsonToRes<BlockCommunityResponse>(msg);
1245 updateCommunityBlock(data);
1246 const mui = UserService.Instance.myUserInfo;
1248 this.setState({ communityBlocks: mui.community_blocks });