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-show-new-post-notifs"
839 checked={this.state.saveUserSettingsForm.show_new_post_notifs}
842 this.handleUserSettingsShowNewPostNotifs
846 class="form-check-label"
847 htmlFor="user-show-new-post-notifs"
849 {i18n.t("show_new_post_notifs")}
853 <div class="form-group">
854 <div class="form-check">
856 class="form-check-input"
857 id="user-send-notifications-to-email"
859 disabled={!this.state.saveUserSettingsForm.email}
861 this.state.saveUserSettingsForm.send_notifications_to_email
865 this.handleUserSettingsSendNotificationsToEmailChange
869 class="form-check-label"
870 htmlFor="user-send-notifications-to-email"
872 {i18n.t("send_notifications_to_email")}
876 <div class="form-group">
877 <button type="submit" class="btn btn-block btn-secondary mr-4">
878 {this.state.saveUserSettingsLoading ? (
881 capitalizeFirstLetter(i18n.t("save"))
886 <div class="form-group">
888 class="btn btn-block btn-danger"
891 this.handleDeleteAccountShowConfirmToggle
894 {i18n.t("delete_account")}
896 {this.state.deleteAccountShowConfirm && (
898 <div class="my-2 alert alert-danger" role="alert">
899 {i18n.t("delete_account_confirm")}
903 value={this.state.deleteAccountForm.password}
904 autoComplete="new-password"
908 this.handleDeleteAccountPasswordChange
910 class="form-control my-2"
913 class="btn btn-danger mr-4"
914 disabled={!this.state.deleteAccountForm.password}
915 onClick={linkEvent(this, this.handleDeleteAccount)}
917 {this.state.deleteAccountLoading ? (
920 capitalizeFirstLetter(i18n.t("delete"))
924 class="btn btn-secondary"
927 this.handleDeleteAccountShowConfirmToggle
943 {this.state.personRes.moderates.length > 0 && (
944 <div class="card border-secondary mb-3">
945 <div class="card-body">
946 <h5>{i18n.t("moderates")}</h5>
947 <ul class="list-unstyled mb-0">
948 {this.state.personRes.moderates.map(cmv => (
950 <CommunityLink community={cmv.community} />
964 {this.state.personRes.follows.length > 0 && (
965 <div class="card border-secondary mb-3">
966 <div class="card-body">
967 <h5>{i18n.t("subscribed")}</h5>
968 <ul class="list-unstyled mb-0">
969 {this.state.personRes.follows.map(cfv => (
971 <CommunityLink community={cfv.community} />
982 updateUrl(paramUpdates: UrlParams) {
983 const page = paramUpdates.page || this.state.page;
984 const viewStr = paramUpdates.view || PersonDetailsView[this.state.view];
985 const sortStr = paramUpdates.sort || this.state.sort;
987 let typeView = `/u/${this.state.userName}`;
989 this.props.history.push(
990 `${typeView}/view/${viewStr}/sort/${sortStr}/page/${page}`
992 this.state.loading = true;
993 this.setState(this.state);
994 this.fetchUserData();
997 handlePageChange(page: number) {
998 this.updateUrl({ page });
1001 handleSortChange(val: SortType) {
1002 this.updateUrl({ sort: val, page: 1 });
1005 handleViewChange(i: Person, event: any) {
1007 view: PersonDetailsView[Number(event.target.value)],
1012 handleUserSettingsShowNsfwChange(i: Person, event: any) {
1013 i.state.saveUserSettingsForm.show_nsfw = event.target.checked;
1014 i.setState(i.state);
1017 handleUserSettingsShowAvatarsChange(i: Person, event: any) {
1018 i.state.saveUserSettingsForm.show_avatars = event.target.checked;
1019 UserService.Instance.localUserView.local_user.show_avatars =
1020 event.target.checked; // Just for instant updates
1021 i.setState(i.state);
1024 handleUserSettingsBotAccount(i: Person, event: any) {
1025 i.state.saveUserSettingsForm.bot_account = event.target.checked;
1026 i.setState(i.state);
1029 handleUserSettingsShowBotAccounts(i: Person, event: any) {
1030 i.state.saveUserSettingsForm.show_bot_accounts = event.target.checked;
1031 i.setState(i.state);
1034 handleUserSettingsShowReadPosts(i: Person, event: any) {
1035 i.state.saveUserSettingsForm.show_read_posts = event.target.checked;
1036 i.setState(i.state);
1039 handleUserSettingsShowNewPostNotifs(i: Person, event: any) {
1040 i.state.saveUserSettingsForm.show_new_post_notifs = event.target.checked;
1041 i.setState(i.state);
1044 handleUserSettingsShowScoresChange(i: Person, event: any) {
1045 i.state.saveUserSettingsForm.show_scores = event.target.checked;
1046 UserService.Instance.localUserView.local_user.show_scores =
1047 event.target.checked; // Just for instant updates
1048 i.setState(i.state);
1051 handleUserSettingsSendNotificationsToEmailChange(i: Person, event: any) {
1052 i.state.saveUserSettingsForm.send_notifications_to_email =
1053 event.target.checked;
1054 i.setState(i.state);
1057 handleUserSettingsThemeChange(i: Person, event: any) {
1058 i.state.saveUserSettingsForm.theme = event.target.value;
1059 setTheme(event.target.value, true);
1060 i.setState(i.state);
1063 handleUserSettingsLangChange(i: Person, event: any) {
1064 i.state.saveUserSettingsForm.lang = event.target.value;
1065 i18n.changeLanguage(getLanguage(i.state.saveUserSettingsForm.lang));
1066 i.setState(i.state);
1069 handleUserSettingsSortTypeChange(val: SortType) {
1070 this.state.saveUserSettingsForm.default_sort_type =
1071 Object.keys(SortType).indexOf(val);
1072 this.setState(this.state);
1075 handleUserSettingsListingTypeChange(val: ListingType) {
1076 this.state.saveUserSettingsForm.default_listing_type =
1077 Object.keys(ListingType).indexOf(val);
1078 this.setState(this.state);
1081 handleUserSettingsEmailChange(i: Person, event: any) {
1082 i.state.saveUserSettingsForm.email = event.target.value;
1083 i.setState(i.state);
1086 handleUserSettingsBioChange(val: string) {
1087 this.state.saveUserSettingsForm.bio = val;
1088 this.setState(this.state);
1091 handleAvatarUpload(url: string) {
1092 this.state.saveUserSettingsForm.avatar = url;
1093 this.setState(this.state);
1096 handleAvatarRemove() {
1097 this.state.saveUserSettingsForm.avatar = "";
1098 this.setState(this.state);
1101 handleBannerUpload(url: string) {
1102 this.state.saveUserSettingsForm.banner = url;
1103 this.setState(this.state);
1106 handleBannerRemove() {
1107 this.state.saveUserSettingsForm.banner = "";
1108 this.setState(this.state);
1111 handleUserSettingsPreferredUsernameChange(i: Person, event: any) {
1112 i.state.saveUserSettingsForm.display_name = event.target.value;
1113 i.setState(i.state);
1116 handleUserSettingsMatrixUserIdChange(i: Person, event: any) {
1117 i.state.saveUserSettingsForm.matrix_user_id = event.target.value;
1119 i.state.saveUserSettingsForm.matrix_user_id == "" &&
1120 !UserService.Instance.localUserView.person.matrix_user_id
1122 i.state.saveUserSettingsForm.matrix_user_id = undefined;
1124 i.setState(i.state);
1127 handleNewPasswordChange(i: Person, event: any) {
1128 i.state.changePasswordForm.new_password = event.target.value;
1129 if (i.state.changePasswordForm.new_password == "") {
1130 i.state.changePasswordForm.new_password = undefined;
1132 i.setState(i.state);
1135 handleNewPasswordVerifyChange(i: Person, event: any) {
1136 i.state.changePasswordForm.new_password_verify = event.target.value;
1137 if (i.state.changePasswordForm.new_password_verify == "") {
1138 i.state.changePasswordForm.new_password_verify = undefined;
1140 i.setState(i.state);
1143 handleOldPasswordChange(i: Person, event: any) {
1144 i.state.changePasswordForm.old_password = event.target.value;
1145 if (i.state.changePasswordForm.old_password == "") {
1146 i.state.changePasswordForm.old_password = undefined;
1148 i.setState(i.state);
1151 handleSaveUserSettingsSubmit(i: Person, event: any) {
1152 event.preventDefault();
1153 i.state.saveUserSettingsLoading = true;
1154 i.setState(i.state);
1156 WebSocketService.Instance.send(
1157 wsClient.saveUserSettings(i.state.saveUserSettingsForm)
1161 handleChangePasswordSubmit(i: Person, event: any) {
1162 event.preventDefault();
1163 i.state.changePasswordLoading = true;
1164 i.setState(i.state);
1166 WebSocketService.Instance.send(
1167 wsClient.changePassword(i.state.changePasswordForm)
1171 handleDeleteAccountShowConfirmToggle(i: Person, event: any) {
1172 event.preventDefault();
1173 i.state.deleteAccountShowConfirm = !i.state.deleteAccountShowConfirm;
1174 i.setState(i.state);
1177 handleDeleteAccountPasswordChange(i: Person, event: any) {
1178 i.state.deleteAccountForm.password = event.target.value;
1179 i.setState(i.state);
1182 handleLogoutClick(i: Person) {
1183 UserService.Instance.logout();
1184 i.context.router.history.push("/");
1187 handleDeleteAccount(i: Person, event: any) {
1188 event.preventDefault();
1189 i.state.deleteAccountLoading = true;
1190 i.setState(i.state);
1192 WebSocketService.Instance.send(
1193 wsClient.deleteAccount(i.state.deleteAccountForm)
1198 if (this.isCurrentUser) {
1199 this.state.saveUserSettingsForm.show_nsfw =
1200 UserService.Instance.localUserView.local_user.show_nsfw;
1201 this.state.saveUserSettingsForm.theme = UserService.Instance.localUserView
1203 ? UserService.Instance.localUserView.local_user.theme
1205 this.state.saveUserSettingsForm.default_sort_type =
1206 UserService.Instance.localUserView.local_user.default_sort_type;
1207 this.state.saveUserSettingsForm.default_listing_type =
1208 UserService.Instance.localUserView.local_user.default_listing_type;
1209 this.state.saveUserSettingsForm.lang =
1210 UserService.Instance.localUserView.local_user.lang;
1211 this.state.saveUserSettingsForm.avatar =
1212 UserService.Instance.localUserView.person.avatar;
1213 this.state.saveUserSettingsForm.banner =
1214 UserService.Instance.localUserView.person.banner;
1215 this.state.saveUserSettingsForm.display_name =
1216 UserService.Instance.localUserView.person.display_name;
1217 this.state.saveUserSettingsForm.show_avatars =
1218 UserService.Instance.localUserView.local_user.show_avatars;
1219 this.state.saveUserSettingsForm.bot_account =
1220 UserService.Instance.localUserView.person.bot_account;
1221 this.state.saveUserSettingsForm.show_bot_accounts =
1222 UserService.Instance.localUserView.local_user.show_bot_accounts;
1223 this.state.saveUserSettingsForm.show_scores =
1224 UserService.Instance.localUserView.local_user.show_scores;
1225 this.state.saveUserSettingsForm.show_read_posts =
1226 UserService.Instance.localUserView.local_user.show_read_posts;
1227 this.state.saveUserSettingsForm.show_new_post_notifs =
1228 UserService.Instance.localUserView.local_user.show_new_post_notifs;
1229 this.state.saveUserSettingsForm.email =
1230 UserService.Instance.localUserView.local_user.email;
1231 this.state.saveUserSettingsForm.bio =
1232 UserService.Instance.localUserView.person.bio;
1233 this.state.saveUserSettingsForm.send_notifications_to_email =
1234 UserService.Instance.localUserView.local_user.send_notifications_to_email;
1235 this.state.saveUserSettingsForm.matrix_user_id =
1236 UserService.Instance.localUserView.person.matrix_user_id;
1240 parseMessage(msg: any) {
1241 let op = wsUserOp(msg);
1244 toast(i18n.t(msg.error), "danger");
1245 if (msg.error == "couldnt_find_that_username_or_email") {
1246 this.context.router.history.push("/");
1249 deleteAccountLoading: false,
1250 saveUserSettingsLoading: false,
1251 changePasswordLoading: false,
1254 } else if (msg.reconnect) {
1255 this.fetchUserData();
1256 } else if (op == UserOperation.GetPersonDetails) {
1257 // Since the PersonDetails contains posts/comments as well as some general user info we listen here as well
1258 // and set the parent state if it is not set or differs
1259 // TODO this might need to get abstracted
1260 let data = wsJsonToRes<GetPersonDetailsResponse>(msg).data;
1261 this.state.personRes = data;
1264 this.state.loading = false;
1265 this.setState(this.state);
1266 restoreScrollPosition(this.context);
1267 } else if (op == UserOperation.SaveUserSettings) {
1268 let data = wsJsonToRes<LoginResponse>(msg).data;
1269 UserService.Instance.login(data);
1270 this.state.personRes.person_view.person.bio =
1271 this.state.saveUserSettingsForm.bio;
1272 this.state.personRes.person_view.person.display_name =
1273 this.state.saveUserSettingsForm.display_name;
1274 this.state.personRes.person_view.person.banner =
1275 this.state.saveUserSettingsForm.banner;
1276 this.state.personRes.person_view.person.avatar =
1277 this.state.saveUserSettingsForm.avatar;
1278 this.state.saveUserSettingsLoading = false;
1279 this.setState(this.state);
1281 window.scrollTo(0, 0);
1282 } else if (op == UserOperation.ChangePassword) {
1283 let data = wsJsonToRes<LoginResponse>(msg).data;
1284 UserService.Instance.login(data);
1285 this.state.changePasswordLoading = false;
1286 this.setState(this.state);
1287 window.scrollTo(0, 0);
1288 toast(i18n.t("password_changed"));
1289 } else if (op == UserOperation.DeleteAccount) {
1291 deleteAccountLoading: false,
1292 deleteAccountShowConfirm: false,
1294 UserService.Instance.logout();
1295 window.location.href = "/";
1296 } else if (op == UserOperation.AddAdmin) {
1297 let data = wsJsonToRes<AddAdminResponse>(msg).data;
1298 this.state.siteRes.admins = data.admins;
1299 this.setState(this.state);
1300 } else if (op == UserOperation.CreateCommentLike) {
1301 let data = wsJsonToRes<CommentResponse>(msg).data;
1302 createCommentLikeRes(data.comment_view, this.state.personRes.comments);
1303 this.setState(this.state);
1305 op == UserOperation.EditComment ||
1306 op == UserOperation.DeleteComment ||
1307 op == UserOperation.RemoveComment
1309 let data = wsJsonToRes<CommentResponse>(msg).data;
1310 editCommentRes(data.comment_view, this.state.personRes.comments);
1311 this.setState(this.state);
1312 } else if (op == UserOperation.CreateComment) {
1313 let data = wsJsonToRes<CommentResponse>(msg).data;
1315 UserService.Instance.localUserView &&
1316 data.comment_view.creator.id ==
1317 UserService.Instance.localUserView.person.id
1319 toast(i18n.t("reply_sent"));
1321 } else if (op == UserOperation.SaveComment) {
1322 let data = wsJsonToRes<CommentResponse>(msg).data;
1323 saveCommentRes(data.comment_view, this.state.personRes.comments);
1324 this.setState(this.state);
1326 op == UserOperation.EditPost ||
1327 op == UserOperation.DeletePost ||
1328 op == UserOperation.RemovePost ||
1329 op == UserOperation.LockPost ||
1330 op == UserOperation.StickyPost ||
1331 op == UserOperation.SavePost
1333 let data = wsJsonToRes<PostResponse>(msg).data;
1334 editPostFindRes(data.post_view, this.state.personRes.posts);
1335 this.setState(this.state);
1336 } else if (op == UserOperation.CreatePostLike) {
1337 let data = wsJsonToRes<PostResponse>(msg).data;
1338 createPostLikeFindRes(data.post_view, this.state.personRes.posts);
1339 this.setState(this.state);
1340 } else if (op == UserOperation.BanPerson) {
1341 let data = wsJsonToRes<BanPersonResponse>(msg).data;
1342 this.state.personRes.comments
1343 .filter(c => c.creator.id == data.person_view.person.id)
1344 .forEach(c => (c.creator.banned = data.banned));
1345 this.state.personRes.posts
1346 .filter(c => c.creator.id == data.person_view.person.id)
1347 .forEach(c => (c.creator.banned = data.banned));
1348 this.setState(this.state);