13 import { restoreScrollPosition, saveScrollPosition } from "@utils/browser";
15 capitalizeFirstLetter,
21 } from "@utils/helpers";
22 import { canMod, isAdmin, isBanned } from "@utils/roles";
23 import type { QueryParams } from "@utils/types";
24 import { RouteDataResponse } from "@utils/types";
25 import classNames from "classnames";
26 import { NoOptionI18nKeys } from "i18next";
27 import { Component, linkEvent } from "inferno";
28 import { Link } from "inferno-router";
29 import { RouteComponentProps } from "inferno-router/dist/Route";
34 BanFromCommunityResponse,
42 CommunityModeratorView,
55 GetPersonDetailsResponse,
58 MarkCommentReplyAsRead,
59 MarkPersonMentionAsRead,
72 } from "lemmy-js-client";
73 import moment from "moment";
74 import { fetchLimit, relTags } from "../../config";
75 import { InitialFetchRequest, PersonDetailsView } from "../../interfaces";
76 import { mdToHtml } from "../../markdown";
77 import { FirstLoadService, I18NextService, UserService } from "../../services";
78 import { HttpService, RequestState } from "../../services/HttpService";
79 import { setupTippy } from "../../tippy";
80 import { toast } from "../../toast";
81 import { BannerIconHeader } from "../common/banner-icon-header";
82 import { HtmlTags } from "../common/html-tags";
83 import { Icon, Spinner } from "../common/icon";
84 import { MomentTime } from "../common/moment-time";
85 import { SortSelect } from "../common/sort-select";
86 import { CommunityLink } from "../community/community-link";
87 import { PersonDetails } from "./person-details";
88 import { PersonListing } from "./person-listing";
90 type ProfileData = RouteDataResponse<{
91 personResponse: GetPersonDetailsResponse;
94 interface ProfileState {
95 personRes: RequestState<GetPersonDetailsResponse>;
96 personBlocked: boolean;
98 banExpireDays?: number;
99 showBanDialog: boolean;
101 siteRes: GetSiteResponse;
102 finished: Map<CommentId, boolean | undefined>;
103 isIsomorphic: boolean;
106 interface ProfileProps {
107 view: PersonDetailsView;
112 function getProfileQueryParams() {
113 return getQueryParams<ProfileProps>({
114 view: getViewFromProps,
115 page: getPageFromString,
116 sort: getSortTypeFromQuery,
120 function getSortTypeFromQuery(sort?: string): SortType {
121 return sort ? (sort as SortType) : "New";
124 function getViewFromProps(view?: string): PersonDetailsView {
126 ? PersonDetailsView[view] ?? PersonDetailsView.Overview
127 : PersonDetailsView.Overview;
130 const getCommunitiesListing = (
131 translationKey: NoOptionI18nKeys,
132 communityViews?: { community: Community }[]
135 communityViews.length > 0 && (
136 <div className="card border-secondary mb-3">
137 <div className="card-body">
138 <h5>{I18NextService.i18n.t(translationKey)}</h5>
139 <ul className="list-unstyled mb-0">
140 {communityViews.map(({ community }) => (
141 <li key={community.id}>
142 <CommunityLink community={community} />
150 const Moderates = ({ moderates }: { moderates?: CommunityModeratorView[] }) =>
151 getCommunitiesListing("moderates", moderates);
153 const Follows = () =>
154 getCommunitiesListing("subscribed", UserService.Instance.myUserInfo?.follows);
156 export class Profile extends Component<
157 RouteComponentProps<{ username: string }>,
160 private isoData = setIsoData<ProfileData>(this.context);
161 state: ProfileState = {
162 personRes: { state: "empty" },
163 personBlocked: false,
164 siteRes: this.isoData.site_res,
165 showBanDialog: false,
171 constructor(props: RouteComponentProps<{ username: string }>, context: any) {
172 super(props, context);
174 this.handleSortChange = this.handleSortChange.bind(this);
175 this.handlePageChange = this.handlePageChange.bind(this);
177 this.handleBlockPerson = this.handleBlockPerson.bind(this);
178 this.handleUnblockPerson = this.handleUnblockPerson.bind(this);
180 this.handleCreateComment = this.handleCreateComment.bind(this);
181 this.handleEditComment = this.handleEditComment.bind(this);
182 this.handleSaveComment = this.handleSaveComment.bind(this);
183 this.handleBlockPersonAlt = this.handleBlockPersonAlt.bind(this);
184 this.handleDeleteComment = this.handleDeleteComment.bind(this);
185 this.handleRemoveComment = this.handleRemoveComment.bind(this);
186 this.handleCommentVote = this.handleCommentVote.bind(this);
187 this.handleAddModToCommunity = this.handleAddModToCommunity.bind(this);
188 this.handleAddAdmin = this.handleAddAdmin.bind(this);
189 this.handlePurgePerson = this.handlePurgePerson.bind(this);
190 this.handlePurgeComment = this.handlePurgeComment.bind(this);
191 this.handleCommentReport = this.handleCommentReport.bind(this);
192 this.handleDistinguishComment = this.handleDistinguishComment.bind(this);
193 this.handleTransferCommunity = this.handleTransferCommunity.bind(this);
194 this.handleCommentReplyRead = this.handleCommentReplyRead.bind(this);
195 this.handlePersonMentionRead = this.handlePersonMentionRead.bind(this);
196 this.handleBanFromCommunity = this.handleBanFromCommunity.bind(this);
197 this.handleBanPerson = this.handleBanPerson.bind(this);
198 this.handlePostVote = this.handlePostVote.bind(this);
199 this.handlePostEdit = this.handlePostEdit.bind(this);
200 this.handlePostReport = this.handlePostReport.bind(this);
201 this.handleLockPost = this.handleLockPost.bind(this);
202 this.handleDeletePost = this.handleDeletePost.bind(this);
203 this.handleRemovePost = this.handleRemovePost.bind(this);
204 this.handleSavePost = this.handleSavePost.bind(this);
205 this.handlePurgePost = this.handlePurgePost.bind(this);
206 this.handleFeaturePost = this.handleFeaturePost.bind(this);
208 // Only fetch the data if coming from another route
209 if (FirstLoadService.isFirstLoad) {
212 personRes: this.isoData.routeData.personResponse,
218 async componentDidMount() {
219 if (!this.state.isIsomorphic) {
220 await this.fetchUserData();
225 componentWillUnmount() {
226 saveScrollPosition(this.context);
229 async fetchUserData() {
230 const { page, sort, view } = getProfileQueryParams();
232 this.setState({ personRes: { state: "empty" } });
234 personRes: await HttpService.client.getPersonDetails({
235 username: this.props.match.params.username,
237 saved_only: view === PersonDetailsView.Saved,
243 restoreScrollPosition(this.context);
244 this.setPersonBlock();
247 get amCurrentUser() {
248 if (this.state.personRes.state === "success") {
250 UserService.Instance.myUserInfo?.local_user_view.person.id ===
251 this.state.personRes.data.person_view.person.id
259 const mui = UserService.Instance.myUserInfo;
260 const res = this.state.personRes;
262 if (mui && res.state === "success") {
264 personBlocked: mui.person_blocks.some(
265 ({ target: { id } }) => id === res.data.person_view.person.id
271 static async fetchInitialData({
274 query: { page, sort, view: urlView },
276 }: InitialFetchRequest<QueryParams<ProfileProps>>): Promise<ProfileData> {
277 const pathSplit = path.split("/");
279 const username = pathSplit[2];
280 const view = getViewFromProps(urlView);
282 const form: GetPersonDetails = {
284 sort: getSortTypeFromQuery(sort),
285 saved_only: view === PersonDetailsView.Saved,
286 page: getPageFromString(page),
292 personResponse: await client.getPersonDetails(form),
296 get documentTitle(): string {
297 const siteName = this.state.siteRes.site_view.site.name;
298 const res = this.state.personRes;
299 return res.state == "success"
300 ? `@${res.data.person_view.person.name} - ${siteName}`
305 switch (this.state.personRes.state) {
313 const siteRes = this.state.siteRes;
314 const personRes = this.state.personRes.data;
315 const { page, sort, view } = getProfileQueryParams();
318 <div className="row">
319 <div className="col-12 col-md-8">
321 title={this.documentTitle}
322 path={this.context.router.route.match.url}
323 description={personRes.person_view.person.bio}
324 image={personRes.person_view.person.avatar}
327 {this.userInfo(personRes.person_view)}
334 personRes={personRes}
335 admins={siteRes.admins}
339 finished={this.state.finished}
340 enableDownvotes={enableDownvotes(siteRes)}
341 enableNsfw={enableNsfw(siteRes)}
343 onPageChange={this.handlePageChange}
344 allLanguages={siteRes.all_languages}
345 siteLanguages={siteRes.discussion_languages}
346 // TODO all the forms here
347 onSaveComment={this.handleSaveComment}
348 onBlockPerson={this.handleBlockPersonAlt}
349 onDeleteComment={this.handleDeleteComment}
350 onRemoveComment={this.handleRemoveComment}
351 onCommentVote={this.handleCommentVote}
352 onCommentReport={this.handleCommentReport}
353 onDistinguishComment={this.handleDistinguishComment}
354 onAddModToCommunity={this.handleAddModToCommunity}
355 onAddAdmin={this.handleAddAdmin}
356 onTransferCommunity={this.handleTransferCommunity}
357 onPurgeComment={this.handlePurgeComment}
358 onPurgePerson={this.handlePurgePerson}
359 onCommentReplyRead={this.handleCommentReplyRead}
360 onPersonMentionRead={this.handlePersonMentionRead}
361 onBanPersonFromCommunity={this.handleBanFromCommunity}
362 onBanPerson={this.handleBanPerson}
363 onCreateComment={this.handleCreateComment}
364 onEditComment={this.handleEditComment}
365 onPostEdit={this.handlePostEdit}
366 onPostVote={this.handlePostVote}
367 onPostReport={this.handlePostReport}
368 onLockPost={this.handleLockPost}
369 onDeletePost={this.handleDeletePost}
370 onRemovePost={this.handleRemovePost}
371 onSavePost={this.handleSavePost}
372 onPurgePost={this.handlePurgePost}
373 onFeaturePost={this.handleFeaturePost}
377 <div className="col-12 col-md-4">
378 <Moderates moderates={personRes.moderates} />
379 {this.amCurrentUser && <Follows />}
389 <div className="person-profile container-lg">
390 {this.renderPersonRes()}
397 <div className="btn-group btn-group-toggle flex-wrap mb-2">
398 {this.getRadio(PersonDetailsView.Overview)}
399 {this.getRadio(PersonDetailsView.Comments)}
400 {this.getRadio(PersonDetailsView.Posts)}
401 {this.amCurrentUser && this.getRadio(PersonDetailsView.Saved)}
406 getRadio(view: PersonDetailsView) {
407 const { view: urlView } = getProfileQueryParams();
408 const active = view === urlView;
412 className={classNames("btn btn-outline-secondary pointer", {
418 className="btn-check"
421 onChange={linkEvent(this, this.handleViewChange)}
423 {I18NextService.i18n.t(view.toLowerCase() as NoOptionI18nKeys)}
429 const { sort } = getProfileQueryParams();
430 const { username } = this.props.match.params;
432 const profileRss = `/feeds/u/${username}.xml?sort=${sort}`;
435 <div className="mb-2">
436 <span className="me-3">{this.viewRadios}</span>
439 onChange={this.handleSortChange}
443 <a href={profileRss} rel={relTags} title="RSS">
444 <Icon icon="rss" classes="text-muted small mx-2" />
446 <link rel="alternate" type="application/atom+xml" href={profileRss} />
451 userInfo(pv: PersonView) {
461 {!isBanned(pv.person) && (
463 banner={pv.person.banner}
464 icon={pv.person.avatar}
467 <div className="mb-3">
469 <div className="mb-0 d-flex flex-wrap">
471 {pv.person.display_name && (
472 <h5 className="mb-0">{pv.person.display_name}</h5>
474 <ul className="list-inline mb-2">
475 <li className="list-inline-item">
484 {isBanned(pv.person) && (
485 <li className="list-inline-item badge text-bg-danger">
486 {I18NextService.i18n.t("banned")}
489 {pv.person.deleted && (
490 <li className="list-inline-item badge text-bg-danger">
491 {I18NextService.i18n.t("deleted")}
494 {pv.person.admin && (
495 <li className="list-inline-item badge text-bg-light">
496 {I18NextService.i18n.t("admin")}
499 {pv.person.bot_account && (
500 <li className="list-inline-item badge text-bg-light">
501 {I18NextService.i18n.t("bot_account").toLowerCase()}
507 <div className="flex-grow-1 unselectable pointer mx-2"></div>
508 {!this.amCurrentUser && UserService.Instance.myUserInfo && (
511 className={`d-flex align-self-start btn btn-secondary me-2 ${
512 !pv.person.matrix_user_id && "invisible"
515 href={`https://matrix.to/#/${pv.person.matrix_user_id}`}
517 {I18NextService.i18n.t("send_secure_message")}
521 "d-flex align-self-start btn btn-secondary me-2"
523 to={`/create_private_message/${pv.person.id}`}
525 {I18NextService.i18n.t("send_message")}
530 "d-flex align-self-start btn btn-secondary me-2"
534 this.handleUnblockPerson
537 {I18NextService.i18n.t("unblock_user")}
542 "d-flex align-self-start btn btn-secondary me-2"
546 this.handleBlockPerson
549 {I18NextService.i18n.t("block_user")}
555 {canMod(pv.person.id, undefined, admins) &&
556 !isAdmin(pv.person.id, admins) &&
558 (!isBanned(pv.person) ? (
561 "d-flex align-self-start btn btn-secondary me-2"
563 onClick={linkEvent(this, this.handleModBanShow)}
564 aria-label={I18NextService.i18n.t("ban")}
566 {capitalizeFirstLetter(I18NextService.i18n.t("ban"))}
571 "d-flex align-self-start btn btn-secondary me-2"
573 onClick={linkEvent(this, this.handleModBanSubmit)}
574 aria-label={I18NextService.i18n.t("unban")}
576 {capitalizeFirstLetter(I18NextService.i18n.t("unban"))}
581 <div className="d-flex align-items-center mb-2">
584 dangerouslySetInnerHTML={mdToHtml(pv.person.bio)}
589 <ul className="list-inline mb-2">
590 <li className="list-inline-item badge text-bg-light">
591 {I18NextService.i18n.t("number_of_posts", {
592 count: Number(pv.counts.post_count),
593 formattedCount: numToSI(pv.counts.post_count),
596 <li className="list-inline-item badge text-bg-light">
597 {I18NextService.i18n.t("number_of_comments", {
598 count: Number(pv.counts.comment_count),
599 formattedCount: numToSI(pv.counts.comment_count),
604 <div className="text-muted">
605 {I18NextService.i18n.t("joined")}{" "}
607 published={pv.person.published}
612 <div className="d-flex align-items-center text-muted mb-2">
614 <span className="ms-2">
615 {I18NextService.i18n.t("cake_day_title")}{" "}
617 .utc(pv.person.published)
619 .format("MMM DD, YYYY")}
622 {!UserService.Instance.myUserInfo && (
623 <div className="alert alert-info" role="alert">
624 {I18NextService.i18n.t("profile_not_logged_in_alert")}
634 banDialog(pv: PersonView) {
635 const { showBanDialog } = this.state;
639 <form onSubmit={linkEvent(this, this.handleModBanSubmit)}>
640 <div className="mb-3 row col-12">
641 <label className="col-form-label" htmlFor="profile-ban-reason">
642 {I18NextService.i18n.t("reason")}
646 id="profile-ban-reason"
647 className="form-control me-2"
648 placeholder={I18NextService.i18n.t("reason")}
649 value={this.state.banReason}
650 onInput={linkEvent(this, this.handleModBanReasonChange)}
652 <label className="col-form-label" htmlFor={`mod-ban-expires`}>
653 {I18NextService.i18n.t("expires")}
657 id={`mod-ban-expires`}
658 className="form-control me-2"
659 placeholder={I18NextService.i18n.t("number_of_days")}
660 value={this.state.banExpireDays}
661 onInput={linkEvent(this, this.handleModBanExpireDaysChange)}
663 <div className="input-group mb-3">
664 <div className="form-check">
666 className="form-check-input"
667 id="mod-ban-remove-data"
669 checked={this.state.removeData}
670 onChange={linkEvent(this, this.handleModRemoveDataChange)}
673 className="form-check-label"
674 htmlFor="mod-ban-remove-data"
675 title={I18NextService.i18n.t("remove_content_more")}
677 {I18NextService.i18n.t("remove_content")}
682 {/* TODO hold off on expires until later */}
683 {/* <div class="mb-3 row"> */}
684 {/* <label class="col-form-label">Expires</label> */}
685 {/* <input type="date" class="form-control me-2" placeholder={I18NextService.i18n.t('expires')} value={this.state.banExpires} onInput={linkEvent(this, this.handleModBanExpiresChange)} /> */}
687 <div className="mb-3 row">
690 className="btn btn-secondary me-2"
691 aria-label={I18NextService.i18n.t("cancel")}
692 onClick={linkEvent(this, this.handleModBanSubmitCancel)}
694 {I18NextService.i18n.t("cancel")}
698 className="btn btn-secondary"
699 aria-label={I18NextService.i18n.t("ban")}
701 {I18NextService.i18n.t("ban")} {pv.person.name}
709 async updateUrl({ page, sort, view }: Partial<ProfileProps>) {
714 } = getProfileQueryParams();
716 const queryParams: QueryParams<ProfileProps> = {
717 page: (page ?? urlPage).toString(),
718 sort: sort ?? urlSort,
719 view: view ?? urlView,
722 const { username } = this.props.match.params;
724 this.props.history.push(`/u/${username}${getQueryString(queryParams)}`);
725 await this.fetchUserData();
728 handlePageChange(page: number) {
729 this.updateUrl({ page });
732 handleSortChange(sort: SortType) {
733 this.updateUrl({ sort, page: 1 });
736 handleViewChange(i: Profile, event: any) {
738 view: PersonDetailsView[event.target.value],
743 handleModBanShow(i: Profile) {
744 i.setState({ showBanDialog: true });
747 handleModBanReasonChange(i: Profile, event: any) {
748 i.setState({ banReason: event.target.value });
751 handleModBanExpireDaysChange(i: Profile, event: any) {
752 i.setState({ banExpireDays: event.target.value });
755 handleModRemoveDataChange(i: Profile, event: any) {
756 i.setState({ removeData: event.target.checked });
759 handleModBanSubmitCancel(i: Profile) {
760 i.setState({ showBanDialog: false });
763 async handleModBanSubmit(i: Profile, event: any) {
764 event.preventDefault();
765 const { removeData, banReason, banExpireDays } = i.state;
767 const personRes = i.state.personRes;
769 if (personRes.state == "success") {
770 const person = personRes.data.person_view.person;
771 const ban = !person.banned;
773 // If its an unban, restore all their data
775 i.setState({ removeData: false });
778 const res = await HttpService.client.banPerson({
779 person_id: person.id,
781 remove_data: removeData,
783 expires: futureDaysToUnixTime(banExpireDays),
784 auth: myAuthRequired(),
788 i.setState({ showBanDialog: false });
792 async toggleBlockPerson(recipientId: number, block: boolean) {
793 const res = await HttpService.client.blockPerson({
794 person_id: recipientId,
796 auth: myAuthRequired(),
798 if (res.state == "success") {
799 updatePersonBlock(res.data);
803 handleUnblockPerson(personId: number) {
804 this.toggleBlockPerson(personId, false);
807 handleBlockPerson(personId: number) {
808 this.toggleBlockPerson(personId, true);
811 async handleAddModToCommunity(form: AddModToCommunity) {
812 // TODO not sure what to do here
813 await HttpService.client.addModToCommunity(form);
816 async handlePurgePerson(form: PurgePerson) {
817 const purgePersonRes = await HttpService.client.purgePerson(form);
818 this.purgeItem(purgePersonRes);
821 async handlePurgeComment(form: PurgeComment) {
822 const purgeCommentRes = await HttpService.client.purgeComment(form);
823 this.purgeItem(purgeCommentRes);
826 async handlePurgePost(form: PurgePost) {
827 const purgeRes = await HttpService.client.purgePost(form);
828 this.purgeItem(purgeRes);
831 async handleBlockPersonAlt(form: BlockPerson) {
832 const blockPersonRes = await HttpService.client.blockPerson(form);
833 if (blockPersonRes.state === "success") {
834 updatePersonBlock(blockPersonRes.data);
838 async handleCreateComment(form: CreateComment) {
839 const createCommentRes = await HttpService.client.createComment(form);
840 this.createAndUpdateComments(createCommentRes);
842 return createCommentRes;
845 async handleEditComment(form: EditComment) {
846 const editCommentRes = await HttpService.client.editComment(form);
847 this.findAndUpdateComment(editCommentRes);
849 return editCommentRes;
852 async handleDeleteComment(form: DeleteComment) {
853 const deleteCommentRes = await HttpService.client.deleteComment(form);
854 this.findAndUpdateComment(deleteCommentRes);
857 async handleDeletePost(form: DeletePost) {
858 const deleteRes = await HttpService.client.deletePost(form);
859 this.findAndUpdatePost(deleteRes);
862 async handleRemovePost(form: RemovePost) {
863 const removeRes = await HttpService.client.removePost(form);
864 this.findAndUpdatePost(removeRes);
867 async handleRemoveComment(form: RemoveComment) {
868 const removeCommentRes = await HttpService.client.removeComment(form);
869 this.findAndUpdateComment(removeCommentRes);
872 async handleSaveComment(form: SaveComment) {
873 const saveCommentRes = await HttpService.client.saveComment(form);
874 this.findAndUpdateComment(saveCommentRes);
877 async handleSavePost(form: SavePost) {
878 const saveRes = await HttpService.client.savePost(form);
879 this.findAndUpdatePost(saveRes);
882 async handleFeaturePost(form: FeaturePost) {
883 const featureRes = await HttpService.client.featurePost(form);
884 this.findAndUpdatePost(featureRes);
887 async handleCommentVote(form: CreateCommentLike) {
888 const voteRes = await HttpService.client.likeComment(form);
889 this.findAndUpdateComment(voteRes);
892 async handlePostVote(form: CreatePostLike) {
893 const voteRes = await HttpService.client.likePost(form);
894 this.findAndUpdatePost(voteRes);
897 async handlePostEdit(form: EditPost) {
898 const res = await HttpService.client.editPost(form);
899 this.findAndUpdatePost(res);
902 async handleCommentReport(form: CreateCommentReport) {
903 const reportRes = await HttpService.client.createCommentReport(form);
904 if (reportRes.state === "success") {
905 toast(I18NextService.i18n.t("report_created"));
909 async handlePostReport(form: CreatePostReport) {
910 const reportRes = await HttpService.client.createPostReport(form);
911 if (reportRes.state === "success") {
912 toast(I18NextService.i18n.t("report_created"));
916 async handleLockPost(form: LockPost) {
917 const lockRes = await HttpService.client.lockPost(form);
918 this.findAndUpdatePost(lockRes);
921 async handleDistinguishComment(form: DistinguishComment) {
922 const distinguishRes = await HttpService.client.distinguishComment(form);
923 this.findAndUpdateComment(distinguishRes);
926 async handleAddAdmin(form: AddAdmin) {
927 const addAdminRes = await HttpService.client.addAdmin(form);
929 if (addAdminRes.state == "success") {
930 this.setState(s => ((s.siteRes.admins = addAdminRes.data.admins), s));
934 async handleTransferCommunity(form: TransferCommunity) {
935 await HttpService.client.transferCommunity(form);
936 toast(I18NextService.i18n.t("transfer_community"));
939 async handleCommentReplyRead(form: MarkCommentReplyAsRead) {
940 const readRes = await HttpService.client.markCommentReplyAsRead(form);
941 this.findAndUpdateCommentReply(readRes);
944 async handlePersonMentionRead(form: MarkPersonMentionAsRead) {
945 // TODO not sure what to do here. Maybe it is actually optional, because post doesn't need it.
946 await HttpService.client.markPersonMentionAsRead(form);
949 async handleBanFromCommunity(form: BanFromCommunity) {
950 const banRes = await HttpService.client.banFromCommunity(form);
951 this.updateBanFromCommunity(banRes);
954 async handleBanPerson(form: BanPerson) {
955 const banRes = await HttpService.client.banPerson(form);
956 this.updateBan(banRes);
959 updateBanFromCommunity(banRes: RequestState<BanFromCommunityResponse>) {
960 // Maybe not necessary
961 if (banRes.state === "success") {
963 if (s.personRes.state == "success") {
964 s.personRes.data.posts
965 .filter(c => c.creator.id === banRes.data.person_view.person.id)
967 c => (c.creator_banned_from_community = banRes.data.banned)
970 s.personRes.data.comments
971 .filter(c => c.creator.id === banRes.data.person_view.person.id)
973 c => (c.creator_banned_from_community = banRes.data.banned)
981 updateBan(banRes: RequestState<BanPersonResponse>) {
982 // Maybe not necessary
983 if (banRes.state == "success") {
985 if (s.personRes.state == "success") {
986 s.personRes.data.posts
987 .filter(c => c.creator.id == banRes.data.person_view.person.id)
988 .forEach(c => (c.creator.banned = banRes.data.banned));
989 s.personRes.data.comments
990 .filter(c => c.creator.id == banRes.data.person_view.person.id)
991 .forEach(c => (c.creator.banned = banRes.data.banned));
998 purgeItem(purgeRes: RequestState<PurgeItemResponse>) {
999 if (purgeRes.state == "success") {
1000 toast(I18NextService.i18n.t("purge_success"));
1001 this.context.router.history.push(`/`);
1005 findAndUpdateComment(res: RequestState<CommentResponse>) {
1006 this.setState(s => {
1007 if (s.personRes.state == "success" && res.state == "success") {
1008 s.personRes.data.comments = editComment(
1009 res.data.comment_view,
1010 s.personRes.data.comments
1012 s.finished.set(res.data.comment_view.comment.id, true);
1018 createAndUpdateComments(res: RequestState<CommentResponse>) {
1019 this.setState(s => {
1020 if (s.personRes.state == "success" && res.state == "success") {
1021 s.personRes.data.comments.unshift(res.data.comment_view);
1022 // Set finished for the parent
1024 getCommentParentId(res.data.comment_view.comment) ?? 0,
1032 findAndUpdateCommentReply(res: RequestState<CommentReplyResponse>) {
1033 this.setState(s => {
1034 if (s.personRes.state == "success" && res.state == "success") {
1035 s.personRes.data.comments = editWith(
1036 res.data.comment_reply_view,
1037 s.personRes.data.comments
1044 findAndUpdatePost(res: RequestState<PostResponse>) {
1045 this.setState(s => {
1046 if (s.personRes.state == "success" && res.state == "success") {
1047 s.personRes.data.posts = editPost(
1049 s.personRes.data.posts