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="^(?!@)(.+)$"
686 <div class="form-group row">
687 <label class="col-lg-3 col-form-label" htmlFor="user-bio">
690 <div class="col-lg-9">
692 initialContent={this.state.saveUserSettingsForm.bio}
693 onContentChange={this.handleUserSettingsBioChange}
695 hideNavigationWarnings
699 <div class="form-group row">
700 <label class="col-lg-3 col-form-label" htmlFor="user-email">
703 <div class="col-lg-9">
708 placeholder={i18n.t("optional")}
709 value={this.state.saveUserSettingsForm.email}
710 onInput={linkEvent(this, this.handleUserSettingsEmailChange)}
715 <div class="form-group row">
716 <label class="col-lg-5 col-form-label" htmlFor="matrix-user-id">
717 <a href={elementUrl} rel="noopener">
718 {i18n.t("matrix_user_id")}
721 <div class="col-lg-7">
726 placeholder="@user:example.com"
727 value={this.state.saveUserSettingsForm.matrix_user_id}
730 this.handleUserSettingsMatrixUserIdChange
732 pattern="^@[A-Za-z0-9._=-]+:[A-Za-z0-9.-]+\.[A-Za-z]{2,}$"
736 {this.state.siteRes.site_view.site.enable_nsfw && (
737 <div class="form-group">
738 <div class="form-check">
740 class="form-check-input"
743 checked={this.state.saveUserSettingsForm.show_nsfw}
746 this.handleUserSettingsShowNsfwChange
749 <label class="form-check-label" htmlFor="user-show-nsfw">
750 {i18n.t("show_nsfw")}
755 <div class="form-group">
756 <div class="form-check">
758 class="form-check-input"
759 id="user-show-scores"
761 checked={this.state.saveUserSettingsForm.show_scores}
764 this.handleUserSettingsShowScoresChange
767 <label class="form-check-label" htmlFor="user-show-scores">
768 {i18n.t("show_scores")}
772 <div class="form-group">
773 <div class="form-check">
775 class="form-check-input"
776 id="user-show-avatars"
778 checked={this.state.saveUserSettingsForm.show_avatars}
781 this.handleUserSettingsShowAvatarsChange
784 <label class="form-check-label" htmlFor="user-show-avatars">
785 {i18n.t("show_avatars")}
789 <div class="form-group">
790 <div class="form-check">
792 class="form-check-input"
793 id="user-bot-account"
795 checked={this.state.saveUserSettingsForm.bot_account}
796 onChange={linkEvent(this, this.handleUserSettingsBotAccount)}
798 <label class="form-check-label" htmlFor="user-bot-account">
799 {i18n.t("bot_account")}
803 <div class="form-group">
804 <div class="form-check">
806 class="form-check-input"
807 id="user-show-bot-accounts"
809 checked={this.state.saveUserSettingsForm.show_bot_accounts}
812 this.handleUserSettingsShowBotAccounts
815 <label class="form-check-label" htmlFor="user-show-bot-accounts">
816 {i18n.t("show_bot_accounts")}
820 <div class="form-group">
821 <div class="form-check">
823 class="form-check-input"
824 id="user-show-read-posts"
826 checked={this.state.saveUserSettingsForm.show_read_posts}
827 onChange={linkEvent(this, this.handleUserSettingsShowReadPosts)}
829 <label class="form-check-label" htmlFor="user-show-read-posts">
830 {i18n.t("show_read_posts")}
834 <div class="form-group">
835 <div class="form-check">
837 class="form-check-input"
838 id="user-send-notifications-to-email"
840 disabled={!this.state.saveUserSettingsForm.email}
842 this.state.saveUserSettingsForm.send_notifications_to_email
846 this.handleUserSettingsSendNotificationsToEmailChange
850 class="form-check-label"
851 htmlFor="user-send-notifications-to-email"
853 {i18n.t("send_notifications_to_email")}
857 <div class="form-group">
858 <button type="submit" class="btn btn-block btn-secondary mr-4">
859 {this.state.saveUserSettingsLoading ? (
862 capitalizeFirstLetter(i18n.t("save"))
867 <div class="form-group">
869 class="btn btn-block btn-danger"
872 this.handleDeleteAccountShowConfirmToggle
875 {i18n.t("delete_account")}
877 {this.state.deleteAccountShowConfirm && (
879 <div class="my-2 alert alert-danger" role="alert">
880 {i18n.t("delete_account_confirm")}
884 value={this.state.deleteAccountForm.password}
885 autoComplete="new-password"
889 this.handleDeleteAccountPasswordChange
891 class="form-control my-2"
894 class="btn btn-danger mr-4"
895 disabled={!this.state.deleteAccountForm.password}
896 onClick={linkEvent(this, this.handleDeleteAccount)}
898 {this.state.deleteAccountLoading ? (
901 capitalizeFirstLetter(i18n.t("delete"))
905 class="btn btn-secondary"
908 this.handleDeleteAccountShowConfirmToggle
924 {this.state.personRes.moderates.length > 0 && (
925 <div class="card border-secondary mb-3">
926 <div class="card-body">
927 <h5>{i18n.t("moderates")}</h5>
928 <ul class="list-unstyled mb-0">
929 {this.state.personRes.moderates.map(cmv => (
931 <CommunityLink community={cmv.community} />
945 {this.state.personRes.follows.length > 0 && (
946 <div class="card border-secondary mb-3">
947 <div class="card-body">
948 <h5>{i18n.t("subscribed")}</h5>
949 <ul class="list-unstyled mb-0">
950 {this.state.personRes.follows.map(cfv => (
952 <CommunityLink community={cfv.community} />
963 updateUrl(paramUpdates: UrlParams) {
964 const page = paramUpdates.page || this.state.page;
965 const viewStr = paramUpdates.view || PersonDetailsView[this.state.view];
966 const sortStr = paramUpdates.sort || this.state.sort;
968 let typeView = `/u/${this.state.userName}`;
970 this.props.history.push(
971 `${typeView}/view/${viewStr}/sort/${sortStr}/page/${page}`
973 this.state.loading = true;
974 this.setState(this.state);
975 this.fetchUserData();
978 handlePageChange(page: number) {
979 this.updateUrl({ page });
982 handleSortChange(val: SortType) {
983 this.updateUrl({ sort: val, page: 1 });
986 handleViewChange(i: Person, event: any) {
988 view: PersonDetailsView[Number(event.target.value)],
993 handleUserSettingsShowNsfwChange(i: Person, event: any) {
994 i.state.saveUserSettingsForm.show_nsfw = event.target.checked;
998 handleUserSettingsShowAvatarsChange(i: Person, event: any) {
999 i.state.saveUserSettingsForm.show_avatars = event.target.checked;
1000 UserService.Instance.localUserView.local_user.show_avatars =
1001 event.target.checked; // Just for instant updates
1002 i.setState(i.state);
1005 handleUserSettingsBotAccount(i: Person, event: any) {
1006 i.state.saveUserSettingsForm.bot_account = event.target.checked;
1007 i.setState(i.state);
1010 handleUserSettingsShowBotAccounts(i: Person, event: any) {
1011 i.state.saveUserSettingsForm.show_bot_accounts = event.target.checked;
1012 i.setState(i.state);
1015 handleUserSettingsShowReadPosts(i: Person, event: any) {
1016 i.state.saveUserSettingsForm.show_read_posts = event.target.checked;
1017 i.setState(i.state);
1020 handleUserSettingsShowScoresChange(i: Person, event: any) {
1021 i.state.saveUserSettingsForm.show_scores = event.target.checked;
1022 UserService.Instance.localUserView.local_user.show_scores =
1023 event.target.checked; // Just for instant updates
1024 i.setState(i.state);
1027 handleUserSettingsSendNotificationsToEmailChange(i: Person, event: any) {
1028 i.state.saveUserSettingsForm.send_notifications_to_email =
1029 event.target.checked;
1030 i.setState(i.state);
1033 handleUserSettingsThemeChange(i: Person, event: any) {
1034 i.state.saveUserSettingsForm.theme = event.target.value;
1035 setTheme(event.target.value, true);
1036 i.setState(i.state);
1039 handleUserSettingsLangChange(i: Person, event: any) {
1040 i.state.saveUserSettingsForm.lang = event.target.value;
1041 i18n.changeLanguage(getLanguage(i.state.saveUserSettingsForm.lang));
1042 i.setState(i.state);
1045 handleUserSettingsSortTypeChange(val: SortType) {
1046 this.state.saveUserSettingsForm.default_sort_type =
1047 Object.keys(SortType).indexOf(val);
1048 this.setState(this.state);
1051 handleUserSettingsListingTypeChange(val: ListingType) {
1052 this.state.saveUserSettingsForm.default_listing_type =
1053 Object.keys(ListingType).indexOf(val);
1054 this.setState(this.state);
1057 handleUserSettingsEmailChange(i: Person, event: any) {
1058 i.state.saveUserSettingsForm.email = event.target.value;
1059 i.setState(i.state);
1062 handleUserSettingsBioChange(val: string) {
1063 this.state.saveUserSettingsForm.bio = val;
1064 this.setState(this.state);
1067 handleAvatarUpload(url: string) {
1068 this.state.saveUserSettingsForm.avatar = url;
1069 this.setState(this.state);
1072 handleAvatarRemove() {
1073 this.state.saveUserSettingsForm.avatar = "";
1074 this.setState(this.state);
1077 handleBannerUpload(url: string) {
1078 this.state.saveUserSettingsForm.banner = url;
1079 this.setState(this.state);
1082 handleBannerRemove() {
1083 this.state.saveUserSettingsForm.banner = "";
1084 this.setState(this.state);
1087 handleUserSettingsPreferredUsernameChange(i: Person, event: any) {
1088 i.state.saveUserSettingsForm.display_name = event.target.value;
1089 i.setState(i.state);
1092 handleUserSettingsMatrixUserIdChange(i: Person, event: any) {
1093 i.state.saveUserSettingsForm.matrix_user_id = event.target.value;
1095 i.state.saveUserSettingsForm.matrix_user_id == "" &&
1096 !UserService.Instance.localUserView.person.matrix_user_id
1098 i.state.saveUserSettingsForm.matrix_user_id = undefined;
1100 i.setState(i.state);
1103 handleNewPasswordChange(i: Person, event: any) {
1104 i.state.changePasswordForm.new_password = event.target.value;
1105 if (i.state.changePasswordForm.new_password == "") {
1106 i.state.changePasswordForm.new_password = undefined;
1108 i.setState(i.state);
1111 handleNewPasswordVerifyChange(i: Person, event: any) {
1112 i.state.changePasswordForm.new_password_verify = event.target.value;
1113 if (i.state.changePasswordForm.new_password_verify == "") {
1114 i.state.changePasswordForm.new_password_verify = undefined;
1116 i.setState(i.state);
1119 handleOldPasswordChange(i: Person, event: any) {
1120 i.state.changePasswordForm.old_password = event.target.value;
1121 if (i.state.changePasswordForm.old_password == "") {
1122 i.state.changePasswordForm.old_password = undefined;
1124 i.setState(i.state);
1127 handleSaveUserSettingsSubmit(i: Person, event: any) {
1128 event.preventDefault();
1129 i.state.saveUserSettingsLoading = true;
1130 i.setState(i.state);
1132 WebSocketService.Instance.send(
1133 wsClient.saveUserSettings(i.state.saveUserSettingsForm)
1137 handleChangePasswordSubmit(i: Person, event: any) {
1138 event.preventDefault();
1139 i.state.changePasswordLoading = true;
1140 i.setState(i.state);
1142 WebSocketService.Instance.send(
1143 wsClient.changePassword(i.state.changePasswordForm)
1147 handleDeleteAccountShowConfirmToggle(i: Person, event: any) {
1148 event.preventDefault();
1149 i.state.deleteAccountShowConfirm = !i.state.deleteAccountShowConfirm;
1150 i.setState(i.state);
1153 handleDeleteAccountPasswordChange(i: Person, event: any) {
1154 i.state.deleteAccountForm.password = event.target.value;
1155 i.setState(i.state);
1158 handleLogoutClick(i: Person) {
1159 UserService.Instance.logout();
1160 i.context.router.history.push("/");
1163 handleDeleteAccount(i: Person, event: any) {
1164 event.preventDefault();
1165 i.state.deleteAccountLoading = true;
1166 i.setState(i.state);
1168 WebSocketService.Instance.send(
1169 wsClient.deleteAccount(i.state.deleteAccountForm)
1174 if (this.isCurrentUser) {
1175 this.state.saveUserSettingsForm.show_nsfw =
1176 UserService.Instance.localUserView.local_user.show_nsfw;
1177 this.state.saveUserSettingsForm.theme = UserService.Instance.localUserView
1179 ? UserService.Instance.localUserView.local_user.theme
1181 this.state.saveUserSettingsForm.default_sort_type =
1182 UserService.Instance.localUserView.local_user.default_sort_type;
1183 this.state.saveUserSettingsForm.default_listing_type =
1184 UserService.Instance.localUserView.local_user.default_listing_type;
1185 this.state.saveUserSettingsForm.lang =
1186 UserService.Instance.localUserView.local_user.lang;
1187 this.state.saveUserSettingsForm.avatar =
1188 UserService.Instance.localUserView.person.avatar;
1189 this.state.saveUserSettingsForm.banner =
1190 UserService.Instance.localUserView.person.banner;
1191 this.state.saveUserSettingsForm.display_name =
1192 UserService.Instance.localUserView.person.display_name;
1193 this.state.saveUserSettingsForm.show_avatars =
1194 UserService.Instance.localUserView.local_user.show_avatars;
1195 this.state.saveUserSettingsForm.bot_account =
1196 UserService.Instance.localUserView.person.bot_account;
1197 this.state.saveUserSettingsForm.show_bot_accounts =
1198 UserService.Instance.localUserView.local_user.show_bot_accounts;
1199 this.state.saveUserSettingsForm.show_scores =
1200 UserService.Instance.localUserView.local_user.show_scores;
1201 this.state.saveUserSettingsForm.show_read_posts =
1202 UserService.Instance.localUserView.local_user.show_read_posts;
1203 this.state.saveUserSettingsForm.email =
1204 UserService.Instance.localUserView.local_user.email;
1205 this.state.saveUserSettingsForm.bio =
1206 UserService.Instance.localUserView.person.bio;
1207 this.state.saveUserSettingsForm.send_notifications_to_email =
1208 UserService.Instance.localUserView.local_user.send_notifications_to_email;
1209 this.state.saveUserSettingsForm.matrix_user_id =
1210 UserService.Instance.localUserView.person.matrix_user_id;
1214 parseMessage(msg: any) {
1215 let op = wsUserOp(msg);
1218 toast(i18n.t(msg.error), "danger");
1219 if (msg.error == "couldnt_find_that_username_or_email") {
1220 this.context.router.history.push("/");
1223 deleteAccountLoading: false,
1224 saveUserSettingsLoading: false,
1225 changePasswordLoading: false,
1228 } else if (msg.reconnect) {
1229 this.fetchUserData();
1230 } else if (op == UserOperation.GetPersonDetails) {
1231 // Since the PersonDetails contains posts/comments as well as some general user info we listen here as well
1232 // and set the parent state if it is not set or differs
1233 // TODO this might need to get abstracted
1234 let data = wsJsonToRes<GetPersonDetailsResponse>(msg).data;
1235 this.state.personRes = data;
1238 this.state.loading = false;
1239 this.setState(this.state);
1240 restoreScrollPosition(this.context);
1241 } else if (op == UserOperation.SaveUserSettings) {
1242 let data = wsJsonToRes<LoginResponse>(msg).data;
1243 UserService.Instance.login(data);
1244 this.state.personRes.person_view.person.bio =
1245 this.state.saveUserSettingsForm.bio;
1246 this.state.personRes.person_view.person.display_name =
1247 this.state.saveUserSettingsForm.display_name;
1248 this.state.personRes.person_view.person.banner =
1249 this.state.saveUserSettingsForm.banner;
1250 this.state.personRes.person_view.person.avatar =
1251 this.state.saveUserSettingsForm.avatar;
1252 this.state.saveUserSettingsLoading = false;
1253 this.setState(this.state);
1255 window.scrollTo(0, 0);
1256 } else if (op == UserOperation.ChangePassword) {
1257 let data = wsJsonToRes<LoginResponse>(msg).data;
1258 UserService.Instance.login(data);
1259 this.state.changePasswordLoading = false;
1260 this.setState(this.state);
1261 window.scrollTo(0, 0);
1262 toast(i18n.t("password_changed"));
1263 } else if (op == UserOperation.DeleteAccount) {
1265 deleteAccountLoading: false,
1266 deleteAccountShowConfirm: false,
1268 UserService.Instance.logout();
1269 window.location.href = "/";
1270 } else if (op == UserOperation.AddAdmin) {
1271 let data = wsJsonToRes<AddAdminResponse>(msg).data;
1272 this.state.siteRes.admins = data.admins;
1273 this.setState(this.state);
1274 } else if (op == UserOperation.CreateCommentLike) {
1275 let data = wsJsonToRes<CommentResponse>(msg).data;
1276 createCommentLikeRes(data.comment_view, this.state.personRes.comments);
1277 this.setState(this.state);
1279 op == UserOperation.EditComment ||
1280 op == UserOperation.DeleteComment ||
1281 op == UserOperation.RemoveComment
1283 let data = wsJsonToRes<CommentResponse>(msg).data;
1284 editCommentRes(data.comment_view, this.state.personRes.comments);
1285 this.setState(this.state);
1286 } else if (op == UserOperation.CreateComment) {
1287 let data = wsJsonToRes<CommentResponse>(msg).data;
1289 UserService.Instance.localUserView &&
1290 data.comment_view.creator.id ==
1291 UserService.Instance.localUserView.person.id
1293 toast(i18n.t("reply_sent"));
1295 } else if (op == UserOperation.SaveComment) {
1296 let data = wsJsonToRes<CommentResponse>(msg).data;
1297 saveCommentRes(data.comment_view, this.state.personRes.comments);
1298 this.setState(this.state);
1300 op == UserOperation.EditPost ||
1301 op == UserOperation.DeletePost ||
1302 op == UserOperation.RemovePost ||
1303 op == UserOperation.LockPost ||
1304 op == UserOperation.StickyPost ||
1305 op == UserOperation.SavePost
1307 let data = wsJsonToRes<PostResponse>(msg).data;
1308 editPostFindRes(data.post_view, this.state.personRes.posts);
1309 this.setState(this.state);
1310 } else if (op == UserOperation.CreatePostLike) {
1311 let data = wsJsonToRes<PostResponse>(msg).data;
1312 createPostLikeFindRes(data.post_view, this.state.personRes.posts);
1313 this.setState(this.state);
1314 } else if (op == UserOperation.BanPerson) {
1315 let data = wsJsonToRes<BanPersonResponse>(msg).data;
1316 this.state.personRes.comments
1317 .filter(c => c.creator.id == data.person_view.person.id)
1318 .forEach(c => (c.creator.banned = data.banned));
1319 this.state.personRes.posts
1320 .filter(c => c.creator.id == data.person_view.person.id)
1321 .forEach(c => (c.creator.banned = data.banned));
1322 this.setState(this.state);