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 { CommunityLink } from "../community/community-link";
58 import { PersonListing } from "./person-listing";
60 interface SettingsState {
61 // TODO redo these forms
62 saveUserSettingsForm: {
65 default_sort_type?: number;
66 default_listing_type?: number;
67 interface_language?: string;
70 display_name?: string;
73 matrix_user_id?: string;
74 show_avatars?: boolean;
75 show_scores?: boolean;
76 send_notifications_to_email?: boolean;
77 bot_account?: boolean;
78 show_bot_accounts?: boolean;
79 show_read_posts?: boolean;
80 show_new_post_notifs?: boolean;
81 discussion_languages?: number[];
82 generate_totp_2fa?: boolean;
85 new_password?: string;
86 new_password_verify?: string;
87 old_password?: string;
92 personBlocks: PersonBlockView[];
93 communityBlocks: CommunityBlockView[];
96 saveUserSettingsLoading: boolean;
97 changePasswordLoading: boolean;
98 deleteAccountLoading: boolean;
99 deleteAccountShowConfirm: boolean;
100 siteRes: GetSiteResponse;
101 searchCommunityLoading: boolean;
102 searchCommunityOptions: Choice[];
103 searchPersonLoading: boolean;
104 searchPersonOptions: Choice[];
107 type FilterType = "user" | "community";
116 filterType: FilterType;
118 onSearch: (text: string) => void;
119 onChange: (choice: Choice) => void;
122 <div className="form-group row">
124 className="col-md-4 col-form-label"
125 htmlFor={`block-${filterType}-filter`}
127 {i18n.t(`block_${filterType}` as NoOptionI18nKeys)}
129 <div className="col-md-8">
131 id={`block-${filterType}-filter`}
133 { label: emDash, value: "0", disabled: true } as Choice,
143 export class Settings extends Component<any, SettingsState> {
144 private isoData = setIsoData(this.context);
145 private subscription?: Subscription;
146 state: SettingsState = {
147 saveUserSettingsForm: {},
148 changePasswordForm: {},
149 saveUserSettingsLoading: false,
150 changePasswordLoading: false,
151 deleteAccountLoading: false,
152 deleteAccountShowConfirm: false,
153 deleteAccountForm: {},
156 currentTab: "settings",
157 siteRes: this.isoData.site_res,
159 searchCommunityLoading: false,
160 searchCommunityOptions: [],
161 searchPersonLoading: false,
162 searchPersonOptions: [],
165 constructor(props: any, context: any) {
166 super(props, context);
168 this.handleSortTypeChange = this.handleSortTypeChange.bind(this);
169 this.handleListingTypeChange = this.handleListingTypeChange.bind(this);
170 this.handleBioChange = this.handleBioChange.bind(this);
171 this.handleDiscussionLanguageChange =
172 this.handleDiscussionLanguageChange.bind(this);
174 this.handleAvatarUpload = this.handleAvatarUpload.bind(this);
175 this.handleAvatarRemove = this.handleAvatarRemove.bind(this);
177 this.handleBannerUpload = this.handleBannerUpload.bind(this);
178 this.handleBannerRemove = this.handleBannerRemove.bind(this);
180 this.parseMessage = this.parseMessage.bind(this);
181 this.subscription = wsSubscribe(this.parseMessage);
183 const mui = UserService.Instance.myUserInfo;
190 default_listing_type,
196 show_new_post_notifs,
197 send_notifications_to_email,
208 } = mui.local_user_view;
212 personBlocks: mui.person_blocks,
213 communityBlocks: mui.community_blocks,
214 saveUserSettingsForm: {
215 ...this.state.saveUserSettingsForm,
217 theme: theme ?? "browser",
219 default_listing_type,
221 discussion_languages: mui.discussion_languages,
230 show_new_post_notifs,
233 send_notifications_to_email,
240 async componentDidMount() {
242 this.setState({ themeList: await fetchThemeList() });
245 componentWillUnmount() {
246 this.subscription?.unsubscribe();
249 get documentTitle(): string {
250 return i18n.t("settings");
255 <div className="container-lg">
258 title={this.documentTitle}
259 path={this.context.router.route.match.url}
260 description={this.documentTitle}
261 image={this.state.saveUserSettingsForm.avatar}
263 <ul className="nav nav-tabs mb-2">
264 <li className="nav-item">
266 className={`nav-link btn ${
267 this.state.currentTab == "settings" && "active"
270 { ctx: this, tab: "settings" },
277 <li className="nav-item">
279 className={`nav-link btn ${
280 this.state.currentTab == "blocks" && "active"
283 { ctx: this, tab: "blocks" },
291 {this.state.currentTab == "settings" && this.userSettings()}
292 {this.state.currentTab == "blocks" && this.blockCards()}
300 <div className="row">
301 <div className="col-12 col-md-6">
302 <div className="card border-secondary mb-3">
303 <div className="card-body">{this.saveUserSettingsHtmlForm()}</div>
306 <div className="col-12 col-md-6">
307 <div className="card border-secondary mb-3">
308 <div className="card-body">{this.changePasswordHtmlForm()}</div>
317 <div className="row">
318 <div className="col-12 col-md-6">
319 <div className="card border-secondary mb-3">
320 <div className="card-body">{this.blockUserCard()}</div>
323 <div className="col-12 col-md-6">
324 <div className="card border-secondary mb-3">
325 <div className="card-body">{this.blockCommunityCard()}</div>
332 changePasswordHtmlForm() {
335 <h5>{i18n.t("change_password")}</h5>
336 <form onSubmit={linkEvent(this, this.handleChangePasswordSubmit)}>
337 <div className="form-group row">
338 <label className="col-sm-5 col-form-label" htmlFor="user-password">
339 {i18n.t("new_password")}
341 <div className="col-sm-7">
345 className="form-control"
346 value={this.state.changePasswordForm.new_password}
347 autoComplete="new-password"
349 onInput={linkEvent(this, this.handleNewPasswordChange)}
353 <div className="form-group row">
355 className="col-sm-5 col-form-label"
356 htmlFor="user-verify-password"
358 {i18n.t("verify_password")}
360 <div className="col-sm-7">
363 id="user-verify-password"
364 className="form-control"
365 value={this.state.changePasswordForm.new_password_verify}
366 autoComplete="new-password"
368 onInput={linkEvent(this, this.handleNewPasswordVerifyChange)}
372 <div className="form-group row">
374 className="col-sm-5 col-form-label"
375 htmlFor="user-old-password"
377 {i18n.t("old_password")}
379 <div className="col-sm-7">
382 id="user-old-password"
383 className="form-control"
384 value={this.state.changePasswordForm.old_password}
385 autoComplete="new-password"
387 onInput={linkEvent(this, this.handleOldPasswordChange)}
391 <div className="form-group">
392 <button type="submit" className="btn btn-block btn-secondary mr-4">
393 {this.state.changePasswordLoading ? (
396 capitalizeFirstLetter(i18n.t("save"))
406 const { searchPersonLoading, searchPersonOptions } = this.state;
412 loading={searchPersonLoading}
413 onChange={this.handleBlockPerson}
414 onSearch={this.handlePersonSearch}
415 options={searchPersonOptions}
417 {this.blockedUsersList()}
425 <h5>{i18n.t("blocked_users")}</h5>
426 <ul className="list-unstyled mb-0">
427 {this.state.personBlocks.map(pb => (
428 <li key={pb.target.id}>
430 <PersonListing person={pb.target} />
432 className="btn btn-sm"
434 { ctx: this, recipientId: pb.target.id },
435 this.handleUnblockPerson
437 data-tippy-content={i18n.t("unblock_user")}
439 <Icon icon="x" classes="icon-inline" />
449 blockCommunityCard() {
450 const { searchCommunityLoading, searchCommunityOptions } = this.state;
455 filterType="community"
456 loading={searchCommunityLoading}
457 onChange={this.handleBlockCommunity}
458 onSearch={this.handleCommunitySearch}
459 options={searchCommunityOptions}
461 {this.blockedCommunitiesList()}
466 blockedCommunitiesList() {
469 <h5>{i18n.t("blocked_communities")}</h5>
470 <ul className="list-unstyled mb-0">
471 {this.state.communityBlocks.map(cb => (
472 <li key={cb.community.id}>
474 <CommunityLink community={cb.community} />
476 className="btn btn-sm"
478 { ctx: this, communityId: cb.community.id },
479 this.handleUnblockCommunity
481 data-tippy-content={i18n.t("unblock_community")}
483 <Icon icon="x" classes="icon-inline" />
493 saveUserSettingsHtmlForm() {
494 let selectedLangs = this.state.saveUserSettingsForm.discussion_languages;
498 <h5>{i18n.t("settings")}</h5>
499 <form onSubmit={linkEvent(this, this.handleSaveSettingsSubmit)}>
500 <div className="form-group row">
501 <label className="col-sm-5 col-form-label" htmlFor="display-name">
502 {i18n.t("display_name")}
504 <div className="col-sm-7">
508 className="form-control"
509 placeholder={i18n.t("optional")}
510 value={this.state.saveUserSettingsForm.display_name}
511 onInput={linkEvent(this, this.handleDisplayNameChange)}
512 pattern="^(?!@)(.+)$"
517 <div className="form-group row">
518 <label className="col-sm-3 col-form-label" htmlFor="user-bio">
521 <div className="col-sm-9">
523 initialContent={this.state.saveUserSettingsForm.bio}
524 onContentChange={this.handleBioChange}
526 hideNavigationWarnings
527 allLanguages={this.state.siteRes.all_languages}
528 siteLanguages={this.state.siteRes.discussion_languages}
532 <div className="form-group row">
533 <label className="col-sm-3 col-form-label" htmlFor="user-email">
536 <div className="col-sm-9">
540 className="form-control"
541 placeholder={i18n.t("optional")}
542 value={this.state.saveUserSettingsForm.email}
543 onInput={linkEvent(this, this.handleEmailChange)}
548 <div className="form-group row">
549 <label className="col-sm-5 col-form-label" htmlFor="matrix-user-id">
550 <a href={elementUrl} rel={relTags}>
551 {i18n.t("matrix_user_id")}
554 <div className="col-sm-7">
558 className="form-control"
559 placeholder="@user:example.com"
560 value={this.state.saveUserSettingsForm.matrix_user_id}
561 onInput={linkEvent(this, this.handleMatrixUserIdChange)}
562 pattern="^@[A-Za-z0-9._=-]+:[A-Za-z0-9.-]+\.[A-Za-z]{2,}$"
566 <div className="form-group row">
567 <label className="col-sm-3">{i18n.t("avatar")}</label>
568 <div className="col-sm-9">
570 uploadTitle={i18n.t("upload_avatar")}
571 imageSrc={this.state.saveUserSettingsForm.avatar}
572 onUpload={this.handleAvatarUpload}
573 onRemove={this.handleAvatarRemove}
578 <div className="form-group row">
579 <label className="col-sm-3">{i18n.t("banner")}</label>
580 <div className="col-sm-9">
582 uploadTitle={i18n.t("upload_banner")}
583 imageSrc={this.state.saveUserSettingsForm.banner}
584 onUpload={this.handleBannerUpload}
585 onRemove={this.handleBannerRemove}
589 <div className="form-group row">
590 <label className="col-sm-3" htmlFor="user-language">
591 {i18n.t("interface_language")}
593 <div className="col-sm-9">
596 value={this.state.saveUserSettingsForm.interface_language}
597 onChange={linkEvent(this, this.handleInterfaceLangChange)}
598 className="custom-select w-auto"
600 <option disabled aria-hidden="true">
601 {i18n.t("interface_language")}
603 <option value="browser">{i18n.t("browser_default")}</option>
604 <option disabled aria-hidden="true">
608 .sort((a, b) => a.code.localeCompare(b.code))
610 <option key={lang.code} value={lang.code}>
618 allLanguages={this.state.siteRes.all_languages}
619 siteLanguages={this.state.siteRes.discussion_languages}
620 selectedLanguageIds={selectedLangs}
623 onChange={this.handleDiscussionLanguageChange}
625 <div className="form-group row">
626 <label className="col-sm-3" htmlFor="user-theme">
629 <div className="col-sm-9">
632 value={this.state.saveUserSettingsForm.theme}
633 onChange={linkEvent(this, this.handleThemeChange)}
634 className="custom-select w-auto"
636 <option disabled aria-hidden="true">
639 <option value="browser">{i18n.t("browser_default")}</option>
640 {this.state.themeList.map(theme => (
641 <option key={theme} value={theme}>
648 <form className="form-group row">
649 <label className="col-sm-3">{i18n.t("type")}</label>
650 <div className="col-sm-9">
653 Object.values(ListingType)[
654 this.state.saveUserSettingsForm.default_listing_type ?? 1
657 showLocal={showLocal(this.isoData)}
659 onChange={this.handleListingTypeChange}
663 <form className="form-group row">
664 <label className="col-sm-3">{i18n.t("sort_type")}</label>
665 <div className="col-sm-9">
668 Object.values(SortType)[
669 this.state.saveUserSettingsForm.default_sort_type ?? 0
672 onChange={this.handleSortTypeChange}
676 {enableNsfw(this.state.siteRes) && (
677 <div className="form-group">
678 <div className="form-check">
680 className="form-check-input"
683 checked={this.state.saveUserSettingsForm.show_nsfw}
684 onChange={linkEvent(this, this.handleShowNsfwChange)}
686 <label className="form-check-label" htmlFor="user-show-nsfw">
687 {i18n.t("show_nsfw")}
692 <div className="form-group">
693 <div className="form-check">
695 className="form-check-input"
696 id="user-show-scores"
698 checked={this.state.saveUserSettingsForm.show_scores}
699 onChange={linkEvent(this, this.handleShowScoresChange)}
701 <label className="form-check-label" htmlFor="user-show-scores">
702 {i18n.t("show_scores")}
706 <div className="form-group">
707 <div className="form-check">
709 className="form-check-input"
710 id="user-show-avatars"
712 checked={this.state.saveUserSettingsForm.show_avatars}
713 onChange={linkEvent(this, this.handleShowAvatarsChange)}
715 <label className="form-check-label" htmlFor="user-show-avatars">
716 {i18n.t("show_avatars")}
720 <div className="form-group">
721 <div className="form-check">
723 className="form-check-input"
724 id="user-bot-account"
726 checked={this.state.saveUserSettingsForm.bot_account}
727 onChange={linkEvent(this, this.handleBotAccount)}
729 <label className="form-check-label" htmlFor="user-bot-account">
730 {i18n.t("bot_account")}
734 <div className="form-group">
735 <div className="form-check">
737 className="form-check-input"
738 id="user-show-bot-accounts"
740 checked={this.state.saveUserSettingsForm.show_bot_accounts}
741 onChange={linkEvent(this, this.handleShowBotAccounts)}
744 className="form-check-label"
745 htmlFor="user-show-bot-accounts"
747 {i18n.t("show_bot_accounts")}
751 <div className="form-group">
752 <div className="form-check">
754 className="form-check-input"
755 id="user-show-read-posts"
757 checked={this.state.saveUserSettingsForm.show_read_posts}
758 onChange={linkEvent(this, this.handleReadPosts)}
761 className="form-check-label"
762 htmlFor="user-show-read-posts"
764 {i18n.t("show_read_posts")}
768 <div className="form-group">
769 <div className="form-check">
771 className="form-check-input"
772 id="user-show-new-post-notifs"
774 checked={this.state.saveUserSettingsForm.show_new_post_notifs}
775 onChange={linkEvent(this, this.handleShowNewPostNotifs)}
778 className="form-check-label"
779 htmlFor="user-show-new-post-notifs"
781 {i18n.t("show_new_post_notifs")}
785 <div className="form-group">
786 <div className="form-check">
788 className="form-check-input"
789 id="user-send-notifications-to-email"
791 disabled={!this.state.saveUserSettingsForm.email}
793 this.state.saveUserSettingsForm.send_notifications_to_email
797 this.handleSendNotificationsToEmailChange
801 className="form-check-label"
802 htmlFor="user-send-notifications-to-email"
804 {i18n.t("send_notifications_to_email")}
809 <div className="form-group">
810 <button type="submit" className="btn btn-block btn-secondary mr-4">
811 {this.state.saveUserSettingsLoading ? (
814 capitalizeFirstLetter(i18n.t("save"))
819 <div className="form-group">
821 className="btn btn-block btn-danger"
824 this.handleDeleteAccountShowConfirmToggle
827 {i18n.t("delete_account")}
829 {this.state.deleteAccountShowConfirm && (
831 <div className="my-2 alert alert-danger" role="alert">
832 {i18n.t("delete_account_confirm")}
836 value={this.state.deleteAccountForm.password}
837 autoComplete="new-password"
841 this.handleDeleteAccountPasswordChange
843 className="form-control my-2"
846 className="btn btn-danger mr-4"
847 disabled={!this.state.deleteAccountForm.password}
848 onClick={linkEvent(this, this.handleDeleteAccount)}
850 {this.state.deleteAccountLoading ? (
853 capitalizeFirstLetter(i18n.t("delete"))
857 className="btn btn-secondary"
860 this.handleDeleteAccountShowConfirmToggle
875 UserService.Instance.myUserInfo?.local_user_view.local_user.totp_2fa_url;
880 <div className="form-group">
881 <div className="form-check">
883 className="form-check-input"
884 id="user-generate-totp"
886 checked={this.state.saveUserSettingsForm.generate_totp_2fa}
887 onChange={linkEvent(this, this.handleGenerateTotp)}
889 <label className="form-check-label" htmlFor="user-generate-totp">
890 {i18n.t("set_up_two_factor")}
899 <a className="btn btn-secondary mb-2" href={totpUrl}>
900 {i18n.t("two_factor_link")}
903 <div className="form-group">
904 <div className="form-check">
906 className="form-check-input"
907 id="user-remove-totp"
910 this.state.saveUserSettingsForm.generate_totp_2fa == false
912 onChange={linkEvent(this, this.handleRemoveTotp)}
914 <label className="form-check-label" htmlFor="user-remove-totp">
915 {i18n.t("remove_two_factor")}
925 handlePersonSearch = debounce(async (text: string) => {
926 this.setState({ searchPersonLoading: true });
928 const searchPersonOptions: Choice[] = [];
930 if (text.length > 0) {
931 searchPersonOptions.push(
932 ...(await fetchUsers(text)).users.map(personToChoice)
937 searchPersonLoading: false,
942 handleCommunitySearch = debounce(async (text: string) => {
943 this.setState({ searchCommunityLoading: true });
945 const searchCommunityOptions: Choice[] = [];
947 if (text.length > 0) {
948 searchCommunityOptions.push(
949 ...(await fetchCommunities(text)).communities.map(communityToChoice)
954 searchCommunityLoading: false,
955 searchCommunityOptions,
959 handleBlockPerson({ value }: Choice) {
960 const auth = myAuth();
961 if (auth && value !== "0") {
962 const blockUserForm: BlockPerson = {
963 person_id: Number(value),
968 WebSocketService.Instance.send(wsClient.blockPerson(blockUserForm));
972 handleUnblockPerson(i: { ctx: Settings; recipientId: number }) {
973 const auth = myAuth();
975 const blockUserForm: BlockPerson = {
976 person_id: i.recipientId,
980 WebSocketService.Instance.send(wsClient.blockPerson(blockUserForm));
984 handleBlockCommunity({ value }: Choice) {
985 const auth = myAuth();
986 if (auth && value !== "0") {
987 const blockCommunityForm: BlockCommunity = {
988 community_id: Number(value),
992 WebSocketService.Instance.send(
993 wsClient.blockCommunity(blockCommunityForm)
998 handleUnblockCommunity(i: { ctx: Settings; communityId: number }) {
999 const auth = myAuth();
1001 const blockCommunityForm: BlockCommunity = {
1002 community_id: i.communityId,
1006 WebSocketService.Instance.send(
1007 wsClient.blockCommunity(blockCommunityForm)
1012 handleShowNsfwChange(i: Settings, event: any) {
1013 i.state.saveUserSettingsForm.show_nsfw = event.target.checked;
1014 i.setState(i.state);
1017 handleShowAvatarsChange(i: Settings, event: any) {
1018 i.state.saveUserSettingsForm.show_avatars = event.target.checked;
1019 let mui = UserService.Instance.myUserInfo;
1021 mui.local_user_view.local_user.show_avatars = event.target.checked;
1023 i.setState(i.state);
1026 handleBotAccount(i: Settings, event: any) {
1027 i.state.saveUserSettingsForm.bot_account = event.target.checked;
1028 i.setState(i.state);
1031 handleShowBotAccounts(i: Settings, event: any) {
1032 i.state.saveUserSettingsForm.show_bot_accounts = event.target.checked;
1033 i.setState(i.state);
1036 handleReadPosts(i: Settings, event: any) {
1037 i.state.saveUserSettingsForm.show_read_posts = event.target.checked;
1038 i.setState(i.state);
1041 handleShowNewPostNotifs(i: Settings, event: any) {
1042 i.state.saveUserSettingsForm.show_new_post_notifs = event.target.checked;
1043 i.setState(i.state);
1046 handleShowScoresChange(i: Settings, event: any) {
1047 i.state.saveUserSettingsForm.show_scores = event.target.checked;
1048 let mui = UserService.Instance.myUserInfo;
1050 mui.local_user_view.local_user.show_scores = event.target.checked;
1052 i.setState(i.state);
1055 handleGenerateTotp(i: Settings, event: any) {
1056 // Coerce false to undefined here, so it won't generate it.
1057 let checked: boolean | undefined = event.target.checked || undefined;
1059 toast(i18n.t("two_factor_setup_instructions"));
1061 i.state.saveUserSettingsForm.generate_totp_2fa = checked;
1062 i.setState(i.state);
1065 handleRemoveTotp(i: Settings, event: any) {
1066 // Coerce true to undefined here, so it won't generate it.
1067 let checked: boolean | undefined = !event.target.checked && undefined;
1068 i.state.saveUserSettingsForm.generate_totp_2fa = checked;
1069 i.setState(i.state);
1072 handleSendNotificationsToEmailChange(i: Settings, event: any) {
1073 i.state.saveUserSettingsForm.send_notifications_to_email =
1074 event.target.checked;
1075 i.setState(i.state);
1078 handleThemeChange(i: Settings, event: any) {
1079 i.state.saveUserSettingsForm.theme = event.target.value;
1080 setTheme(event.target.value, true);
1081 i.setState(i.state);
1084 handleInterfaceLangChange(i: Settings, event: any) {
1085 i.state.saveUserSettingsForm.interface_language = event.target.value;
1086 i18n.changeLanguage(
1087 getLanguages(i.state.saveUserSettingsForm.interface_language).at(0)
1089 i.setState(i.state);
1092 handleDiscussionLanguageChange(val: number[]) {
1094 s => ((s.saveUserSettingsForm.discussion_languages = val), s)
1098 handleSortTypeChange(val: SortType) {
1101 (s.saveUserSettingsForm.default_sort_type =
1102 Object.keys(SortType).indexOf(val)),
1108 handleListingTypeChange(val: ListingType) {
1111 (s.saveUserSettingsForm.default_listing_type =
1112 Object.keys(ListingType).indexOf(val)),
1118 handleEmailChange(i: Settings, event: any) {
1119 i.state.saveUserSettingsForm.email = event.target.value;
1120 i.setState(i.state);
1123 handleBioChange(val: string) {
1124 this.setState(s => ((s.saveUserSettingsForm.bio = val), s));
1127 handleAvatarUpload(url: string) {
1128 this.setState(s => ((s.saveUserSettingsForm.avatar = url), s));
1131 handleAvatarRemove() {
1132 this.setState(s => ((s.saveUserSettingsForm.avatar = ""), s));
1135 handleBannerUpload(url: string) {
1136 this.setState(s => ((s.saveUserSettingsForm.banner = url), s));
1139 handleBannerRemove() {
1140 this.setState(s => ((s.saveUserSettingsForm.banner = ""), s));
1143 handleDisplayNameChange(i: Settings, event: any) {
1144 i.state.saveUserSettingsForm.display_name = event.target.value;
1145 i.setState(i.state);
1148 handleMatrixUserIdChange(i: Settings, event: any) {
1149 i.state.saveUserSettingsForm.matrix_user_id = event.target.value;
1150 i.setState(i.state);
1153 handleNewPasswordChange(i: Settings, event: any) {
1154 i.state.changePasswordForm.new_password = event.target.value;
1155 if (i.state.changePasswordForm.new_password == "") {
1156 i.state.changePasswordForm.new_password = undefined;
1158 i.setState(i.state);
1161 handleNewPasswordVerifyChange(i: Settings, event: any) {
1162 i.state.changePasswordForm.new_password_verify = event.target.value;
1163 if (i.state.changePasswordForm.new_password_verify == "") {
1164 i.state.changePasswordForm.new_password_verify = undefined;
1166 i.setState(i.state);
1169 handleOldPasswordChange(i: Settings, event: any) {
1170 i.state.changePasswordForm.old_password = event.target.value;
1171 if (i.state.changePasswordForm.old_password == "") {
1172 i.state.changePasswordForm.old_password = undefined;
1174 i.setState(i.state);
1177 handleSaveSettingsSubmit(i: Settings, event: any) {
1178 event.preventDefault();
1179 i.setState({ saveUserSettingsLoading: true });
1180 let auth = myAuth();
1182 let form: SaveUserSettings = { ...i.state.saveUserSettingsForm, auth };
1183 WebSocketService.Instance.send(wsClient.saveUserSettings(form));
1187 handleChangePasswordSubmit(i: Settings, event: any) {
1188 event.preventDefault();
1189 i.setState({ changePasswordLoading: true });
1190 let auth = myAuth();
1191 let pForm = i.state.changePasswordForm;
1192 let new_password = pForm.new_password;
1193 let new_password_verify = pForm.new_password_verify;
1194 let old_password = pForm.old_password;
1195 if (auth && new_password && old_password && new_password_verify) {
1196 let form: ChangePassword = {
1198 new_password_verify,
1203 WebSocketService.Instance.send(wsClient.changePassword(form));
1207 handleDeleteAccountShowConfirmToggle(i: Settings, event: any) {
1208 event.preventDefault();
1209 i.setState({ deleteAccountShowConfirm: !i.state.deleteAccountShowConfirm });
1212 handleDeleteAccountPasswordChange(i: Settings, event: any) {
1213 i.state.deleteAccountForm.password = event.target.value;
1214 i.setState(i.state);
1217 handleDeleteAccount(i: Settings, event: any) {
1218 event.preventDefault();
1219 i.setState({ deleteAccountLoading: true });
1220 let auth = myAuth();
1221 let password = i.state.deleteAccountForm.password;
1222 if (auth && password) {
1223 let form: DeleteAccount = {
1227 WebSocketService.Instance.send(wsClient.deleteAccount(form));
1231 handleSwitchTab(i: { ctx: Settings; tab: string }) {
1232 i.ctx.setState({ currentTab: i.tab });
1235 parseMessage(msg: any) {
1236 let op = wsUserOp(msg);
1240 saveUserSettingsLoading: false,
1241 changePasswordLoading: false,
1242 deleteAccountLoading: false,
1244 toast(i18n.t(msg.error), "danger");
1246 } else if (op == UserOperation.SaveUserSettings) {
1247 let data = wsJsonToRes<LoginResponse>(msg);
1248 UserService.Instance.login(data);
1250 this.setState({ saveUserSettingsLoading: false });
1251 toast(i18n.t("saved"));
1252 window.scrollTo(0, 0);
1253 } else if (op == UserOperation.ChangePassword) {
1254 let data = wsJsonToRes<LoginResponse>(msg);
1255 UserService.Instance.login(data);
1256 this.setState({ changePasswordLoading: false });
1257 window.scrollTo(0, 0);
1258 toast(i18n.t("password_changed"));
1259 } else if (op == UserOperation.DeleteAccount) {
1261 deleteAccountLoading: false,
1262 deleteAccountShowConfirm: false,
1264 UserService.Instance.logout();
1265 window.location.href = "/";
1266 } else if (op == UserOperation.BlockPerson) {
1267 let data = wsJsonToRes<BlockPersonResponse>(msg);
1268 updatePersonBlock(data);
1269 let mui = UserService.Instance.myUserInfo;
1271 this.setState({ personBlocks: mui.person_blocks });
1273 } else if (op == UserOperation.BlockCommunity) {
1274 let data = wsJsonToRes<BlockCommunityResponse>(msg);
1275 updateCommunityBlock(data);
1276 let mui = UserService.Instance.myUserInfo;
1278 this.setState({ communityBlocks: mui.community_blocks });