1 import { Component, linkEvent } from "inferno";
2 import { Link } from "inferno-router";
3 import { Subscription } from "rxjs";
4 import ISO6391 from "iso-639-1";
13 GetPersonDetailsResponse,
20 } from "lemmy-js-client";
21 import { InitialFetchRequest, PersonDetailsView } from "../interfaces";
22 import { WebSocketService, UserService } from "../services";
27 capitalizeFirstLetter,
43 createPostLikeFindRes,
51 restoreScrollPosition,
53 import { PersonListing } from "./person-listing";
54 import { HtmlTags } from "./html-tags";
55 import { SortSelect } from "./sort-select";
56 import { ListingTypeSelect } from "./listing-type-select";
57 import { MomentTime } from "./moment-time";
58 import { i18n } from "../i18next";
59 import moment from "moment";
60 import { PersonDetails } from "./person-details";
61 import { MarkdownTextArea } from "./markdown-textarea";
62 import { Icon, Spinner } from "./icon";
63 import { ImageUploadForm } from "./image-upload-form";
64 import { BannerIconHeader } from "./banner-icon-header";
65 import { CommunityLink } from "./community-link";
67 interface PersonState {
68 personRes: GetPersonDetailsResponse;
71 view: PersonDetailsView;
75 saveUserSettingsForm: SaveUserSettings;
76 changePasswordForm: ChangePassword;
77 saveUserSettingsLoading: boolean;
78 changePasswordLoading: boolean;
79 deleteAccountLoading: boolean;
80 deleteAccountShowConfirm: boolean;
81 deleteAccountForm: DeleteAccount;
82 siteRes: GetSiteResponse;
85 interface PersonProps {
86 view: PersonDetailsView;
89 person_id: number | null;
99 export class Person extends Component<any, PersonState> {
100 private isoData = setIsoData(this.context);
101 private subscription: Subscription;
102 private emptyState: PersonState = {
103 personRes: undefined,
104 personId: getIdFromProps(this.props),
105 userName: getUsernameFromProps(this.props),
107 view: Person.getViewFromProps(this.props.match.view),
108 sort: Person.getSortTypeFromProps(this.props.match.sort),
109 page: Person.getPageFromProps(this.props.match.page),
110 saveUserSettingsForm: {
111 auth: authField(false),
113 changePasswordForm: {
115 new_password_verify: null,
117 auth: authField(false),
119 saveUserSettingsLoading: null,
120 changePasswordLoading: false,
121 deleteAccountLoading: null,
122 deleteAccountShowConfirm: false,
125 auth: authField(false),
127 siteRes: this.isoData.site_res,
130 constructor(props: any, context: any) {
131 super(props, context);
133 this.state = this.emptyState;
134 this.handleSortChange = this.handleSortChange.bind(this);
135 this.handleUserSettingsSortTypeChange = this.handleUserSettingsSortTypeChange.bind(
138 this.handleUserSettingsListingTypeChange = this.handleUserSettingsListingTypeChange.bind(
141 this.handlePageChange = this.handlePageChange.bind(this);
142 this.handleUserSettingsBioChange = this.handleUserSettingsBioChange.bind(
146 this.handleAvatarUpload = this.handleAvatarUpload.bind(this);
147 this.handleAvatarRemove = this.handleAvatarRemove.bind(this);
149 this.handleBannerUpload = this.handleBannerUpload.bind(this);
150 this.handleBannerRemove = this.handleBannerRemove.bind(this);
152 this.parseMessage = this.parseMessage.bind(this);
153 this.subscription = wsSubscribe(this.parseMessage);
155 // Only fetch the data if coming from another route
156 if (this.isoData.path == this.context.router.route.match.url) {
157 this.state.personRes = this.isoData.routeData[0];
159 this.state.loading = false;
161 this.fetchUserData();
168 let form: GetPersonDetails = {
169 person_id: this.state.personId,
170 username: this.state.userName,
171 sort: this.state.sort,
172 saved_only: this.state.view === PersonDetailsView.Saved,
173 page: this.state.page,
175 auth: authField(false),
177 WebSocketService.Instance.send(wsClient.getPersonDetails(form));
180 get isCurrentUser() {
182 UserService.Instance.localUserView?.person.id ==
183 this.state.personRes.person_view.person.id
187 static getViewFromProps(view: string): PersonDetailsView {
188 return view ? PersonDetailsView[view] : PersonDetailsView.Overview;
191 static getSortTypeFromProps(sort: string): SortType {
192 return sort ? routeSortTypeToEnum(sort) : SortType.New;
195 static getPageFromProps(page: number): number {
196 return page ? Number(page) : 1;
199 static fetchInitialData(req: InitialFetchRequest): Promise<any>[] {
200 let pathSplit = req.path.split("/");
201 let promises: Promise<any>[] = [];
203 // It can be /u/me, or /username/1
204 let idOrName = pathSplit[2];
205 let person_id: number;
206 let username: string;
207 if (isNaN(Number(idOrName))) {
210 person_id = Number(idOrName);
213 let view = this.getViewFromProps(pathSplit[4]);
214 let sort = this.getSortTypeFromProps(pathSplit[6]);
215 let page = this.getPageFromProps(Number(pathSplit[8]));
217 let form: GetPersonDetails = {
219 saved_only: view === PersonDetailsView.Saved,
223 setOptionalAuth(form, req.auth);
224 this.setIdOrName(form, person_id, username);
225 promises.push(req.client.getPersonDetails(form));
229 static setIdOrName(obj: any, id: number, name_: string) {
233 obj.username = name_;
237 componentWillUnmount() {
238 this.subscription.unsubscribe();
239 saveScrollPosition(this.context);
242 static getDerivedStateFromProps(props: any): PersonProps {
244 view: this.getViewFromProps(props.match.params.view),
245 sort: this.getSortTypeFromProps(props.match.params.sort),
246 page: this.getPageFromProps(props.match.params.page),
247 person_id: Number(props.match.params.id) || null,
248 username: props.match.params.username,
252 componentDidUpdate(lastProps: any) {
253 // Necessary if you are on a post and you click another post (same route)
255 lastProps.location.pathname.split("/")[2] !==
256 lastProps.history.location.pathname.split("/")[2]
258 // Couldnt get a refresh working. This does for now.
263 get documentTitle(): string {
264 return `@${this.state.personRes.person_view.person.name} - ${this.state.siteRes.site_view.site.name}`;
267 get bioTag(): string {
268 return this.state.personRes.person_view.person.bio
269 ? previewLines(this.state.personRes.person_view.person.bio)
275 <div class="container">
276 {this.state.loading ? (
282 <div class="col-12 col-md-8">
285 title={this.documentTitle}
286 path={this.context.router.route.match.url}
287 description={this.bioTag}
288 image={this.state.personRes.person_view.person.avatar}
293 {!this.state.loading && this.selects()}
295 personRes={this.state.personRes}
296 admins={this.state.siteRes.admins}
297 sort={this.state.sort}
298 page={this.state.page}
301 this.state.siteRes.site_view.site.enable_downvotes
303 enableNsfw={this.state.siteRes.site_view.site.enable_nsfw}
304 view={this.state.view}
305 onPageChange={this.handlePageChange}
309 {!this.state.loading && (
310 <div class="col-12 col-md-4">
311 {this.isCurrentUser && this.userSettings()}
324 <div class="btn-group btn-group-toggle flex-wrap mb-2">
326 className={`btn btn-outline-secondary pointer
327 ${this.state.view == PersonDetailsView.Overview && "active"}
332 value={PersonDetailsView.Overview}
333 checked={this.state.view === PersonDetailsView.Overview}
334 onChange={linkEvent(this, this.handleViewChange)}
339 className={`btn btn-outline-secondary pointer
340 ${this.state.view == PersonDetailsView.Comments && "active"}
345 value={PersonDetailsView.Comments}
346 checked={this.state.view == PersonDetailsView.Comments}
347 onChange={linkEvent(this, this.handleViewChange)}
352 className={`btn btn-outline-secondary pointer
353 ${this.state.view == PersonDetailsView.Posts && "active"}
358 value={PersonDetailsView.Posts}
359 checked={this.state.view == PersonDetailsView.Posts}
360 onChange={linkEvent(this, this.handleViewChange)}
365 className={`btn btn-outline-secondary pointer
366 ${this.state.view == PersonDetailsView.Saved && "active"}
371 value={PersonDetailsView.Saved}
372 checked={this.state.view == PersonDetailsView.Saved}
373 onChange={linkEvent(this, this.handleViewChange)}
383 <div className="mb-2">
384 <span class="mr-3">{this.viewRadios()}</span>
386 sort={this.state.sort}
387 onChange={this.handleSortChange}
392 href={`/feeds/u/${this.state.userName}.xml?sort=${this.state.sort}`}
396 <Icon icon="rss" classes="text-muted small mx-2" />
403 let pv = this.state.personRes?.person_view;
407 <BannerIconHeader banner={pv.person.banner} icon={pv.person.avatar} />
410 <div class="mb-0 d-flex flex-wrap">
412 {pv.person.display_name && (
413 <h5 class="mb-0">{pv.person.display_name}</h5>
415 <ul class="list-inline mb-2">
416 <li className="list-inline-item">
425 {pv.person.banned && (
426 <li className="list-inline-item badge badge-danger">
432 <div className="flex-grow-1 unselectable pointer mx-2"></div>
433 {this.isCurrentUser ? (
435 class="d-flex align-self-start btn btn-secondary mr-2"
436 onClick={linkEvent(this, this.handleLogoutClick)}
442 className={"d-flex align-self-start btn btn-secondary"}
443 to={`/create_private_message/recipient/${pv.person.id}`}
445 {i18n.t("send_message")}
450 <div className="d-flex align-items-center mb-2">
453 dangerouslySetInnerHTML={mdToHtml(pv.person.bio)}
458 <ul class="list-inline mb-2">
459 <li className="list-inline-item badge badge-light">
460 {i18n.t("number_of_posts", { count: pv.counts.post_count })}
462 <li className="list-inline-item badge badge-light">
463 {i18n.t("number_of_comments", {
464 count: pv.counts.comment_count,
469 <div class="text-muted">
470 {i18n.t("joined")}{" "}
471 <MomentTime data={pv.person} showAgo ignoreUpdated />
473 <div className="d-flex align-items-center text-muted mb-2">
475 <span className="ml-2">
476 {i18n.t("cake_day_title")}{" "}
477 {moment.utc(pv.person.published).local().format("MMM DD, YYYY")}
486 // TODO this isn't used currently
490 // className={`d-flex align-self-start btn btn-secondary mr-2 ${
491 // !pv.local_user.matrix_user_id && "invisible"
494 // href={`https://matrix.to/#/${pv.local_user.matrix_user_id}`}
496 // {i18n.t("send_secure_message")}
504 <div class="card border-secondary mb-3">
505 <div class="card-body">
506 {this.saveUserSettingsHtmlForm()}
508 {this.changePasswordHtmlForm()}
515 changePasswordHtmlForm() {
518 <h5>{i18n.t("change_password")}</h5>
519 <form onSubmit={linkEvent(this, this.handleChangePasswordSubmit)}>
520 <div class="form-group row">
521 <label class="col-lg-5 col-form-label" htmlFor="user-password">
522 {i18n.t("new_password")}
524 <div class="col-lg-7">
529 value={this.state.changePasswordForm.new_password}
530 autoComplete="new-password"
532 onInput={linkEvent(this, this.handleNewPasswordChange)}
536 <div class="form-group row">
538 class="col-lg-5 col-form-label"
539 htmlFor="user-verify-password"
541 {i18n.t("verify_password")}
543 <div class="col-lg-7">
546 id="user-verify-password"
548 value={this.state.changePasswordForm.new_password_verify}
549 autoComplete="new-password"
551 onInput={linkEvent(this, this.handleNewPasswordVerifyChange)}
555 <div class="form-group row">
556 <label class="col-lg-5 col-form-label" htmlFor="user-old-password">
557 {i18n.t("old_password")}
559 <div class="col-lg-7">
562 id="user-old-password"
564 value={this.state.changePasswordForm.old_password}
565 autoComplete="new-password"
567 onInput={linkEvent(this, this.handleOldPasswordChange)}
571 <div class="form-group">
572 <button type="submit" class="btn btn-block btn-secondary mr-4">
573 {this.state.changePasswordLoading ? (
576 capitalizeFirstLetter(i18n.t("save"))
585 saveUserSettingsHtmlForm() {
588 <h5>{i18n.t("settings")}</h5>
589 <form onSubmit={linkEvent(this, this.handleSaveUserSettingsSubmit)}>
590 <div class="form-group">
591 <label>{i18n.t("avatar")}</label>
593 uploadTitle={i18n.t("upload_avatar")}
594 imageSrc={this.state.saveUserSettingsForm.avatar}
595 onUpload={this.handleAvatarUpload}
596 onRemove={this.handleAvatarRemove}
600 <div class="form-group">
601 <label>{i18n.t("banner")}</label>
603 uploadTitle={i18n.t("upload_banner")}
604 imageSrc={this.state.saveUserSettingsForm.banner}
605 onUpload={this.handleBannerUpload}
606 onRemove={this.handleBannerRemove}
609 <div class="form-group">
610 <label htmlFor="user-language">{i18n.t("language")}</label>
613 value={this.state.saveUserSettingsForm.lang}
614 onChange={linkEvent(this, this.handleUserSettingsLangChange)}
615 class="ml-2 custom-select w-auto"
617 <option disabled aria-hidden="true">
620 <option value="browser">{i18n.t("browser_default")}</option>
621 <option disabled aria-hidden="true">
624 {languages.sort().map(lang => (
625 <option value={lang.code}>
626 {ISO6391.getNativeName(lang.code) || lang.code}
631 <div class="form-group">
632 <label htmlFor="user-theme">{i18n.t("theme")}</label>
635 value={this.state.saveUserSettingsForm.theme}
636 onChange={linkEvent(this, this.handleUserSettingsThemeChange)}
637 class="ml-2 custom-select w-auto"
639 <option disabled aria-hidden="true">
642 <option value="browser">{i18n.t("browser_default")}</option>
643 {themes.map(theme => (
644 <option value={theme}>{theme}</option>
648 <form className="form-group">
650 <div class="mr-2">{i18n.t("type")}</div>
654 Object.values(ListingType)[
655 this.state.saveUserSettingsForm.default_listing_type
659 this.state.siteRes.federated_instances?.linked.length > 0
661 onChange={this.handleUserSettingsListingTypeChange}
664 <form className="form-group">
666 <div class="mr-2">{i18n.t("sort_type")}</div>
670 Object.values(SortType)[
671 this.state.saveUserSettingsForm.default_sort_type
674 onChange={this.handleUserSettingsSortTypeChange}
677 <div class="form-group row">
678 <label class="col-lg-5 col-form-label" htmlFor="display-name">
679 {i18n.t("display_name")}
681 <div class="col-lg-7">
686 placeholder={i18n.t("optional")}
687 value={this.state.saveUserSettingsForm.display_name}
690 this.handleUserSettingsPreferredUsernameChange
692 pattern="^(?!@)(.+)$"
698 <div class="form-group row">
699 <label class="col-lg-3 col-form-label" htmlFor="user-bio">
702 <div class="col-lg-9">
704 initialContent={this.state.saveUserSettingsForm.bio}
705 onContentChange={this.handleUserSettingsBioChange}
707 hideNavigationWarnings
711 <div class="form-group row">
712 <label class="col-lg-3 col-form-label" htmlFor="user-email">
715 <div class="col-lg-9">
720 placeholder={i18n.t("optional")}
721 value={this.state.saveUserSettingsForm.email}
722 onInput={linkEvent(this, this.handleUserSettingsEmailChange)}
727 <div class="form-group row">
728 <label class="col-lg-5 col-form-label" htmlFor="matrix-user-id">
729 <a href={elementUrl} rel="noopener">
730 {i18n.t("matrix_user_id")}
733 <div class="col-lg-7">
738 placeholder="@user:example.com"
739 value={this.state.saveUserSettingsForm.matrix_user_id}
742 this.handleUserSettingsMatrixUserIdChange
744 pattern="^@[A-Za-z0-9._=-]+:[A-Za-z0-9.-]+\.[A-Za-z]{2,}$"
748 {this.state.siteRes.site_view.site.enable_nsfw && (
749 <div class="form-group">
750 <div class="form-check">
752 class="form-check-input"
755 checked={this.state.saveUserSettingsForm.show_nsfw}
758 this.handleUserSettingsShowNsfwChange
761 <label class="form-check-label" htmlFor="user-show-nsfw">
762 {i18n.t("show_nsfw")}
767 <div class="form-group">
768 <div class="form-check">
770 class="form-check-input"
771 id="user-show-scores"
773 checked={this.state.saveUserSettingsForm.show_scores}
776 this.handleUserSettingsShowScoresChange
779 <label class="form-check-label" htmlFor="user-show-scores">
780 {i18n.t("show_scores")}
784 <div class="form-group">
785 <div class="form-check">
787 class="form-check-input"
788 id="user-show-avatars"
790 checked={this.state.saveUserSettingsForm.show_avatars}
793 this.handleUserSettingsShowAvatarsChange
796 <label class="form-check-label" htmlFor="user-show-avatars">
797 {i18n.t("show_avatars")}
801 <div class="form-group">
802 <div class="form-check">
804 class="form-check-input"
805 id="user-send-notifications-to-email"
807 disabled={!this.state.saveUserSettingsForm.email}
809 this.state.saveUserSettingsForm.send_notifications_to_email
813 this.handleUserSettingsSendNotificationsToEmailChange
817 class="form-check-label"
818 htmlFor="user-send-notifications-to-email"
820 {i18n.t("send_notifications_to_email")}
824 <div class="form-group">
825 <button type="submit" class="btn btn-block btn-secondary mr-4">
826 {this.state.saveUserSettingsLoading ? (
829 capitalizeFirstLetter(i18n.t("save"))
834 <div class="form-group">
836 class="btn btn-block btn-danger"
839 this.handleDeleteAccountShowConfirmToggle
842 {i18n.t("delete_account")}
844 {this.state.deleteAccountShowConfirm && (
846 <div class="my-2 alert alert-danger" role="alert">
847 {i18n.t("delete_account_confirm")}
851 value={this.state.deleteAccountForm.password}
852 autoComplete="new-password"
856 this.handleDeleteAccountPasswordChange
858 class="form-control my-2"
861 class="btn btn-danger mr-4"
862 disabled={!this.state.deleteAccountForm.password}
863 onClick={linkEvent(this, this.handleDeleteAccount)}
865 {this.state.deleteAccountLoading ? (
868 capitalizeFirstLetter(i18n.t("delete"))
872 class="btn btn-secondary"
875 this.handleDeleteAccountShowConfirmToggle
891 {this.state.personRes.moderates.length > 0 && (
892 <div class="card border-secondary mb-3">
893 <div class="card-body">
894 <h5>{i18n.t("moderates")}</h5>
895 <ul class="list-unstyled mb-0">
896 {this.state.personRes.moderates.map(cmv => (
898 <CommunityLink community={cmv.community} />
912 {this.state.personRes.follows.length > 0 && (
913 <div class="card border-secondary mb-3">
914 <div class="card-body">
915 <h5>{i18n.t("subscribed")}</h5>
916 <ul class="list-unstyled mb-0">
917 {this.state.personRes.follows.map(cfv => (
919 <CommunityLink community={cfv.community} />
930 updateUrl(paramUpdates: UrlParams) {
931 const page = paramUpdates.page || this.state.page;
932 const viewStr = paramUpdates.view || PersonDetailsView[this.state.view];
933 const sortStr = paramUpdates.sort || this.state.sort;
935 let typeView = this.state.userName
936 ? `/u/${this.state.userName}`
937 : `/user/${this.state.personId}`;
939 this.props.history.push(
940 `${typeView}/view/${viewStr}/sort/${sortStr}/page/${page}`
942 this.state.loading = true;
943 this.setState(this.state);
944 this.fetchUserData();
947 handlePageChange(page: number) {
948 this.updateUrl({ page });
951 handleSortChange(val: SortType) {
952 this.updateUrl({ sort: val, page: 1 });
955 handleViewChange(i: Person, event: any) {
957 view: PersonDetailsView[Number(event.target.value)],
962 handleUserSettingsShowNsfwChange(i: Person, event: any) {
963 i.state.saveUserSettingsForm.show_nsfw = event.target.checked;
967 handleUserSettingsShowAvatarsChange(i: Person, event: any) {
968 i.state.saveUserSettingsForm.show_avatars = event.target.checked;
969 UserService.Instance.localUserView.local_user.show_avatars =
970 event.target.checked; // Just for instant updates
974 handleUserSettingsShowScoresChange(i: Person, event: any) {
975 i.state.saveUserSettingsForm.show_scores = event.target.checked;
976 UserService.Instance.localUserView.local_user.show_scores =
977 event.target.checked; // Just for instant updates
981 handleUserSettingsSendNotificationsToEmailChange(i: Person, event: any) {
982 i.state.saveUserSettingsForm.send_notifications_to_email =
983 event.target.checked;
987 handleUserSettingsThemeChange(i: Person, event: any) {
988 i.state.saveUserSettingsForm.theme = event.target.value;
989 setTheme(event.target.value, true);
993 handleUserSettingsLangChange(i: Person, event: any) {
994 i.state.saveUserSettingsForm.lang = event.target.value;
995 i18n.changeLanguage(getLanguage(i.state.saveUserSettingsForm.lang));
999 handleUserSettingsSortTypeChange(val: SortType) {
1000 this.state.saveUserSettingsForm.default_sort_type = Object.keys(
1003 this.setState(this.state);
1006 handleUserSettingsListingTypeChange(val: ListingType) {
1007 this.state.saveUserSettingsForm.default_listing_type = Object.keys(
1010 this.setState(this.state);
1013 handleUserSettingsEmailChange(i: Person, event: any) {
1014 i.state.saveUserSettingsForm.email = event.target.value;
1015 i.setState(i.state);
1018 handleUserSettingsBioChange(val: string) {
1019 this.state.saveUserSettingsForm.bio = val;
1020 this.setState(this.state);
1023 handleAvatarUpload(url: string) {
1024 this.state.saveUserSettingsForm.avatar = url;
1025 this.setState(this.state);
1028 handleAvatarRemove() {
1029 this.state.saveUserSettingsForm.avatar = "";
1030 this.setState(this.state);
1033 handleBannerUpload(url: string) {
1034 this.state.saveUserSettingsForm.banner = url;
1035 this.setState(this.state);
1038 handleBannerRemove() {
1039 this.state.saveUserSettingsForm.banner = "";
1040 this.setState(this.state);
1043 handleUserSettingsPreferredUsernameChange(i: Person, event: any) {
1044 i.state.saveUserSettingsForm.display_name = event.target.value;
1045 i.setState(i.state);
1048 handleUserSettingsMatrixUserIdChange(i: Person, event: any) {
1049 i.state.saveUserSettingsForm.matrix_user_id = event.target.value;
1051 i.state.saveUserSettingsForm.matrix_user_id == "" &&
1052 !UserService.Instance.localUserView.person.matrix_user_id
1054 i.state.saveUserSettingsForm.matrix_user_id = undefined;
1056 i.setState(i.state);
1059 handleNewPasswordChange(i: Person, event: any) {
1060 i.state.changePasswordForm.new_password = event.target.value;
1061 if (i.state.changePasswordForm.new_password == "") {
1062 i.state.changePasswordForm.new_password = undefined;
1064 i.setState(i.state);
1067 handleNewPasswordVerifyChange(i: Person, event: any) {
1068 i.state.changePasswordForm.new_password_verify = event.target.value;
1069 if (i.state.changePasswordForm.new_password_verify == "") {
1070 i.state.changePasswordForm.new_password_verify = undefined;
1072 i.setState(i.state);
1075 handleOldPasswordChange(i: Person, event: any) {
1076 i.state.changePasswordForm.old_password = event.target.value;
1077 if (i.state.changePasswordForm.old_password == "") {
1078 i.state.changePasswordForm.old_password = undefined;
1080 i.setState(i.state);
1083 handleSaveUserSettingsSubmit(i: Person, event: any) {
1084 event.preventDefault();
1085 i.state.saveUserSettingsLoading = true;
1086 i.setState(i.state);
1088 WebSocketService.Instance.send(
1089 wsClient.saveUserSettings(i.state.saveUserSettingsForm)
1093 handleChangePasswordSubmit(i: Person, event: any) {
1094 event.preventDefault();
1095 i.state.changePasswordLoading = true;
1096 i.setState(i.state);
1098 WebSocketService.Instance.send(
1099 wsClient.changePassword(i.state.changePasswordForm)
1103 handleDeleteAccountShowConfirmToggle(i: Person, event: any) {
1104 event.preventDefault();
1105 i.state.deleteAccountShowConfirm = !i.state.deleteAccountShowConfirm;
1106 i.setState(i.state);
1109 handleDeleteAccountPasswordChange(i: Person, event: any) {
1110 i.state.deleteAccountForm.password = event.target.value;
1111 i.setState(i.state);
1114 handleLogoutClick(i: Person) {
1115 UserService.Instance.logout();
1116 i.context.router.history.push("/");
1119 handleDeleteAccount(i: Person, event: any) {
1120 event.preventDefault();
1121 i.state.deleteAccountLoading = true;
1122 i.setState(i.state);
1124 WebSocketService.Instance.send(
1125 wsClient.deleteAccount(i.state.deleteAccountForm)
1127 i.handleLogoutClick(i);
1131 if (this.isCurrentUser) {
1132 this.state.saveUserSettingsForm.show_nsfw =
1133 UserService.Instance.localUserView.local_user.show_nsfw;
1134 this.state.saveUserSettingsForm.theme = UserService.Instance.localUserView
1136 ? UserService.Instance.localUserView.local_user.theme
1138 this.state.saveUserSettingsForm.default_sort_type =
1139 UserService.Instance.localUserView.local_user.default_sort_type;
1140 this.state.saveUserSettingsForm.default_listing_type =
1141 UserService.Instance.localUserView.local_user.default_listing_type;
1142 this.state.saveUserSettingsForm.lang =
1143 UserService.Instance.localUserView.local_user.lang;
1144 this.state.saveUserSettingsForm.avatar =
1145 UserService.Instance.localUserView.person.avatar;
1146 this.state.saveUserSettingsForm.banner =
1147 UserService.Instance.localUserView.person.banner;
1148 this.state.saveUserSettingsForm.display_name =
1149 UserService.Instance.localUserView.person.display_name;
1150 this.state.saveUserSettingsForm.show_avatars =
1151 UserService.Instance.localUserView.local_user.show_avatars;
1152 this.state.saveUserSettingsForm.show_scores =
1153 UserService.Instance.localUserView.local_user.show_scores;
1154 this.state.saveUserSettingsForm.email =
1155 UserService.Instance.localUserView.local_user.email;
1156 this.state.saveUserSettingsForm.bio =
1157 UserService.Instance.localUserView.person.bio;
1158 this.state.saveUserSettingsForm.send_notifications_to_email =
1159 UserService.Instance.localUserView.local_user.send_notifications_to_email;
1160 this.state.saveUserSettingsForm.matrix_user_id =
1161 UserService.Instance.localUserView.person.matrix_user_id;
1165 parseMessage(msg: any) {
1166 let op = wsUserOp(msg);
1169 toast(i18n.t(msg.error), "danger");
1170 if (msg.error == "couldnt_find_that_username_or_email") {
1171 this.context.router.history.push("/");
1174 deleteAccountLoading: false,
1175 saveUserSettingsLoading: false,
1176 changePasswordLoading: false,
1179 } else if (msg.reconnect) {
1180 this.fetchUserData();
1181 } else if (op == UserOperation.GetPersonDetails) {
1182 // Since the PersonDetails contains posts/comments as well as some general user info we listen here as well
1183 // and set the parent state if it is not set or differs
1184 // TODO this might need to get abstracted
1185 let data = wsJsonToRes<GetPersonDetailsResponse>(msg).data;
1186 this.state.personRes = data;
1189 this.state.loading = false;
1190 this.setState(this.state);
1191 restoreScrollPosition(this.context);
1192 } else if (op == UserOperation.SaveUserSettings) {
1193 let data = wsJsonToRes<LoginResponse>(msg).data;
1194 UserService.Instance.login(data);
1195 this.state.personRes.person_view.person.bio = this.state.saveUserSettingsForm.bio;
1196 this.state.personRes.person_view.person.display_name = this.state.saveUserSettingsForm.display_name;
1197 this.state.personRes.person_view.person.banner = this.state.saveUserSettingsForm.banner;
1198 this.state.personRes.person_view.person.avatar = this.state.saveUserSettingsForm.avatar;
1199 this.state.saveUserSettingsLoading = false;
1200 this.setState(this.state);
1202 window.scrollTo(0, 0);
1203 } else if (op == UserOperation.ChangePassword) {
1204 let data = wsJsonToRes<LoginResponse>(msg).data;
1205 UserService.Instance.login(data);
1206 this.state.changePasswordLoading = false;
1207 this.setState(this.state);
1208 window.scrollTo(0, 0);
1209 toast(i18n.t("password_changed"));
1210 } else if (op == UserOperation.DeleteAccount) {
1212 deleteAccountLoading: false,
1213 deleteAccountShowConfirm: false,
1215 this.context.router.history.push("/");
1216 } else if (op == UserOperation.AddAdmin) {
1217 let data = wsJsonToRes<AddAdminResponse>(msg).data;
1218 this.state.siteRes.admins = data.admins;
1219 this.setState(this.state);
1220 } else if (op == UserOperation.CreateCommentLike) {
1221 let data = wsJsonToRes<CommentResponse>(msg).data;
1222 createCommentLikeRes(data.comment_view, this.state.personRes.comments);
1223 this.setState(this.state);
1225 op == UserOperation.EditComment ||
1226 op == UserOperation.DeleteComment ||
1227 op == UserOperation.RemoveComment
1229 let data = wsJsonToRes<CommentResponse>(msg).data;
1230 editCommentRes(data.comment_view, this.state.personRes.comments);
1231 this.setState(this.state);
1232 } else if (op == UserOperation.CreateComment) {
1233 let data = wsJsonToRes<CommentResponse>(msg).data;
1235 UserService.Instance.localUserView &&
1236 data.comment_view.creator.id ==
1237 UserService.Instance.localUserView.person.id
1239 toast(i18n.t("reply_sent"));
1241 } else if (op == UserOperation.SaveComment) {
1242 let data = wsJsonToRes<CommentResponse>(msg).data;
1243 saveCommentRes(data.comment_view, this.state.personRes.comments);
1244 this.setState(this.state);
1246 op == UserOperation.EditPost ||
1247 op == UserOperation.DeletePost ||
1248 op == UserOperation.RemovePost ||
1249 op == UserOperation.LockPost ||
1250 op == UserOperation.StickyPost ||
1251 op == UserOperation.SavePost
1253 let data = wsJsonToRes<PostResponse>(msg).data;
1254 editPostFindRes(data.post_view, this.state.personRes.posts);
1255 this.setState(this.state);
1256 } else if (op == UserOperation.CreatePostLike) {
1257 let data = wsJsonToRes<PostResponse>(msg).data;
1258 createPostLikeFindRes(data.post_view, this.state.personRes.posts);
1259 this.setState(this.state);
1260 } else if (op == UserOperation.BanPerson) {
1261 let data = wsJsonToRes<BanPersonResponse>(msg).data;
1262 this.state.personRes.comments
1263 .filter(c => c.creator.id == data.person_view.person.id)
1264 .forEach(c => (c.creator.banned = data.banned));
1265 this.state.personRes.posts
1266 .filter(c => c.creator.id == data.person_view.person.id)
1267 .forEach(c => (c.creator.banned = data.banned));
1268 this.setState(this.state);