15 restoreScrollPosition,
17 } from "@utils/browser";
19 capitalizeFirstLetter,
25 } from "@utils/helpers";
26 import { canMod, isAdmin, isBanned } from "@utils/roles";
27 import type { QueryParams } from "@utils/types";
28 import { RouteDataResponse } from "@utils/types";
29 import classNames from "classnames";
30 import { NoOptionI18nKeys } from "i18next";
31 import { Component, linkEvent } from "inferno";
32 import { Link } from "inferno-router";
33 import { RouteComponentProps } from "inferno-router/dist/Route";
38 BanFromCommunityResponse,
46 CommunityModeratorView,
59 GetPersonDetailsResponse,
62 MarkCommentReplyAsRead,
63 MarkPersonMentionAsRead,
76 } from "lemmy-js-client";
77 import moment from "moment";
78 import { fetchLimit, relTags } from "../../config";
79 import { InitialFetchRequest, PersonDetailsView } from "../../interfaces";
80 import { mdToHtml } from "../../markdown";
81 import { FirstLoadService, I18NextService, UserService } from "../../services";
82 import { HttpService, RequestState } from "../../services/HttpService";
83 import { setupTippy } from "../../tippy";
84 import { toast } from "../../toast";
85 import { BannerIconHeader } from "../common/banner-icon-header";
86 import { HtmlTags } from "../common/html-tags";
87 import { Icon, Spinner } from "../common/icon";
88 import { MomentTime } from "../common/moment-time";
89 import { SortSelect } from "../common/sort-select";
90 import { CommunityLink } from "../community/community-link";
91 import { PersonDetails } from "./person-details";
92 import { PersonListing } from "./person-listing";
94 type ProfileData = RouteDataResponse<{
95 personResponse: GetPersonDetailsResponse;
98 interface ProfileState {
99 personRes: RequestState<GetPersonDetailsResponse>;
100 personBlocked: boolean;
102 banExpireDays?: number;
103 showBanDialog: boolean;
105 siteRes: GetSiteResponse;
106 finished: Map<CommentId, boolean | undefined>;
107 isIsomorphic: boolean;
110 interface ProfileProps {
111 view: PersonDetailsView;
116 function getProfileQueryParams() {
117 return getQueryParams<ProfileProps>({
118 view: getViewFromProps,
119 page: getPageFromString,
120 sort: getSortTypeFromQuery,
124 function getSortTypeFromQuery(sort?: string): SortType {
125 return sort ? (sort as SortType) : "New";
128 function getViewFromProps(view?: string): PersonDetailsView {
130 ? PersonDetailsView[view] ?? PersonDetailsView.Overview
131 : PersonDetailsView.Overview;
134 const getCommunitiesListing = (
135 translationKey: NoOptionI18nKeys,
136 communityViews?: { community: Community }[]
139 communityViews.length > 0 && (
140 <div className="card border-secondary mb-3">
141 <div className="card-body">
142 <h5>{I18NextService.i18n.t(translationKey)}</h5>
143 <ul className="list-unstyled mb-0">
144 {communityViews.map(({ community }) => (
145 <li key={community.id}>
146 <CommunityLink community={community} />
154 const Moderates = ({ moderates }: { moderates?: CommunityModeratorView[] }) =>
155 getCommunitiesListing("moderates", moderates);
157 const Follows = () =>
158 getCommunitiesListing("subscribed", UserService.Instance.myUserInfo?.follows);
160 export class Profile extends Component<
161 RouteComponentProps<{ username: string }>,
164 private isoData = setIsoData<ProfileData>(this.context);
165 state: ProfileState = {
166 personRes: { state: "empty" },
167 personBlocked: false,
168 siteRes: this.isoData.site_res,
169 showBanDialog: false,
175 constructor(props: RouteComponentProps<{ username: string }>, context: any) {
176 super(props, context);
178 this.handleSortChange = this.handleSortChange.bind(this);
179 this.handlePageChange = this.handlePageChange.bind(this);
181 this.handleBlockPerson = this.handleBlockPerson.bind(this);
182 this.handleUnblockPerson = this.handleUnblockPerson.bind(this);
184 this.handleCreateComment = this.handleCreateComment.bind(this);
185 this.handleEditComment = this.handleEditComment.bind(this);
186 this.handleSaveComment = this.handleSaveComment.bind(this);
187 this.handleBlockPersonAlt = this.handleBlockPersonAlt.bind(this);
188 this.handleDeleteComment = this.handleDeleteComment.bind(this);
189 this.handleRemoveComment = this.handleRemoveComment.bind(this);
190 this.handleCommentVote = this.handleCommentVote.bind(this);
191 this.handleAddModToCommunity = this.handleAddModToCommunity.bind(this);
192 this.handleAddAdmin = this.handleAddAdmin.bind(this);
193 this.handlePurgePerson = this.handlePurgePerson.bind(this);
194 this.handlePurgeComment = this.handlePurgeComment.bind(this);
195 this.handleCommentReport = this.handleCommentReport.bind(this);
196 this.handleDistinguishComment = this.handleDistinguishComment.bind(this);
197 this.handleTransferCommunity = this.handleTransferCommunity.bind(this);
198 this.handleCommentReplyRead = this.handleCommentReplyRead.bind(this);
199 this.handlePersonMentionRead = this.handlePersonMentionRead.bind(this);
200 this.handleBanFromCommunity = this.handleBanFromCommunity.bind(this);
201 this.handleBanPerson = this.handleBanPerson.bind(this);
202 this.handlePostVote = this.handlePostVote.bind(this);
203 this.handlePostEdit = this.handlePostEdit.bind(this);
204 this.handlePostReport = this.handlePostReport.bind(this);
205 this.handleLockPost = this.handleLockPost.bind(this);
206 this.handleDeletePost = this.handleDeletePost.bind(this);
207 this.handleRemovePost = this.handleRemovePost.bind(this);
208 this.handleSavePost = this.handleSavePost.bind(this);
209 this.handlePurgePost = this.handlePurgePost.bind(this);
210 this.handleFeaturePost = this.handleFeaturePost.bind(this);
212 // Only fetch the data if coming from another route
213 if (!isBrowser() || FirstLoadService.isFirstLoad) {
216 personRes: this.isoData.routeData.personResponse,
222 async componentDidMount() {
223 if (!this.state.isIsomorphic) {
224 await this.fetchUserData();
229 componentWillUnmount() {
230 saveScrollPosition(this.context);
233 async fetchUserData() {
234 const { page, sort, view } = getProfileQueryParams();
236 this.setState({ personRes: { state: "empty" } });
238 personRes: await HttpService.client.getPersonDetails({
239 username: this.props.match.params.username,
241 saved_only: view === PersonDetailsView.Saved,
247 restoreScrollPosition(this.context);
248 this.setPersonBlock();
251 get amCurrentUser() {
252 if (this.state.personRes.state === "success") {
254 UserService.Instance.myUserInfo?.local_user_view.person.id ===
255 this.state.personRes.data.person_view.person.id
263 const mui = UserService.Instance.myUserInfo;
264 const res = this.state.personRes;
266 if (mui && res.state === "success") {
268 personBlocked: mui.person_blocks.some(
269 ({ target: { id } }) => id === res.data.person_view.person.id
275 static async fetchInitialData({
278 query: { page, sort, view: urlView },
280 }: InitialFetchRequest<QueryParams<ProfileProps>>): Promise<ProfileData> {
281 const pathSplit = path.split("/");
283 const username = pathSplit[2];
284 const view = getViewFromProps(urlView);
286 const form: GetPersonDetails = {
288 sort: getSortTypeFromQuery(sort),
289 saved_only: view === PersonDetailsView.Saved,
290 page: getPageFromString(page),
296 personResponse: await client.getPersonDetails(form),
300 get documentTitle(): string {
301 const siteName = this.state.siteRes.site_view.site.name;
302 const res = this.state.personRes;
303 return res.state == "success"
304 ? `@${res.data.person_view.person.name} - ${siteName}`
309 switch (this.state.personRes.state) {
317 const siteRes = this.state.siteRes;
318 const personRes = this.state.personRes.data;
319 const { page, sort, view } = getProfileQueryParams();
322 <div className="row">
323 <div className="col-12 col-md-8">
325 title={this.documentTitle}
326 path={this.context.router.route.match.url}
327 description={personRes.person_view.person.bio}
328 image={personRes.person_view.person.avatar}
331 {this.userInfo(personRes.person_view)}
338 personRes={personRes}
339 admins={siteRes.admins}
343 finished={this.state.finished}
344 enableDownvotes={enableDownvotes(siteRes)}
345 enableNsfw={enableNsfw(siteRes)}
347 onPageChange={this.handlePageChange}
348 allLanguages={siteRes.all_languages}
349 siteLanguages={siteRes.discussion_languages}
350 // TODO all the forms here
351 onSaveComment={this.handleSaveComment}
352 onBlockPerson={this.handleBlockPersonAlt}
353 onDeleteComment={this.handleDeleteComment}
354 onRemoveComment={this.handleRemoveComment}
355 onCommentVote={this.handleCommentVote}
356 onCommentReport={this.handleCommentReport}
357 onDistinguishComment={this.handleDistinguishComment}
358 onAddModToCommunity={this.handleAddModToCommunity}
359 onAddAdmin={this.handleAddAdmin}
360 onTransferCommunity={this.handleTransferCommunity}
361 onPurgeComment={this.handlePurgeComment}
362 onPurgePerson={this.handlePurgePerson}
363 onCommentReplyRead={this.handleCommentReplyRead}
364 onPersonMentionRead={this.handlePersonMentionRead}
365 onBanPersonFromCommunity={this.handleBanFromCommunity}
366 onBanPerson={this.handleBanPerson}
367 onCreateComment={this.handleCreateComment}
368 onEditComment={this.handleEditComment}
369 onPostEdit={this.handlePostEdit}
370 onPostVote={this.handlePostVote}
371 onPostReport={this.handlePostReport}
372 onLockPost={this.handleLockPost}
373 onDeletePost={this.handleDeletePost}
374 onRemovePost={this.handleRemovePost}
375 onSavePost={this.handleSavePost}
376 onPurgePost={this.handlePurgePost}
377 onFeaturePost={this.handleFeaturePost}
381 <div className="col-12 col-md-4">
382 <Moderates moderates={personRes.moderates} />
383 {this.amCurrentUser && <Follows />}
393 <div className="person-profile container-lg">
394 {this.renderPersonRes()}
401 <div className="btn-group btn-group-toggle flex-wrap mb-2">
402 {this.getRadio(PersonDetailsView.Overview)}
403 {this.getRadio(PersonDetailsView.Comments)}
404 {this.getRadio(PersonDetailsView.Posts)}
405 {this.amCurrentUser && this.getRadio(PersonDetailsView.Saved)}
410 getRadio(view: PersonDetailsView) {
411 const { view: urlView } = getProfileQueryParams();
412 const active = view === urlView;
416 className={classNames("btn btn-outline-secondary pointer", {
422 className="btn-check"
425 onChange={linkEvent(this, this.handleViewChange)}
427 {I18NextService.i18n.t(view.toLowerCase() as NoOptionI18nKeys)}
433 const { sort } = getProfileQueryParams();
434 const { username } = this.props.match.params;
436 const profileRss = `/feeds/u/${username}.xml?sort=${sort}`;
439 <div className="mb-2">
440 <span className="me-3">{this.viewRadios}</span>
443 onChange={this.handleSortChange}
447 <a href={profileRss} rel={relTags} title="RSS">
448 <Icon icon="rss" classes="text-muted small mx-2" />
450 <link rel="alternate" type="application/atom+xml" href={profileRss} />
455 userInfo(pv: PersonView) {
465 {!isBanned(pv.person) && (
467 banner={pv.person.banner}
468 icon={pv.person.avatar}
471 <div className="mb-3">
473 <div className="mb-0 d-flex flex-wrap">
475 {pv.person.display_name && (
476 <h5 className="mb-0">{pv.person.display_name}</h5>
478 <ul className="list-inline mb-2">
479 <li className="list-inline-item">
488 {isBanned(pv.person) && (
489 <li className="list-inline-item badge text-bg-danger">
490 {I18NextService.i18n.t("banned")}
493 {pv.person.deleted && (
494 <li className="list-inline-item badge text-bg-danger">
495 {I18NextService.i18n.t("deleted")}
498 {pv.person.admin && (
499 <li className="list-inline-item badge text-bg-light">
500 {I18NextService.i18n.t("admin")}
503 {pv.person.bot_account && (
504 <li className="list-inline-item badge text-bg-light">
505 {I18NextService.i18n.t("bot_account").toLowerCase()}
511 <div className="flex-grow-1 unselectable pointer mx-2"></div>
512 {!this.amCurrentUser && UserService.Instance.myUserInfo && (
515 className={`d-flex align-self-start btn btn-secondary me-2 ${
516 !pv.person.matrix_user_id && "invisible"
519 href={`https://matrix.to/#/${pv.person.matrix_user_id}`}
521 {I18NextService.i18n.t("send_secure_message")}
525 "d-flex align-self-start btn btn-secondary me-2"
527 to={`/create_private_message/${pv.person.id}`}
529 {I18NextService.i18n.t("send_message")}
534 "d-flex align-self-start btn btn-secondary me-2"
538 this.handleUnblockPerson
541 {I18NextService.i18n.t("unblock_user")}
546 "d-flex align-self-start btn btn-secondary me-2"
550 this.handleBlockPerson
553 {I18NextService.i18n.t("block_user")}
559 {canMod(pv.person.id, undefined, admins) &&
560 !isAdmin(pv.person.id, admins) &&
562 (!isBanned(pv.person) ? (
565 "d-flex align-self-start btn btn-secondary me-2"
567 onClick={linkEvent(this, this.handleModBanShow)}
568 aria-label={I18NextService.i18n.t("ban")}
570 {capitalizeFirstLetter(I18NextService.i18n.t("ban"))}
575 "d-flex align-self-start btn btn-secondary me-2"
577 onClick={linkEvent(this, this.handleModBanSubmit)}
578 aria-label={I18NextService.i18n.t("unban")}
580 {capitalizeFirstLetter(I18NextService.i18n.t("unban"))}
585 <div className="d-flex align-items-center mb-2">
588 dangerouslySetInnerHTML={mdToHtml(pv.person.bio)}
593 <ul className="list-inline mb-2">
594 <li className="list-inline-item badge text-bg-light">
595 {I18NextService.i18n.t("number_of_posts", {
596 count: Number(pv.counts.post_count),
597 formattedCount: numToSI(pv.counts.post_count),
600 <li className="list-inline-item badge text-bg-light">
601 {I18NextService.i18n.t("number_of_comments", {
602 count: Number(pv.counts.comment_count),
603 formattedCount: numToSI(pv.counts.comment_count),
608 <div className="text-muted">
609 {I18NextService.i18n.t("joined")}{" "}
611 published={pv.person.published}
616 <div className="d-flex align-items-center text-muted mb-2">
618 <span className="ms-2">
619 {I18NextService.i18n.t("cake_day_title")}{" "}
621 .utc(pv.person.published)
623 .format("MMM DD, YYYY")}
626 {!UserService.Instance.myUserInfo && (
627 <div className="alert alert-info" role="alert">
628 {I18NextService.i18n.t("profile_not_logged_in_alert")}
638 banDialog(pv: PersonView) {
639 const { showBanDialog } = this.state;
643 <form onSubmit={linkEvent(this, this.handleModBanSubmit)}>
644 <div className="mb-3 row col-12">
645 <label className="col-form-label" htmlFor="profile-ban-reason">
646 {I18NextService.i18n.t("reason")}
650 id="profile-ban-reason"
651 className="form-control me-2"
652 placeholder={I18NextService.i18n.t("reason")}
653 value={this.state.banReason}
654 onInput={linkEvent(this, this.handleModBanReasonChange)}
656 <label className="col-form-label" htmlFor={`mod-ban-expires`}>
657 {I18NextService.i18n.t("expires")}
661 id={`mod-ban-expires`}
662 className="form-control me-2"
663 placeholder={I18NextService.i18n.t("number_of_days")}
664 value={this.state.banExpireDays}
665 onInput={linkEvent(this, this.handleModBanExpireDaysChange)}
667 <div className="input-group mb-3">
668 <div className="form-check">
670 className="form-check-input"
671 id="mod-ban-remove-data"
673 checked={this.state.removeData}
674 onChange={linkEvent(this, this.handleModRemoveDataChange)}
677 className="form-check-label"
678 htmlFor="mod-ban-remove-data"
679 title={I18NextService.i18n.t("remove_content_more")}
681 {I18NextService.i18n.t("remove_content")}
686 {/* TODO hold off on expires until later */}
687 {/* <div class="mb-3 row"> */}
688 {/* <label class="col-form-label">Expires</label> */}
689 {/* <input type="date" class="form-control me-2" placeholder={I18NextService.i18n.t('expires')} value={this.state.banExpires} onInput={linkEvent(this, this.handleModBanExpiresChange)} /> */}
691 <div className="mb-3 row">
694 className="btn btn-secondary me-2"
695 aria-label={I18NextService.i18n.t("cancel")}
696 onClick={linkEvent(this, this.handleModBanSubmitCancel)}
698 {I18NextService.i18n.t("cancel")}
702 className="btn btn-secondary"
703 aria-label={I18NextService.i18n.t("ban")}
705 {I18NextService.i18n.t("ban")} {pv.person.name}
713 async updateUrl({ page, sort, view }: Partial<ProfileProps>) {
718 } = getProfileQueryParams();
720 const queryParams: QueryParams<ProfileProps> = {
721 page: (page ?? urlPage).toString(),
722 sort: sort ?? urlSort,
723 view: view ?? urlView,
726 const { username } = this.props.match.params;
728 this.props.history.push(`/u/${username}${getQueryString(queryParams)}`);
729 await this.fetchUserData();
732 handlePageChange(page: number) {
733 this.updateUrl({ page });
736 handleSortChange(sort: SortType) {
737 this.updateUrl({ sort, page: 1 });
740 handleViewChange(i: Profile, event: any) {
742 view: PersonDetailsView[event.target.value],
747 handleModBanShow(i: Profile) {
748 i.setState({ showBanDialog: true });
751 handleModBanReasonChange(i: Profile, event: any) {
752 i.setState({ banReason: event.target.value });
755 handleModBanExpireDaysChange(i: Profile, event: any) {
756 i.setState({ banExpireDays: event.target.value });
759 handleModRemoveDataChange(i: Profile, event: any) {
760 i.setState({ removeData: event.target.checked });
763 handleModBanSubmitCancel(i: Profile) {
764 i.setState({ showBanDialog: false });
767 async handleModBanSubmit(i: Profile, event: any) {
768 event.preventDefault();
769 const { removeData, banReason, banExpireDays } = i.state;
771 const personRes = i.state.personRes;
773 if (personRes.state == "success") {
774 const person = personRes.data.person_view.person;
775 const ban = !person.banned;
777 // If its an unban, restore all their data
779 i.setState({ removeData: false });
782 const res = await HttpService.client.banPerson({
783 person_id: person.id,
785 remove_data: removeData,
787 expires: futureDaysToUnixTime(banExpireDays),
788 auth: myAuthRequired(),
792 i.setState({ showBanDialog: false });
796 async toggleBlockPerson(recipientId: number, block: boolean) {
797 const res = await HttpService.client.blockPerson({
798 person_id: recipientId,
800 auth: myAuthRequired(),
802 if (res.state == "success") {
803 updatePersonBlock(res.data);
807 handleUnblockPerson(personId: number) {
808 this.toggleBlockPerson(personId, false);
811 handleBlockPerson(personId: number) {
812 this.toggleBlockPerson(personId, true);
815 async handleAddModToCommunity(form: AddModToCommunity) {
816 // TODO not sure what to do here
817 await HttpService.client.addModToCommunity(form);
820 async handlePurgePerson(form: PurgePerson) {
821 const purgePersonRes = await HttpService.client.purgePerson(form);
822 this.purgeItem(purgePersonRes);
825 async handlePurgeComment(form: PurgeComment) {
826 const purgeCommentRes = await HttpService.client.purgeComment(form);
827 this.purgeItem(purgeCommentRes);
830 async handlePurgePost(form: PurgePost) {
831 const purgeRes = await HttpService.client.purgePost(form);
832 this.purgeItem(purgeRes);
835 async handleBlockPersonAlt(form: BlockPerson) {
836 const blockPersonRes = await HttpService.client.blockPerson(form);
837 if (blockPersonRes.state === "success") {
838 updatePersonBlock(blockPersonRes.data);
842 async handleCreateComment(form: CreateComment) {
843 const createCommentRes = await HttpService.client.createComment(form);
844 this.createAndUpdateComments(createCommentRes);
846 return createCommentRes;
849 async handleEditComment(form: EditComment) {
850 const editCommentRes = await HttpService.client.editComment(form);
851 this.findAndUpdateComment(editCommentRes);
853 return editCommentRes;
856 async handleDeleteComment(form: DeleteComment) {
857 const deleteCommentRes = await HttpService.client.deleteComment(form);
858 this.findAndUpdateComment(deleteCommentRes);
861 async handleDeletePost(form: DeletePost) {
862 const deleteRes = await HttpService.client.deletePost(form);
863 this.findAndUpdatePost(deleteRes);
866 async handleRemovePost(form: RemovePost) {
867 const removeRes = await HttpService.client.removePost(form);
868 this.findAndUpdatePost(removeRes);
871 async handleRemoveComment(form: RemoveComment) {
872 const removeCommentRes = await HttpService.client.removeComment(form);
873 this.findAndUpdateComment(removeCommentRes);
876 async handleSaveComment(form: SaveComment) {
877 const saveCommentRes = await HttpService.client.saveComment(form);
878 this.findAndUpdateComment(saveCommentRes);
881 async handleSavePost(form: SavePost) {
882 const saveRes = await HttpService.client.savePost(form);
883 this.findAndUpdatePost(saveRes);
886 async handleFeaturePost(form: FeaturePost) {
887 const featureRes = await HttpService.client.featurePost(form);
888 this.findAndUpdatePost(featureRes);
891 async handleCommentVote(form: CreateCommentLike) {
892 const voteRes = await HttpService.client.likeComment(form);
893 this.findAndUpdateComment(voteRes);
896 async handlePostVote(form: CreatePostLike) {
897 const voteRes = await HttpService.client.likePost(form);
898 this.findAndUpdatePost(voteRes);
901 async handlePostEdit(form: EditPost) {
902 const res = await HttpService.client.editPost(form);
903 this.findAndUpdatePost(res);
906 async handleCommentReport(form: CreateCommentReport) {
907 const reportRes = await HttpService.client.createCommentReport(form);
908 if (reportRes.state === "success") {
909 toast(I18NextService.i18n.t("report_created"));
913 async handlePostReport(form: CreatePostReport) {
914 const reportRes = await HttpService.client.createPostReport(form);
915 if (reportRes.state === "success") {
916 toast(I18NextService.i18n.t("report_created"));
920 async handleLockPost(form: LockPost) {
921 const lockRes = await HttpService.client.lockPost(form);
922 this.findAndUpdatePost(lockRes);
925 async handleDistinguishComment(form: DistinguishComment) {
926 const distinguishRes = await HttpService.client.distinguishComment(form);
927 this.findAndUpdateComment(distinguishRes);
930 async handleAddAdmin(form: AddAdmin) {
931 const addAdminRes = await HttpService.client.addAdmin(form);
933 if (addAdminRes.state == "success") {
934 this.setState(s => ((s.siteRes.admins = addAdminRes.data.admins), s));
938 async handleTransferCommunity(form: TransferCommunity) {
939 await HttpService.client.transferCommunity(form);
940 toast(I18NextService.i18n.t("transfer_community"));
943 async handleCommentReplyRead(form: MarkCommentReplyAsRead) {
944 const readRes = await HttpService.client.markCommentReplyAsRead(form);
945 this.findAndUpdateCommentReply(readRes);
948 async handlePersonMentionRead(form: MarkPersonMentionAsRead) {
949 // TODO not sure what to do here. Maybe it is actually optional, because post doesn't need it.
950 await HttpService.client.markPersonMentionAsRead(form);
953 async handleBanFromCommunity(form: BanFromCommunity) {
954 const banRes = await HttpService.client.banFromCommunity(form);
955 this.updateBanFromCommunity(banRes);
958 async handleBanPerson(form: BanPerson) {
959 const banRes = await HttpService.client.banPerson(form);
960 this.updateBan(banRes);
963 updateBanFromCommunity(banRes: RequestState<BanFromCommunityResponse>) {
964 // Maybe not necessary
965 if (banRes.state === "success") {
967 if (s.personRes.state == "success") {
968 s.personRes.data.posts
969 .filter(c => c.creator.id === banRes.data.person_view.person.id)
971 c => (c.creator_banned_from_community = banRes.data.banned)
974 s.personRes.data.comments
975 .filter(c => c.creator.id === banRes.data.person_view.person.id)
977 c => (c.creator_banned_from_community = banRes.data.banned)
985 updateBan(banRes: RequestState<BanPersonResponse>) {
986 // Maybe not necessary
987 if (banRes.state == "success") {
989 if (s.personRes.state == "success") {
990 s.personRes.data.posts
991 .filter(c => c.creator.id == banRes.data.person_view.person.id)
992 .forEach(c => (c.creator.banned = banRes.data.banned));
993 s.personRes.data.comments
994 .filter(c => c.creator.id == banRes.data.person_view.person.id)
995 .forEach(c => (c.creator.banned = banRes.data.banned));
1002 purgeItem(purgeRes: RequestState<PurgeItemResponse>) {
1003 if (purgeRes.state == "success") {
1004 toast(I18NextService.i18n.t("purge_success"));
1005 this.context.router.history.push(`/`);
1009 findAndUpdateComment(res: RequestState<CommentResponse>) {
1010 this.setState(s => {
1011 if (s.personRes.state == "success" && res.state == "success") {
1012 s.personRes.data.comments = editComment(
1013 res.data.comment_view,
1014 s.personRes.data.comments
1016 s.finished.set(res.data.comment_view.comment.id, true);
1022 createAndUpdateComments(res: RequestState<CommentResponse>) {
1023 this.setState(s => {
1024 if (s.personRes.state == "success" && res.state == "success") {
1025 s.personRes.data.comments.unshift(res.data.comment_view);
1026 // Set finished for the parent
1028 getCommentParentId(res.data.comment_view.comment) ?? 0,
1036 findAndUpdateCommentReply(res: RequestState<CommentReplyResponse>) {
1037 this.setState(s => {
1038 if (s.personRes.state == "success" && res.state == "success") {
1039 s.personRes.data.comments = editWith(
1040 res.data.comment_reply_view,
1041 s.personRes.data.comments
1048 findAndUpdatePost(res: RequestState<PostResponse>) {
1049 this.setState(s => {
1050 if (s.personRes.state == "success" && res.state == "success") {
1051 s.personRes.data.posts = editPost(
1053 s.personRes.data.posts