1 import { Component, linkEvent } from 'inferno';
2 import { Link } from 'inferno-router';
3 import { Subscription } from 'rxjs';
11 WebSocketJsonResponse,
19 } from 'lemmy-js-client';
20 import { InitialFetchRequest, UserDetailsView } from '../interfaces';
21 import { WebSocketService, UserService } from '../services';
26 capitalizeFirstLetter,
42 createPostLikeFindRes,
47 import { UserListing } from './user-listing';
48 import { HtmlTags } from './html-tags';
49 import { SortSelect } from './sort-select';
50 import { ListingTypeSelect } from './listing-type-select';
51 import { MomentTime } from './moment-time';
52 import { i18n } from '../i18next';
53 import moment from 'moment';
54 import { UserDetails } from './user-details';
55 import { MarkdownTextArea } from './markdown-textarea';
56 import { ImageUploadForm } from './image-upload-form';
57 import { BannerIconHeader } from './banner-icon-header';
58 import { CommunityLink } from './community-link';
61 userRes: UserDetailsResponse;
64 view: UserDetailsView;
68 userSettingsForm: UserSettingsForm;
69 userSettingsLoading: boolean;
70 deleteAccountLoading: boolean;
71 deleteAccountShowConfirm: boolean;
72 deleteAccountForm: DeleteAccountForm;
73 siteRes: GetSiteResponse;
77 view: UserDetailsView;
80 user_id: number | null;
90 export class User extends Component<any, UserState> {
91 private isoData = setIsoData(this.context);
92 private subscription: Subscription;
93 private emptyState: UserState = {
95 userId: getIdFromProps(this.props),
96 userName: getUsernameFromProps(this.props),
98 view: User.getViewFromProps(this.props.match.view),
99 sort: User.getSortTypeFromProps(this.props.match.sort),
100 page: User.getPageFromProps(this.props.match.page),
104 default_sort_type: null,
105 default_listing_type: null,
108 send_notifications_to_email: null,
111 preferred_username: null,
113 userSettingsLoading: null,
114 deleteAccountLoading: null,
115 deleteAccountShowConfirm: false,
119 siteRes: this.isoData.site,
122 constructor(props: any, context: any) {
123 super(props, context);
125 this.state = this.emptyState;
126 this.handleSortChange = this.handleSortChange.bind(this);
127 this.handleUserSettingsSortTypeChange = this.handleUserSettingsSortTypeChange.bind(
130 this.handleUserSettingsListingTypeChange = this.handleUserSettingsListingTypeChange.bind(
133 this.handlePageChange = this.handlePageChange.bind(this);
134 this.handleUserSettingsBioChange = this.handleUserSettingsBioChange.bind(
138 this.handleAvatarUpload = this.handleAvatarUpload.bind(this);
139 this.handleAvatarRemove = this.handleAvatarRemove.bind(this);
141 this.handleBannerUpload = this.handleBannerUpload.bind(this);
142 this.handleBannerRemove = this.handleBannerRemove.bind(this);
144 this.parseMessage = this.parseMessage.bind(this);
145 this.subscription = wsSubscribe(this.parseMessage);
147 // Only fetch the data if coming from another route
148 if (this.isoData.path == this.context.router.route.match.url) {
149 this.state.userRes = this.isoData.routeData[0];
151 this.state.loading = false;
153 this.fetchUserData();
160 let form: GetUserDetailsForm = {
161 user_id: this.state.userId,
162 username: this.state.userName,
163 sort: this.state.sort,
164 saved_only: this.state.view === UserDetailsView.Saved,
165 page: this.state.page,
168 WebSocketService.Instance.getUserDetails(form);
171 get isCurrentUser() {
173 UserService.Instance.user &&
174 UserService.Instance.user.id == this.state.userRes.user.id
178 static getViewFromProps(view: string): UserDetailsView {
179 return view ? UserDetailsView[view] : UserDetailsView.Overview;
182 static getSortTypeFromProps(sort: string): SortType {
183 return sort ? routeSortTypeToEnum(sort) : SortType.New;
186 static getPageFromProps(page: number): number {
187 return page ? Number(page) : 1;
190 static fetchInitialData(req: InitialFetchRequest): Promise<any>[] {
191 let pathSplit = req.path.split('/');
192 let promises: Promise<any>[] = [];
194 // It can be /u/me, or /username/1
195 let idOrName = pathSplit[2];
197 let username: string;
198 if (isNaN(Number(idOrName))) {
201 user_id = Number(idOrName);
204 let view = this.getViewFromProps(pathSplit[4]);
205 let sort = this.getSortTypeFromProps(pathSplit[6]);
206 let page = this.getPageFromProps(Number(pathSplit[8]));
208 let form: GetUserDetailsForm = {
210 saved_only: view === UserDetailsView.Saved,
214 this.setIdOrName(form, user_id, username);
215 setAuth(form, req.auth);
216 promises.push(req.client.getUserDetails(form));
220 static setIdOrName(obj: any, id: number, name_: string) {
224 obj.username = name_;
228 componentWillUnmount() {
229 this.subscription.unsubscribe();
232 static getDerivedStateFromProps(props: any): UserProps {
234 view: this.getViewFromProps(props.match.params.view),
235 sort: this.getSortTypeFromProps(props.match.params.sort),
236 page: this.getPageFromProps(props.match.params.page),
237 user_id: Number(props.match.params.id) || null,
238 username: props.match.params.username,
242 componentDidUpdate(lastProps: any, _lastState: UserState, _snapshot: any) {
243 // Necessary if you are on a post and you click another post (same route)
245 lastProps.location.pathname.split('/')[2] !==
246 lastProps.history.location.pathname.split('/')[2]
248 // Couldnt get a refresh working. This does for now.
253 get documentTitle(): string {
254 return `@${this.state.userRes.user.name} - ${this.state.siteRes.site.name}`;
257 get bioTag(): string {
258 return this.state.userRes.user.bio
259 ? previewLines(this.state.userRes.user.bio)
265 <div class="container">
266 {this.state.loading ? (
268 <svg class="icon icon-spinner spin">
269 <use xlinkHref="#icon-spinner"></use>
274 <div class="col-12 col-md-8">
277 title={this.documentTitle}
278 path={this.context.router.route.match.url}
279 description={this.bioTag}
280 image={this.state.userRes.user.avatar}
285 {!this.state.loading && this.selects()}
287 userRes={this.state.userRes}
288 sort={this.state.sort}
289 page={this.state.page}
291 enableDownvotes={this.state.siteRes.site.enable_downvotes}
292 enableNsfw={this.state.siteRes.site.enable_nsfw}
293 view={this.state.view}
294 onPageChange={this.handlePageChange}
298 {!this.state.loading && (
299 <div class="col-12 col-md-4">
300 {this.isCurrentUser && this.userSettings()}
313 <div class="btn-group btn-group-toggle flex-wrap mb-2">
315 className={`btn btn-outline-secondary pointer
316 ${this.state.view == UserDetailsView.Overview && 'active'}
321 value={UserDetailsView.Overview}
322 checked={this.state.view === UserDetailsView.Overview}
323 onChange={linkEvent(this, this.handleViewChange)}
328 className={`btn btn-outline-secondary pointer
329 ${this.state.view == UserDetailsView.Comments && 'active'}
334 value={UserDetailsView.Comments}
335 checked={this.state.view == UserDetailsView.Comments}
336 onChange={linkEvent(this, this.handleViewChange)}
341 className={`btn btn-outline-secondary pointer
342 ${this.state.view == UserDetailsView.Posts && 'active'}
347 value={UserDetailsView.Posts}
348 checked={this.state.view == UserDetailsView.Posts}
349 onChange={linkEvent(this, this.handleViewChange)}
354 className={`btn btn-outline-secondary pointer
355 ${this.state.view == UserDetailsView.Saved && 'active'}
360 value={UserDetailsView.Saved}
361 checked={this.state.view == UserDetailsView.Saved}
362 onChange={linkEvent(this, this.handleViewChange)}
372 <div className="mb-2">
373 <span class="mr-3">{this.viewRadios()}</span>
375 sort={this.state.sort}
376 onChange={this.handleSortChange}
380 href={`/feeds/u/${this.state.userName}.xml?sort=${this.state.sort}`}
385 <svg class="icon mx-2 text-muted small">
386 <use xlinkHref="#icon-rss">#</use>
394 let user = this.state.userRes.user;
398 <BannerIconHeader banner={user.banner} icon={user.avatar} />
401 <div class="mb-0 d-flex flex-wrap">
403 {user.preferred_username && (
404 <h5 class="mb-0">{user.preferred_username}</h5>
406 <ul class="list-inline mb-2">
407 <li className="list-inline-item">
417 <li className="list-inline-item badge badge-danger">
423 <div className="flex-grow-1 unselectable pointer mx-2"></div>
424 {this.isCurrentUser ? (
426 class="d-flex align-self-start btn btn-secondary mr-2"
427 onClick={linkEvent(this, this.handleLogoutClick)}
434 className={`d-flex align-self-start btn btn-secondary mr-2 ${
435 !user.matrix_user_id && 'invisible'
439 href={`https://matrix.to/#/${user.matrix_user_id}`}
441 {i18n.t('send_secure_message')}
444 className={'d-flex align-self-start btn btn-secondary'}
445 to={`/create_private_message/recipient/${user.id}`}
447 {i18n.t('send_message')}
453 <div className="d-flex align-items-center mb-2">
456 dangerouslySetInnerHTML={mdToHtml(user.bio)}
461 <ul class="list-inline mb-2">
462 <li className="list-inline-item badge badge-light">
463 {i18n.t('number_of_posts', { count: user.number_of_posts })}
465 <li className="list-inline-item badge badge-light">
466 {i18n.t('number_of_comments', {
467 count: user.number_of_comments,
472 <div class="text-muted">
473 {i18n.t('joined')} <MomentTime data={user} showAgo />
475 <div className="d-flex align-items-center text-muted mb-2">
477 <use xlinkHref="#icon-cake"></use>
479 <span className="ml-2">
480 {i18n.t('cake_day_title')}{' '}
481 {moment.utc(user.published).local().format('MMM DD, YYYY')}
493 <div class="card border-secondary mb-3">
494 <div class="card-body">
495 <h5>{i18n.t('settings')}</h5>
496 <form onSubmit={linkEvent(this, this.handleUserSettingsSubmit)}>
497 <div class="form-group">
498 <label>{i18n.t('avatar')}</label>
500 uploadTitle={i18n.t('upload_avatar')}
501 imageSrc={this.state.userSettingsForm.avatar}
502 onUpload={this.handleAvatarUpload}
503 onRemove={this.handleAvatarRemove}
507 <div class="form-group">
508 <label>{i18n.t('banner')}</label>
510 uploadTitle={i18n.t('upload_banner')}
511 imageSrc={this.state.userSettingsForm.banner}
512 onUpload={this.handleBannerUpload}
513 onRemove={this.handleBannerRemove}
516 <div class="form-group">
517 <label>{i18n.t('language')}</label>
519 value={this.state.userSettingsForm.lang}
520 onChange={linkEvent(this, this.handleUserSettingsLangChange)}
521 class="ml-2 custom-select w-auto"
523 <option disabled>{i18n.t('language')}</option>
524 <option value="browser">{i18n.t('browser_default')}</option>
525 <option disabled>──</option>
526 {languages.map(lang => (
527 <option value={lang.code}>{lang.name}</option>
531 <div class="form-group">
532 <label>{i18n.t('theme')}</label>
534 value={this.state.userSettingsForm.theme}
535 onChange={linkEvent(this, this.handleUserSettingsThemeChange)}
536 class="ml-2 custom-select w-auto"
538 <option disabled>{i18n.t('theme')}</option>
539 <option value="browser">{i18n.t('browser_default')}</option>
540 {themes.map(theme => (
541 <option value={theme}>{theme}</option>
545 <form className="form-group">
547 <div class="mr-2">{i18n.t('sort_type')}</div>
551 Object.values(ListingType)[
552 this.state.userSettingsForm.default_listing_type
556 this.state.siteRes.federated_instances &&
557 this.state.siteRes.federated_instances.length > 0
559 onChange={this.handleUserSettingsListingTypeChange}
562 <form className="form-group">
564 <div class="mr-2">{i18n.t('type')}</div>
568 Object.values(SortType)[
569 this.state.userSettingsForm.default_sort_type
572 onChange={this.handleUserSettingsSortTypeChange}
575 <div class="form-group row">
576 <label class="col-lg-5 col-form-label">
577 {i18n.t('display_name')}
579 <div class="col-lg-7">
583 placeholder={i18n.t('optional')}
584 value={this.state.userSettingsForm.preferred_username}
587 this.handleUserSettingsPreferredUsernameChange
589 pattern="^(?!@)(.+)$"
595 <div class="form-group row">
596 <label class="col-lg-3 col-form-label" htmlFor="user-bio">
599 <div class="col-lg-9">
601 initialContent={this.state.userSettingsForm.bio}
602 onContentChange={this.handleUserSettingsBioChange}
604 hideNavigationWarnings
608 <div class="form-group row">
609 <label class="col-lg-3 col-form-label" htmlFor="user-email">
612 <div class="col-lg-9">
617 placeholder={i18n.t('optional')}
618 value={this.state.userSettingsForm.email}
621 this.handleUserSettingsEmailChange
627 <div class="form-group row">
628 <label class="col-lg-5 col-form-label">
629 <a href={elementUrl} target="_blank" rel="noopener">
630 {i18n.t('matrix_user_id')}
633 <div class="col-lg-7">
637 placeholder="@user:example.com"
638 value={this.state.userSettingsForm.matrix_user_id}
641 this.handleUserSettingsMatrixUserIdChange
647 <div class="form-group row">
648 <label class="col-lg-5 col-form-label" htmlFor="user-password">
649 {i18n.t('new_password')}
651 <div class="col-lg-7">
656 value={this.state.userSettingsForm.new_password}
657 autoComplete="new-password"
660 this.handleUserSettingsNewPasswordChange
665 <div class="form-group row">
667 class="col-lg-5 col-form-label"
668 htmlFor="user-verify-password"
670 {i18n.t('verify_password')}
672 <div class="col-lg-7">
675 id="user-verify-password"
677 value={this.state.userSettingsForm.new_password_verify}
678 autoComplete="new-password"
681 this.handleUserSettingsNewPasswordVerifyChange
686 <div class="form-group row">
688 class="col-lg-5 col-form-label"
689 htmlFor="user-old-password"
691 {i18n.t('old_password')}
693 <div class="col-lg-7">
696 id="user-old-password"
698 value={this.state.userSettingsForm.old_password}
699 autoComplete="new-password"
702 this.handleUserSettingsOldPasswordChange
707 {this.state.siteRes.site.enable_nsfw && (
708 <div class="form-group">
709 <div class="form-check">
711 class="form-check-input"
714 checked={this.state.userSettingsForm.show_nsfw}
717 this.handleUserSettingsShowNsfwChange
720 <label class="form-check-label" htmlFor="user-show-nsfw">
721 {i18n.t('show_nsfw')}
726 <div class="form-group">
727 <div class="form-check">
729 class="form-check-input"
730 id="user-show-avatars"
732 checked={this.state.userSettingsForm.show_avatars}
735 this.handleUserSettingsShowAvatarsChange
738 <label class="form-check-label" htmlFor="user-show-avatars">
739 {i18n.t('show_avatars')}
743 <div class="form-group">
744 <div class="form-check">
746 class="form-check-input"
747 id="user-send-notifications-to-email"
749 disabled={!this.state.userSettingsForm.email}
751 this.state.userSettingsForm.send_notifications_to_email
755 this.handleUserSettingsSendNotificationsToEmailChange
759 class="form-check-label"
760 htmlFor="user-send-notifications-to-email"
762 {i18n.t('send_notifications_to_email')}
766 <div class="form-group">
767 <button type="submit" class="btn btn-block btn-secondary mr-4">
768 {this.state.userSettingsLoading ? (
769 <svg class="icon icon-spinner spin">
770 <use xlinkHref="#icon-spinner"></use>
773 capitalizeFirstLetter(i18n.t('save'))
778 <div class="form-group mb-0">
780 class="btn btn-block btn-danger"
783 this.handleDeleteAccountShowConfirmToggle
786 {i18n.t('delete_account')}
788 {this.state.deleteAccountShowConfirm && (
790 <div class="my-2 alert alert-danger" role="alert">
791 {i18n.t('delete_account_confirm')}
795 value={this.state.deleteAccountForm.password}
796 autoComplete="new-password"
799 this.handleDeleteAccountPasswordChange
801 class="form-control my-2"
804 class="btn btn-danger mr-4"
805 disabled={!this.state.deleteAccountForm.password}
806 onClick={linkEvent(this, this.handleDeleteAccount)}
808 {this.state.deleteAccountLoading ? (
809 <svg class="icon icon-spinner spin">
810 <use xlinkHref="#icon-spinner"></use>
813 capitalizeFirstLetter(i18n.t('delete'))
817 class="btn btn-secondary"
820 this.handleDeleteAccountShowConfirmToggle
838 {this.state.userRes.moderates.length > 0 && (
839 <div class="card border-secondary mb-3">
840 <div class="card-body">
841 <h5>{i18n.t('moderates')}</h5>
842 <ul class="list-unstyled mb-0">
843 {this.state.userRes.moderates.map(community => (
847 name: community.community_name,
848 id: community.community_id,
849 local: community.community_local,
850 actor_id: community.community_actor_id,
851 icon: community.community_icon,
867 {this.state.userRes.follows.length > 0 && (
868 <div class="card border-secondary mb-3">
869 <div class="card-body">
870 <h5>{i18n.t('subscribed')}</h5>
871 <ul class="list-unstyled mb-0">
872 {this.state.userRes.follows.map(community => (
876 name: community.community_name,
877 id: community.community_id,
878 local: community.community_local,
879 actor_id: community.community_actor_id,
880 icon: community.community_icon,
893 updateUrl(paramUpdates: UrlParams) {
894 const page = paramUpdates.page || this.state.page;
895 const viewStr = paramUpdates.view || UserDetailsView[this.state.view];
896 const sortStr = paramUpdates.sort || this.state.sort;
897 this.props.history.push(
898 `/u/${this.state.userName}/view/${viewStr}/sort/${sortStr}/page/${page}`
900 this.state.loading = true;
901 this.setState(this.state);
902 this.fetchUserData();
905 handlePageChange(page: number) {
906 this.updateUrl({ page });
909 handleSortChange(val: SortType) {
910 this.updateUrl({ sort: val, page: 1 });
913 handleViewChange(i: User, event: any) {
915 view: UserDetailsView[Number(event.target.value)],
920 handleUserSettingsShowNsfwChange(i: User, event: any) {
921 i.state.userSettingsForm.show_nsfw = event.target.checked;
925 handleUserSettingsShowAvatarsChange(i: User, event: any) {
926 i.state.userSettingsForm.show_avatars = event.target.checked;
927 UserService.Instance.user.show_avatars = event.target.checked; // Just for instant updates
931 handleUserSettingsSendNotificationsToEmailChange(i: User, event: any) {
932 i.state.userSettingsForm.send_notifications_to_email = event.target.checked;
936 handleUserSettingsThemeChange(i: User, event: any) {
937 i.state.userSettingsForm.theme = event.target.value;
938 setTheme(event.target.value, true);
942 handleUserSettingsLangChange(i: User, event: any) {
943 i.state.userSettingsForm.lang = event.target.value;
944 i18n.changeLanguage(getLanguage(i.state.userSettingsForm.lang));
948 handleUserSettingsSortTypeChange(val: SortType) {
949 this.state.userSettingsForm.default_sort_type = Object.keys(
952 this.setState(this.state);
955 handleUserSettingsListingTypeChange(val: ListingType) {
956 this.state.userSettingsForm.default_listing_type = Object.keys(
959 this.setState(this.state);
962 handleUserSettingsEmailChange(i: User, event: any) {
963 i.state.userSettingsForm.email = event.target.value;
967 handleUserSettingsBioChange(val: string) {
968 this.state.userSettingsForm.bio = val;
969 this.setState(this.state);
972 handleAvatarUpload(url: string) {
973 this.state.userSettingsForm.avatar = url;
974 this.setState(this.state);
977 handleAvatarRemove() {
978 this.state.userSettingsForm.avatar = '';
979 this.setState(this.state);
982 handleBannerUpload(url: string) {
983 this.state.userSettingsForm.banner = url;
984 this.setState(this.state);
987 handleBannerRemove() {
988 this.state.userSettingsForm.banner = '';
989 this.setState(this.state);
992 handleUserSettingsPreferredUsernameChange(i: User, event: any) {
993 i.state.userSettingsForm.preferred_username = event.target.value;
997 handleUserSettingsMatrixUserIdChange(i: User, event: any) {
998 i.state.userSettingsForm.matrix_user_id = event.target.value;
1000 i.state.userSettingsForm.matrix_user_id == '' &&
1001 !i.state.userRes.user.matrix_user_id
1003 i.state.userSettingsForm.matrix_user_id = undefined;
1005 i.setState(i.state);
1008 handleUserSettingsNewPasswordChange(i: User, event: any) {
1009 i.state.userSettingsForm.new_password = event.target.value;
1010 if (i.state.userSettingsForm.new_password == '') {
1011 i.state.userSettingsForm.new_password = undefined;
1013 i.setState(i.state);
1016 handleUserSettingsNewPasswordVerifyChange(i: User, event: any) {
1017 i.state.userSettingsForm.new_password_verify = event.target.value;
1018 if (i.state.userSettingsForm.new_password_verify == '') {
1019 i.state.userSettingsForm.new_password_verify = undefined;
1021 i.setState(i.state);
1024 handleUserSettingsOldPasswordChange(i: User, event: any) {
1025 i.state.userSettingsForm.old_password = event.target.value;
1026 if (i.state.userSettingsForm.old_password == '') {
1027 i.state.userSettingsForm.old_password = undefined;
1029 i.setState(i.state);
1032 handleUserSettingsSubmit(i: User, event: any) {
1033 event.preventDefault();
1034 i.state.userSettingsLoading = true;
1035 i.setState(i.state);
1037 WebSocketService.Instance.saveUserSettings(i.state.userSettingsForm);
1040 handleDeleteAccountShowConfirmToggle(i: User, event: any) {
1041 event.preventDefault();
1042 i.state.deleteAccountShowConfirm = !i.state.deleteAccountShowConfirm;
1043 i.setState(i.state);
1046 handleDeleteAccountPasswordChange(i: User, event: any) {
1047 i.state.deleteAccountForm.password = event.target.value;
1048 i.setState(i.state);
1051 handleLogoutClick(i: User) {
1052 UserService.Instance.logout();
1053 i.context.router.history.push('/');
1056 handleDeleteAccount(i: User, event: any) {
1057 event.preventDefault();
1058 i.state.deleteAccountLoading = true;
1059 i.setState(i.state);
1061 WebSocketService.Instance.deleteAccount(i.state.deleteAccountForm);
1062 i.handleLogoutClick(i);
1066 if (this.isCurrentUser) {
1067 this.state.userSettingsForm.show_nsfw =
1068 UserService.Instance.user.show_nsfw;
1069 this.state.userSettingsForm.theme = UserService.Instance.user.theme
1070 ? UserService.Instance.user.theme
1072 this.state.userSettingsForm.default_sort_type =
1073 UserService.Instance.user.default_sort_type;
1074 this.state.userSettingsForm.default_listing_type =
1075 UserService.Instance.user.default_listing_type;
1076 this.state.userSettingsForm.lang = UserService.Instance.user.lang;
1077 this.state.userSettingsForm.avatar = UserService.Instance.user.avatar;
1078 this.state.userSettingsForm.banner = UserService.Instance.user.banner;
1079 this.state.userSettingsForm.preferred_username =
1080 UserService.Instance.user.preferred_username;
1081 this.state.userSettingsForm.show_avatars =
1082 UserService.Instance.user.show_avatars;
1083 this.state.userSettingsForm.email = UserService.Instance.user.email;
1084 this.state.userSettingsForm.bio = UserService.Instance.user.bio;
1085 this.state.userSettingsForm.send_notifications_to_email =
1086 UserService.Instance.user.send_notifications_to_email;
1087 this.state.userSettingsForm.matrix_user_id =
1088 UserService.Instance.user.matrix_user_id;
1092 parseMessage(msg: WebSocketJsonResponse) {
1094 const res = wsJsonToRes(msg);
1096 toast(i18n.t(msg.error), 'danger');
1097 if (msg.error == 'couldnt_find_that_username_or_email') {
1098 this.context.router.history.push('/');
1101 deleteAccountLoading: false,
1102 userSettingsLoading: false,
1105 } else if (msg.reconnect) {
1106 this.fetchUserData();
1107 } else if (res.op == UserOperation.GetUserDetails) {
1108 // Since the UserDetails contains posts/comments as well as some general user info we listen here as well
1109 // and set the parent state if it is not set or differs
1110 // TODO this might need to get abstracted
1111 const data = res.data as UserDetailsResponse;
1112 this.state.userRes = data;
1114 this.state.loading = false;
1115 this.setState(this.state);
1116 } else if (res.op == UserOperation.SaveUserSettings) {
1117 const data = res.data as LoginResponse;
1118 UserService.Instance.login(data);
1119 this.state.userRes.user.bio = this.state.userSettingsForm.bio;
1120 this.state.userRes.user.preferred_username = this.state.userSettingsForm.preferred_username;
1121 this.state.userRes.user.banner = this.state.userSettingsForm.banner;
1122 this.state.userRes.user.avatar = this.state.userSettingsForm.avatar;
1123 this.state.userSettingsLoading = false;
1124 this.setState(this.state);
1126 window.scrollTo(0, 0);
1127 } else if (res.op == UserOperation.DeleteAccount) {
1129 deleteAccountLoading: false,
1130 deleteAccountShowConfirm: false,
1132 this.context.router.history.push('/');
1133 } else if (res.op == UserOperation.AddAdmin) {
1134 const data = res.data as AddAdminResponse;
1135 this.state.siteRes.admins = data.admins;
1136 this.setState(this.state);
1137 } else if (res.op == UserOperation.CreateCommentLike) {
1138 const data = res.data as CommentResponse;
1139 createCommentLikeRes(data, this.state.userRes.comments);
1140 this.setState(this.state);
1142 res.op == UserOperation.EditComment ||
1143 res.op == UserOperation.DeleteComment ||
1144 res.op == UserOperation.RemoveComment
1146 const data = res.data as CommentResponse;
1147 editCommentRes(data, this.state.userRes.comments);
1148 this.setState(this.state);
1149 } else if (res.op == UserOperation.CreateComment) {
1150 const data = res.data as CommentResponse;
1152 UserService.Instance.user &&
1153 data.comment.creator_id == UserService.Instance.user.id
1155 toast(i18n.t('reply_sent'));
1157 } else if (res.op == UserOperation.SaveComment) {
1158 const data = res.data as CommentResponse;
1159 saveCommentRes(data, this.state.userRes.comments);
1160 this.setState(this.state);
1162 res.op == UserOperation.EditPost ||
1163 res.op == UserOperation.DeletePost ||
1164 res.op == UserOperation.RemovePost ||
1165 res.op == UserOperation.LockPost ||
1166 res.op == UserOperation.StickyPost ||
1167 res.op == UserOperation.SavePost
1169 let data = res.data as PostResponse;
1170 editPostFindRes(data, this.state.userRes.posts);
1171 this.setState(this.state);
1172 } else if (res.op == UserOperation.CreatePostLike) {
1173 const data = res.data as PostResponse;
1174 createPostLikeFindRes(data, this.state.userRes.posts);
1175 this.setState(this.state);
1176 } else if (res.op == UserOperation.BanUser) {
1177 const data = res.data as BanUserResponse;
1178 this.state.userRes.comments
1179 .filter(c => c.creator_id == data.user.id)
1180 .forEach(c => (c.banned = data.banned));
1181 this.state.userRes.posts
1182 .filter(c => c.creator_id == data.user.id)
1183 .forEach(c => (c.banned = data.banned));
1184 this.setState(this.state);