1 import { Component, linkEvent } from "inferno";
2 import { Link } from "inferno-router";
3 import ISO6391 from "iso-639-1";
11 GetPersonDetailsResponse,
19 } from "lemmy-js-client";
20 import moment from "moment";
21 import { Subscription } from "rxjs";
22 import { i18n } from "../../i18next";
23 import { InitialFetchRequest, PersonDetailsView } from "../../interfaces";
24 import { UserService, WebSocketService } from "../../services";
27 capitalizeFirstLetter,
29 createPostLikeFindRes,
39 restoreScrollPosition,
55 import { BannerIconHeader } from "../common/banner-icon-header";
56 import { HtmlTags } from "../common/html-tags";
57 import { Icon, Spinner } from "../common/icon";
58 import { ImageUploadForm } from "../common/image-upload-form";
59 import { ListingTypeSelect } from "../common/listing-type-select";
60 import { MarkdownTextArea } from "../common/markdown-textarea";
61 import { MomentTime } from "../common/moment-time";
62 import { SortSelect } from "../common/sort-select";
63 import { CommunityLink } from "../community/community-link";
64 import { PersonDetails } from "./person-details";
65 import { PersonListing } from "./person-listing";
67 interface PersonState {
68 personRes: GetPersonDetailsResponse;
70 view: PersonDetailsView;
74 saveUserSettingsForm: SaveUserSettings;
75 changePasswordForm: ChangePassword;
76 saveUserSettingsLoading: boolean;
77 changePasswordLoading: boolean;
78 deleteAccountLoading: boolean;
79 deleteAccountShowConfirm: boolean;
80 deleteAccountForm: DeleteAccount;
81 siteRes: GetSiteResponse;
84 interface PersonProps {
85 view: PersonDetailsView;
88 person_id: number | null;
98 export class Person extends Component<any, PersonState> {
99 private isoData = setIsoData(this.context);
100 private subscription: Subscription;
101 private emptyState: PersonState = {
102 personRes: undefined,
103 userName: getUsernameFromProps(this.props),
105 view: Person.getViewFromProps(this.props.match.view),
106 sort: Person.getSortTypeFromProps(this.props.match.sort),
107 page: Person.getPageFromProps(this.props.match.page),
108 saveUserSettingsForm: {
109 auth: authField(false),
111 changePasswordForm: {
113 new_password_verify: null,
115 auth: authField(false),
117 saveUserSettingsLoading: null,
118 changePasswordLoading: false,
119 deleteAccountLoading: null,
120 deleteAccountShowConfirm: false,
123 auth: authField(false),
125 siteRes: this.isoData.site_res,
128 constructor(props: any, context: any) {
129 super(props, context);
131 this.state = this.emptyState;
132 this.handleSortChange = this.handleSortChange.bind(this);
133 this.handleUserSettingsSortTypeChange =
134 this.handleUserSettingsSortTypeChange.bind(this);
135 this.handleUserSettingsListingTypeChange =
136 this.handleUserSettingsListingTypeChange.bind(this);
137 this.handlePageChange = this.handlePageChange.bind(this);
138 this.handleUserSettingsBioChange =
139 this.handleUserSettingsBioChange.bind(this);
141 this.handleAvatarUpload = this.handleAvatarUpload.bind(this);
142 this.handleAvatarRemove = this.handleAvatarRemove.bind(this);
144 this.handleBannerUpload = this.handleBannerUpload.bind(this);
145 this.handleBannerRemove = this.handleBannerRemove.bind(this);
147 this.parseMessage = this.parseMessage.bind(this);
148 this.subscription = wsSubscribe(this.parseMessage);
150 // Only fetch the data if coming from another route
151 if (this.isoData.path == this.context.router.route.match.url) {
152 this.state.personRes = this.isoData.routeData[0];
154 this.state.loading = false;
156 this.fetchUserData();
163 let form: GetPersonDetails = {
164 username: this.state.userName,
165 sort: this.state.sort,
166 saved_only: this.state.view === PersonDetailsView.Saved,
167 page: this.state.page,
169 auth: authField(false),
171 WebSocketService.Instance.send(wsClient.getPersonDetails(form));
174 get isCurrentUser() {
176 UserService.Instance.localUserView?.person.id ==
177 this.state.personRes.person_view.person.id
181 static getViewFromProps(view: string): PersonDetailsView {
182 return view ? PersonDetailsView[view] : PersonDetailsView.Overview;
185 static getSortTypeFromProps(sort: string): SortType {
186 return sort ? routeSortTypeToEnum(sort) : SortType.New;
189 static getPageFromProps(page: number): number {
190 return page ? Number(page) : 1;
193 static fetchInitialData(req: InitialFetchRequest): Promise<any>[] {
194 let pathSplit = req.path.split("/");
195 let promises: Promise<any>[] = [];
197 // It can be /u/me, or /username/1
198 let idOrName = pathSplit[2];
199 let person_id: number;
200 let username: string;
201 if (isNaN(Number(idOrName))) {
204 person_id = Number(idOrName);
207 let view = this.getViewFromProps(pathSplit[4]);
208 let sort = this.getSortTypeFromProps(pathSplit[6]);
209 let page = this.getPageFromProps(Number(pathSplit[8]));
211 let form: GetPersonDetails = {
213 saved_only: view === PersonDetailsView.Saved,
217 setOptionalAuth(form, req.auth);
218 this.setIdOrName(form, person_id, username);
219 promises.push(req.client.getPersonDetails(form));
223 static setIdOrName(obj: any, id: number, name_: string) {
227 obj.username = name_;
231 componentWillUnmount() {
232 this.subscription.unsubscribe();
233 saveScrollPosition(this.context);
236 static getDerivedStateFromProps(props: any): PersonProps {
238 view: this.getViewFromProps(props.match.params.view),
239 sort: this.getSortTypeFromProps(props.match.params.sort),
240 page: this.getPageFromProps(props.match.params.page),
241 person_id: Number(props.match.params.id) || null,
242 username: props.match.params.username,
246 componentDidUpdate(lastProps: any) {
247 // Necessary if you are on a post and you click another post (same route)
249 lastProps.location.pathname.split("/")[2] !==
250 lastProps.history.location.pathname.split("/")[2]
252 // Couldnt get a refresh working. This does for now.
257 get documentTitle(): string {
258 return `@${this.state.personRes.person_view.person.name} - ${this.state.siteRes.site_view.site.name}`;
261 get bioTag(): string {
262 return this.state.personRes.person_view.person.bio
263 ? previewLines(this.state.personRes.person_view.person.bio)
269 <div class="container">
270 {this.state.loading ? (
276 <div class="col-12 col-md-8">
279 title={this.documentTitle}
280 path={this.context.router.route.match.url}
281 description={this.bioTag}
282 image={this.state.personRes.person_view.person.avatar}
287 {!this.state.loading && this.selects()}
289 personRes={this.state.personRes}
290 admins={this.state.siteRes.admins}
291 sort={this.state.sort}
292 page={this.state.page}
295 this.state.siteRes.site_view.site.enable_downvotes
297 enableNsfw={this.state.siteRes.site_view.site.enable_nsfw}
298 view={this.state.view}
299 onPageChange={this.handlePageChange}
303 {!this.state.loading && (
304 <div class="col-12 col-md-4">
305 {this.isCurrentUser && this.userSettings()}
318 <div class="btn-group btn-group-toggle flex-wrap mb-2">
320 className={`btn btn-outline-secondary pointer
321 ${this.state.view == PersonDetailsView.Overview && "active"}
326 value={PersonDetailsView.Overview}
327 checked={this.state.view === PersonDetailsView.Overview}
328 onChange={linkEvent(this, this.handleViewChange)}
333 className={`btn btn-outline-secondary pointer
334 ${this.state.view == PersonDetailsView.Comments && "active"}
339 value={PersonDetailsView.Comments}
340 checked={this.state.view == PersonDetailsView.Comments}
341 onChange={linkEvent(this, this.handleViewChange)}
346 className={`btn btn-outline-secondary pointer
347 ${this.state.view == PersonDetailsView.Posts && "active"}
352 value={PersonDetailsView.Posts}
353 checked={this.state.view == PersonDetailsView.Posts}
354 onChange={linkEvent(this, this.handleViewChange)}
359 className={`btn btn-outline-secondary pointer
360 ${this.state.view == PersonDetailsView.Saved && "active"}
365 value={PersonDetailsView.Saved}
366 checked={this.state.view == PersonDetailsView.Saved}
367 onChange={linkEvent(this, this.handleViewChange)}
377 <div className="mb-2">
378 <span class="mr-3">{this.viewRadios()}</span>
380 sort={this.state.sort}
381 onChange={this.handleSortChange}
386 href={`/feeds/u/${this.state.userName}.xml?sort=${this.state.sort}`}
390 <Icon icon="rss" classes="text-muted small mx-2" />
397 let pv = this.state.personRes?.person_view;
401 <BannerIconHeader banner={pv.person.banner} icon={pv.person.avatar} />
404 <div class="mb-0 d-flex flex-wrap">
406 {pv.person.display_name && (
407 <h5 class="mb-0">{pv.person.display_name}</h5>
409 <ul class="list-inline mb-2">
410 <li className="list-inline-item">
419 {pv.person.banned && (
420 <li className="list-inline-item badge badge-danger">
426 <div className="flex-grow-1 unselectable pointer mx-2"></div>
427 {this.isCurrentUser ? (
429 class="d-flex align-self-start btn btn-secondary mr-2"
430 onClick={linkEvent(this, this.handleLogoutClick)}
437 className={`d-flex align-self-start btn btn-secondary mr-2 ${
438 !pv.person.matrix_user_id && "invisible"
441 href={`https://matrix.to/#/${pv.person.matrix_user_id}`}
443 {i18n.t("send_secure_message")}
446 className={"d-flex align-self-start btn btn-secondary"}
447 to={`/create_private_message/recipient/${pv.person.id}`}
449 {i18n.t("send_message")}
455 <div className="d-flex align-items-center mb-2">
458 dangerouslySetInnerHTML={mdToHtml(pv.person.bio)}
463 <ul class="list-inline mb-2">
464 <li className="list-inline-item badge badge-light">
465 {i18n.t("number_of_posts", { count: pv.counts.post_count })}
467 <li className="list-inline-item badge badge-light">
468 {i18n.t("number_of_comments", {
469 count: pv.counts.comment_count,
474 <div class="text-muted">
475 {i18n.t("joined")}{" "}
476 <MomentTime data={pv.person} showAgo ignoreUpdated />
478 <div className="d-flex align-items-center text-muted mb-2">
480 <span className="ml-2">
481 {i18n.t("cake_day_title")}{" "}
482 {moment.utc(pv.person.published).local().format("MMM DD, YYYY")}
494 <div class="card border-secondary mb-3">
495 <div class="card-body">
496 {this.saveUserSettingsHtmlForm()}
498 {this.changePasswordHtmlForm()}
505 changePasswordHtmlForm() {
508 <h5>{i18n.t("change_password")}</h5>
509 <form onSubmit={linkEvent(this, this.handleChangePasswordSubmit)}>
510 <div class="form-group row">
511 <label class="col-lg-5 col-form-label" htmlFor="user-password">
512 {i18n.t("new_password")}
514 <div class="col-lg-7">
519 value={this.state.changePasswordForm.new_password}
520 autoComplete="new-password"
522 onInput={linkEvent(this, this.handleNewPasswordChange)}
526 <div class="form-group row">
528 class="col-lg-5 col-form-label"
529 htmlFor="user-verify-password"
531 {i18n.t("verify_password")}
533 <div class="col-lg-7">
536 id="user-verify-password"
538 value={this.state.changePasswordForm.new_password_verify}
539 autoComplete="new-password"
541 onInput={linkEvent(this, this.handleNewPasswordVerifyChange)}
545 <div class="form-group row">
546 <label class="col-lg-5 col-form-label" htmlFor="user-old-password">
547 {i18n.t("old_password")}
549 <div class="col-lg-7">
552 id="user-old-password"
554 value={this.state.changePasswordForm.old_password}
555 autoComplete="new-password"
557 onInput={linkEvent(this, this.handleOldPasswordChange)}
561 <div class="form-group">
562 <button type="submit" class="btn btn-block btn-secondary mr-4">
563 {this.state.changePasswordLoading ? (
566 capitalizeFirstLetter(i18n.t("save"))
575 saveUserSettingsHtmlForm() {
578 <h5>{i18n.t("settings")}</h5>
579 <form onSubmit={linkEvent(this, this.handleSaveUserSettingsSubmit)}>
580 <div class="form-group">
581 <label>{i18n.t("avatar")}</label>
583 uploadTitle={i18n.t("upload_avatar")}
584 imageSrc={this.state.saveUserSettingsForm.avatar}
585 onUpload={this.handleAvatarUpload}
586 onRemove={this.handleAvatarRemove}
590 <div class="form-group">
591 <label>{i18n.t("banner")}</label>
593 uploadTitle={i18n.t("upload_banner")}
594 imageSrc={this.state.saveUserSettingsForm.banner}
595 onUpload={this.handleBannerUpload}
596 onRemove={this.handleBannerRemove}
599 <div class="form-group">
600 <label htmlFor="user-language">{i18n.t("language")}</label>
603 value={this.state.saveUserSettingsForm.lang}
604 onChange={linkEvent(this, this.handleUserSettingsLangChange)}
605 class="ml-2 custom-select w-auto"
607 <option disabled aria-hidden="true">
610 <option value="browser">{i18n.t("browser_default")}</option>
611 <option disabled aria-hidden="true">
614 {languages.sort().map(lang => (
615 <option value={lang.code}>
616 {ISO6391.getNativeName(lang.code) || lang.code}
621 <div class="form-group">
622 <label htmlFor="user-theme">{i18n.t("theme")}</label>
625 value={this.state.saveUserSettingsForm.theme}
626 onChange={linkEvent(this, this.handleUserSettingsThemeChange)}
627 class="ml-2 custom-select w-auto"
629 <option disabled aria-hidden="true">
632 <option value="browser">{i18n.t("browser_default")}</option>
633 {themes.map(theme => (
634 <option value={theme}>{theme}</option>
638 <form className="form-group">
640 <div class="mr-2">{i18n.t("type")}</div>
644 Object.values(ListingType)[
645 this.state.saveUserSettingsForm.default_listing_type
648 showLocal={showLocal(this.isoData)}
649 onChange={this.handleUserSettingsListingTypeChange}
652 <form className="form-group">
654 <div class="mr-2">{i18n.t("sort_type")}</div>
658 Object.values(SortType)[
659 this.state.saveUserSettingsForm.default_sort_type
662 onChange={this.handleUserSettingsSortTypeChange}
665 <div class="form-group row">
666 <label class="col-lg-5 col-form-label" htmlFor="display-name">
667 {i18n.t("display_name")}
669 <div class="col-lg-7">
674 placeholder={i18n.t("optional")}
675 value={this.state.saveUserSettingsForm.display_name}
678 this.handleUserSettingsPreferredUsernameChange
680 pattern="^(?!@)(.+)$"
685 <div class="form-group row">
686 <label class="col-lg-3 col-form-label" htmlFor="user-bio">
689 <div class="col-lg-9">
691 initialContent={this.state.saveUserSettingsForm.bio}
692 onContentChange={this.handleUserSettingsBioChange}
694 hideNavigationWarnings
698 <div class="form-group row">
699 <label class="col-lg-3 col-form-label" htmlFor="user-email">
702 <div class="col-lg-9">
707 placeholder={i18n.t("optional")}
708 value={this.state.saveUserSettingsForm.email}
709 onInput={linkEvent(this, this.handleUserSettingsEmailChange)}
714 <div class="form-group row">
715 <label class="col-lg-5 col-form-label" htmlFor="matrix-user-id">
716 <a href={elementUrl} rel="noopener">
717 {i18n.t("matrix_user_id")}
720 <div class="col-lg-7">
725 placeholder="@user:example.com"
726 value={this.state.saveUserSettingsForm.matrix_user_id}
729 this.handleUserSettingsMatrixUserIdChange
731 pattern="^@[A-Za-z0-9._=-]+:[A-Za-z0-9.-]+\.[A-Za-z]{2,}$"
735 {this.state.siteRes.site_view.site.enable_nsfw && (
736 <div class="form-group">
737 <div class="form-check">
739 class="form-check-input"
742 checked={this.state.saveUserSettingsForm.show_nsfw}
745 this.handleUserSettingsShowNsfwChange
748 <label class="form-check-label" htmlFor="user-show-nsfw">
749 {i18n.t("show_nsfw")}
754 <div class="form-group">
755 <div class="form-check">
757 class="form-check-input"
758 id="user-show-scores"
760 checked={this.state.saveUserSettingsForm.show_scores}
763 this.handleUserSettingsShowScoresChange
766 <label class="form-check-label" htmlFor="user-show-scores">
767 {i18n.t("show_scores")}
771 <div class="form-group">
772 <div class="form-check">
774 class="form-check-input"
775 id="user-show-avatars"
777 checked={this.state.saveUserSettingsForm.show_avatars}
780 this.handleUserSettingsShowAvatarsChange
783 <label class="form-check-label" htmlFor="user-show-avatars">
784 {i18n.t("show_avatars")}
788 <div class="form-group">
789 <div class="form-check">
791 class="form-check-input"
792 id="user-bot-account"
794 checked={this.state.saveUserSettingsForm.bot_account}
795 onChange={linkEvent(this, this.handleUserSettingsBotAccount)}
797 <label class="form-check-label" htmlFor="user-bot-account">
798 {i18n.t("bot_account")}
802 <div class="form-group">
803 <div class="form-check">
805 class="form-check-input"
806 id="user-show-bot-accounts"
808 checked={this.state.saveUserSettingsForm.show_bot_accounts}
811 this.handleUserSettingsShowBotAccounts
814 <label class="form-check-label" htmlFor="user-show-bot-accounts">
815 {i18n.t("show_bot_accounts")}
819 <div class="form-group">
820 <div class="form-check">
822 class="form-check-input"
823 id="user-show-read-posts"
825 checked={this.state.saveUserSettingsForm.show_read_posts}
826 onChange={linkEvent(this, this.handleUserSettingsShowReadPosts)}
828 <label class="form-check-label" htmlFor="user-show-read-posts">
829 {i18n.t("show_read_posts")}
833 <div class="form-group">
834 <div class="form-check">
836 class="form-check-input"
837 id="user-send-notifications-to-email"
839 disabled={!this.state.saveUserSettingsForm.email}
841 this.state.saveUserSettingsForm.send_notifications_to_email
845 this.handleUserSettingsSendNotificationsToEmailChange
849 class="form-check-label"
850 htmlFor="user-send-notifications-to-email"
852 {i18n.t("send_notifications_to_email")}
856 <div class="form-group">
857 <button type="submit" class="btn btn-block btn-secondary mr-4">
858 {this.state.saveUserSettingsLoading ? (
861 capitalizeFirstLetter(i18n.t("save"))
866 <div class="form-group">
868 class="btn btn-block btn-danger"
871 this.handleDeleteAccountShowConfirmToggle
874 {i18n.t("delete_account")}
876 {this.state.deleteAccountShowConfirm && (
878 <div class="my-2 alert alert-danger" role="alert">
879 {i18n.t("delete_account_confirm")}
883 value={this.state.deleteAccountForm.password}
884 autoComplete="new-password"
888 this.handleDeleteAccountPasswordChange
890 class="form-control my-2"
893 class="btn btn-danger mr-4"
894 disabled={!this.state.deleteAccountForm.password}
895 onClick={linkEvent(this, this.handleDeleteAccount)}
897 {this.state.deleteAccountLoading ? (
900 capitalizeFirstLetter(i18n.t("delete"))
904 class="btn btn-secondary"
907 this.handleDeleteAccountShowConfirmToggle
923 {this.state.personRes.moderates.length > 0 && (
924 <div class="card border-secondary mb-3">
925 <div class="card-body">
926 <h5>{i18n.t("moderates")}</h5>
927 <ul class="list-unstyled mb-0">
928 {this.state.personRes.moderates.map(cmv => (
930 <CommunityLink community={cmv.community} />
944 {this.state.personRes.follows.length > 0 && (
945 <div class="card border-secondary mb-3">
946 <div class="card-body">
947 <h5>{i18n.t("subscribed")}</h5>
948 <ul class="list-unstyled mb-0">
949 {this.state.personRes.follows.map(cfv => (
951 <CommunityLink community={cfv.community} />
962 updateUrl(paramUpdates: UrlParams) {
963 const page = paramUpdates.page || this.state.page;
964 const viewStr = paramUpdates.view || PersonDetailsView[this.state.view];
965 const sortStr = paramUpdates.sort || this.state.sort;
967 let typeView = `/u/${this.state.userName}`;
969 this.props.history.push(
970 `${typeView}/view/${viewStr}/sort/${sortStr}/page/${page}`
972 this.state.loading = true;
973 this.setState(this.state);
974 this.fetchUserData();
977 handlePageChange(page: number) {
978 this.updateUrl({ page });
981 handleSortChange(val: SortType) {
982 this.updateUrl({ sort: val, page: 1 });
985 handleViewChange(i: Person, event: any) {
987 view: PersonDetailsView[Number(event.target.value)],
992 handleUserSettingsShowNsfwChange(i: Person, event: any) {
993 i.state.saveUserSettingsForm.show_nsfw = event.target.checked;
997 handleUserSettingsShowAvatarsChange(i: Person, event: any) {
998 i.state.saveUserSettingsForm.show_avatars = event.target.checked;
999 UserService.Instance.localUserView.local_user.show_avatars =
1000 event.target.checked; // Just for instant updates
1001 i.setState(i.state);
1004 handleUserSettingsBotAccount(i: Person, event: any) {
1005 i.state.saveUserSettingsForm.bot_account = event.target.checked;
1006 i.setState(i.state);
1009 handleUserSettingsShowBotAccounts(i: Person, event: any) {
1010 i.state.saveUserSettingsForm.show_bot_accounts = event.target.checked;
1011 i.setState(i.state);
1014 handleUserSettingsShowReadPosts(i: Person, event: any) {
1015 i.state.saveUserSettingsForm.show_read_posts = event.target.checked;
1016 i.setState(i.state);
1019 handleUserSettingsShowScoresChange(i: Person, event: any) {
1020 i.state.saveUserSettingsForm.show_scores = event.target.checked;
1021 UserService.Instance.localUserView.local_user.show_scores =
1022 event.target.checked; // Just for instant updates
1023 i.setState(i.state);
1026 handleUserSettingsSendNotificationsToEmailChange(i: Person, event: any) {
1027 i.state.saveUserSettingsForm.send_notifications_to_email =
1028 event.target.checked;
1029 i.setState(i.state);
1032 handleUserSettingsThemeChange(i: Person, event: any) {
1033 i.state.saveUserSettingsForm.theme = event.target.value;
1034 setTheme(event.target.value, true);
1035 i.setState(i.state);
1038 handleUserSettingsLangChange(i: Person, event: any) {
1039 i.state.saveUserSettingsForm.lang = event.target.value;
1040 i18n.changeLanguage(getLanguage(i.state.saveUserSettingsForm.lang));
1041 i.setState(i.state);
1044 handleUserSettingsSortTypeChange(val: SortType) {
1045 this.state.saveUserSettingsForm.default_sort_type =
1046 Object.keys(SortType).indexOf(val);
1047 this.setState(this.state);
1050 handleUserSettingsListingTypeChange(val: ListingType) {
1051 this.state.saveUserSettingsForm.default_listing_type =
1052 Object.keys(ListingType).indexOf(val);
1053 this.setState(this.state);
1056 handleUserSettingsEmailChange(i: Person, event: any) {
1057 i.state.saveUserSettingsForm.email = event.target.value;
1058 i.setState(i.state);
1061 handleUserSettingsBioChange(val: string) {
1062 this.state.saveUserSettingsForm.bio = val;
1063 this.setState(this.state);
1066 handleAvatarUpload(url: string) {
1067 this.state.saveUserSettingsForm.avatar = url;
1068 this.setState(this.state);
1071 handleAvatarRemove() {
1072 this.state.saveUserSettingsForm.avatar = "";
1073 this.setState(this.state);
1076 handleBannerUpload(url: string) {
1077 this.state.saveUserSettingsForm.banner = url;
1078 this.setState(this.state);
1081 handleBannerRemove() {
1082 this.state.saveUserSettingsForm.banner = "";
1083 this.setState(this.state);
1086 handleUserSettingsPreferredUsernameChange(i: Person, event: any) {
1087 i.state.saveUserSettingsForm.display_name = event.target.value;
1088 i.setState(i.state);
1091 handleUserSettingsMatrixUserIdChange(i: Person, event: any) {
1092 i.state.saveUserSettingsForm.matrix_user_id = event.target.value;
1094 i.state.saveUserSettingsForm.matrix_user_id == "" &&
1095 !UserService.Instance.localUserView.person.matrix_user_id
1097 i.state.saveUserSettingsForm.matrix_user_id = undefined;
1099 i.setState(i.state);
1102 handleNewPasswordChange(i: Person, event: any) {
1103 i.state.changePasswordForm.new_password = event.target.value;
1104 if (i.state.changePasswordForm.new_password == "") {
1105 i.state.changePasswordForm.new_password = undefined;
1107 i.setState(i.state);
1110 handleNewPasswordVerifyChange(i: Person, event: any) {
1111 i.state.changePasswordForm.new_password_verify = event.target.value;
1112 if (i.state.changePasswordForm.new_password_verify == "") {
1113 i.state.changePasswordForm.new_password_verify = undefined;
1115 i.setState(i.state);
1118 handleOldPasswordChange(i: Person, event: any) {
1119 i.state.changePasswordForm.old_password = event.target.value;
1120 if (i.state.changePasswordForm.old_password == "") {
1121 i.state.changePasswordForm.old_password = undefined;
1123 i.setState(i.state);
1126 handleSaveUserSettingsSubmit(i: Person, event: any) {
1127 event.preventDefault();
1128 i.state.saveUserSettingsLoading = true;
1129 i.setState(i.state);
1131 WebSocketService.Instance.send(
1132 wsClient.saveUserSettings(i.state.saveUserSettingsForm)
1136 handleChangePasswordSubmit(i: Person, event: any) {
1137 event.preventDefault();
1138 i.state.changePasswordLoading = true;
1139 i.setState(i.state);
1141 WebSocketService.Instance.send(
1142 wsClient.changePassword(i.state.changePasswordForm)
1146 handleDeleteAccountShowConfirmToggle(i: Person, event: any) {
1147 event.preventDefault();
1148 i.state.deleteAccountShowConfirm = !i.state.deleteAccountShowConfirm;
1149 i.setState(i.state);
1152 handleDeleteAccountPasswordChange(i: Person, event: any) {
1153 i.state.deleteAccountForm.password = event.target.value;
1154 i.setState(i.state);
1157 handleLogoutClick(i: Person) {
1158 UserService.Instance.logout();
1159 i.context.router.history.push("/");
1162 handleDeleteAccount(i: Person, event: any) {
1163 event.preventDefault();
1164 i.state.deleteAccountLoading = true;
1165 i.setState(i.state);
1167 WebSocketService.Instance.send(
1168 wsClient.deleteAccount(i.state.deleteAccountForm)
1173 if (this.isCurrentUser) {
1174 this.state.saveUserSettingsForm.show_nsfw =
1175 UserService.Instance.localUserView.local_user.show_nsfw;
1176 this.state.saveUserSettingsForm.theme = UserService.Instance.localUserView
1178 ? UserService.Instance.localUserView.local_user.theme
1180 this.state.saveUserSettingsForm.default_sort_type =
1181 UserService.Instance.localUserView.local_user.default_sort_type;
1182 this.state.saveUserSettingsForm.default_listing_type =
1183 UserService.Instance.localUserView.local_user.default_listing_type;
1184 this.state.saveUserSettingsForm.lang =
1185 UserService.Instance.localUserView.local_user.lang;
1186 this.state.saveUserSettingsForm.avatar =
1187 UserService.Instance.localUserView.person.avatar;
1188 this.state.saveUserSettingsForm.banner =
1189 UserService.Instance.localUserView.person.banner;
1190 this.state.saveUserSettingsForm.display_name =
1191 UserService.Instance.localUserView.person.display_name;
1192 this.state.saveUserSettingsForm.show_avatars =
1193 UserService.Instance.localUserView.local_user.show_avatars;
1194 this.state.saveUserSettingsForm.bot_account =
1195 UserService.Instance.localUserView.person.bot_account;
1196 this.state.saveUserSettingsForm.show_bot_accounts =
1197 UserService.Instance.localUserView.local_user.show_bot_accounts;
1198 this.state.saveUserSettingsForm.show_scores =
1199 UserService.Instance.localUserView.local_user.show_scores;
1200 this.state.saveUserSettingsForm.show_read_posts =
1201 UserService.Instance.localUserView.local_user.show_read_posts;
1202 this.state.saveUserSettingsForm.email =
1203 UserService.Instance.localUserView.local_user.email;
1204 this.state.saveUserSettingsForm.bio =
1205 UserService.Instance.localUserView.person.bio;
1206 this.state.saveUserSettingsForm.send_notifications_to_email =
1207 UserService.Instance.localUserView.local_user.send_notifications_to_email;
1208 this.state.saveUserSettingsForm.matrix_user_id =
1209 UserService.Instance.localUserView.person.matrix_user_id;
1213 parseMessage(msg: any) {
1214 let op = wsUserOp(msg);
1217 toast(i18n.t(msg.error), "danger");
1218 if (msg.error == "couldnt_find_that_username_or_email") {
1219 this.context.router.history.push("/");
1222 deleteAccountLoading: false,
1223 saveUserSettingsLoading: false,
1224 changePasswordLoading: false,
1227 } else if (msg.reconnect) {
1228 this.fetchUserData();
1229 } else if (op == UserOperation.GetPersonDetails) {
1230 // Since the PersonDetails contains posts/comments as well as some general user info we listen here as well
1231 // and set the parent state if it is not set or differs
1232 // TODO this might need to get abstracted
1233 let data = wsJsonToRes<GetPersonDetailsResponse>(msg).data;
1234 this.state.personRes = data;
1237 this.state.loading = false;
1238 this.setState(this.state);
1239 restoreScrollPosition(this.context);
1240 } else if (op == UserOperation.SaveUserSettings) {
1241 let data = wsJsonToRes<LoginResponse>(msg).data;
1242 UserService.Instance.login(data);
1243 this.state.personRes.person_view.person.bio =
1244 this.state.saveUserSettingsForm.bio;
1245 this.state.personRes.person_view.person.display_name =
1246 this.state.saveUserSettingsForm.display_name;
1247 this.state.personRes.person_view.person.banner =
1248 this.state.saveUserSettingsForm.banner;
1249 this.state.personRes.person_view.person.avatar =
1250 this.state.saveUserSettingsForm.avatar;
1251 this.state.saveUserSettingsLoading = false;
1252 this.setState(this.state);
1254 window.scrollTo(0, 0);
1255 } else if (op == UserOperation.ChangePassword) {
1256 let data = wsJsonToRes<LoginResponse>(msg).data;
1257 UserService.Instance.login(data);
1258 this.state.changePasswordLoading = false;
1259 this.setState(this.state);
1260 window.scrollTo(0, 0);
1261 toast(i18n.t("password_changed"));
1262 } else if (op == UserOperation.DeleteAccount) {
1264 deleteAccountLoading: false,
1265 deleteAccountShowConfirm: false,
1267 UserService.Instance.logout();
1268 window.location.href = "/";
1269 } else if (op == UserOperation.AddAdmin) {
1270 let data = wsJsonToRes<AddAdminResponse>(msg).data;
1271 this.state.siteRes.admins = data.admins;
1272 this.setState(this.state);
1273 } else if (op == UserOperation.CreateCommentLike) {
1274 let data = wsJsonToRes<CommentResponse>(msg).data;
1275 createCommentLikeRes(data.comment_view, this.state.personRes.comments);
1276 this.setState(this.state);
1278 op == UserOperation.EditComment ||
1279 op == UserOperation.DeleteComment ||
1280 op == UserOperation.RemoveComment
1282 let data = wsJsonToRes<CommentResponse>(msg).data;
1283 editCommentRes(data.comment_view, this.state.personRes.comments);
1284 this.setState(this.state);
1285 } else if (op == UserOperation.CreateComment) {
1286 let data = wsJsonToRes<CommentResponse>(msg).data;
1288 UserService.Instance.localUserView &&
1289 data.comment_view.creator.id ==
1290 UserService.Instance.localUserView.person.id
1292 toast(i18n.t("reply_sent"));
1294 } else if (op == UserOperation.SaveComment) {
1295 let data = wsJsonToRes<CommentResponse>(msg).data;
1296 saveCommentRes(data.comment_view, this.state.personRes.comments);
1297 this.setState(this.state);
1299 op == UserOperation.EditPost ||
1300 op == UserOperation.DeletePost ||
1301 op == UserOperation.RemovePost ||
1302 op == UserOperation.LockPost ||
1303 op == UserOperation.StickyPost ||
1304 op == UserOperation.SavePost
1306 let data = wsJsonToRes<PostResponse>(msg).data;
1307 editPostFindRes(data.post_view, this.state.personRes.posts);
1308 this.setState(this.state);
1309 } else if (op == UserOperation.CreatePostLike) {
1310 let data = wsJsonToRes<PostResponse>(msg).data;
1311 createPostLikeFindRes(data.post_view, this.state.personRes.posts);
1312 this.setState(this.state);
1313 } else if (op == UserOperation.BanPerson) {
1314 let data = wsJsonToRes<BanPersonResponse>(msg).data;
1315 this.state.personRes.comments
1316 .filter(c => c.creator.id == data.person_view.person.id)
1317 .forEach(c => (c.creator.banned = data.banned));
1318 this.state.personRes.posts
1319 .filter(c => c.creator.id == data.person_view.person.id)
1320 .forEach(c => (c.creator.banned = data.banned));
1321 this.setState(this.state);