]> Untitled Git - lemmy-ui.git/blob - src/shared/components/person/inbox.tsx
4dec69933de51bab2f4ac1cdaa4a951f2af978dc
[lemmy-ui.git] / src / shared / components / person / inbox.tsx
1 import { Component, linkEvent } from "inferno";
2 import {
3   AddAdmin,
4   AddModToCommunity,
5   BanFromCommunity,
6   BanFromCommunityResponse,
7   BanPerson,
8   BanPersonResponse,
9   BlockPerson,
10   CommentId,
11   CommentReplyResponse,
12   CommentReplyView,
13   CommentReportResponse,
14   CommentResponse,
15   CommentSortType,
16   CommentView,
17   CreateComment,
18   CreateCommentLike,
19   CreateCommentReport,
20   CreatePrivateMessage,
21   CreatePrivateMessageReport,
22   DeleteComment,
23   DeletePrivateMessage,
24   DistinguishComment,
25   EditComment,
26   EditPrivateMessage,
27   GetPersonMentionsResponse,
28   GetRepliesResponse,
29   GetSiteResponse,
30   MarkCommentReplyAsRead,
31   MarkPersonMentionAsRead,
32   MarkPrivateMessageAsRead,
33   PersonMentionResponse,
34   PersonMentionView,
35   PrivateMessageReportResponse,
36   PrivateMessageResponse,
37   PrivateMessageView,
38   PrivateMessagesResponse,
39   PurgeComment,
40   PurgeItemResponse,
41   PurgePerson,
42   PurgePost,
43   RemoveComment,
44   SaveComment,
45   TransferCommunity,
46 } from "lemmy-js-client";
47 import { i18n } from "../../i18next";
48 import { CommentViewType, InitialFetchRequest } from "../../interfaces";
49 import { UserService } from "../../services";
50 import { FirstLoadService } from "../../services/FirstLoadService";
51 import { HttpService, RequestState } from "../../services/HttpService";
52 import {
53   RouteDataResponse,
54   commentsToFlatNodes,
55   editCommentReply,
56   editMention,
57   editPrivateMessage,
58   editWith,
59   enableDownvotes,
60   fetchLimit,
61   getCommentParentId,
62   myAuth,
63   myAuthRequired,
64   relTags,
65   setIsoData,
66   toast,
67   updatePersonBlock,
68 } from "../../utils";
69 import { CommentNodes } from "../comment/comment-nodes";
70 import { CommentSortSelect } from "../common/comment-sort-select";
71 import { HtmlTags } from "../common/html-tags";
72 import { Icon, Spinner } from "../common/icon";
73 import { Paginator } from "../common/paginator";
74 import { PrivateMessage } from "../private_message/private-message";
75
76 enum UnreadOrAll {
77   Unread,
78   All,
79 }
80
81 enum MessageType {
82   All,
83   Replies,
84   Mentions,
85   Messages,
86 }
87
88 enum ReplyEnum {
89   Reply,
90   Mention,
91   Message,
92 }
93
94 type InboxData = RouteDataResponse<{
95   repliesRes: GetRepliesResponse;
96   mentionsRes: GetPersonMentionsResponse;
97   messagesRes: PrivateMessagesResponse;
98 }>;
99
100 type ReplyType = {
101   id: number;
102   type_: ReplyEnum;
103   view: CommentView | PrivateMessageView | PersonMentionView | CommentReplyView;
104   published: string;
105 };
106
107 interface InboxState {
108   unreadOrAll: UnreadOrAll;
109   messageType: MessageType;
110   repliesRes: RequestState<GetRepliesResponse>;
111   mentionsRes: RequestState<GetPersonMentionsResponse>;
112   messagesRes: RequestState<PrivateMessagesResponse>;
113   markAllAsReadRes: RequestState<GetRepliesResponse>;
114   sort: CommentSortType;
115   page: number;
116   siteRes: GetSiteResponse;
117   finished: Map<CommentId, boolean | undefined>;
118   isIsomorphic: boolean;
119 }
120
121 export class Inbox extends Component<any, InboxState> {
122   private isoData = setIsoData<InboxData>(this.context);
123   state: InboxState = {
124     unreadOrAll: UnreadOrAll.Unread,
125     messageType: MessageType.All,
126     sort: "New",
127     page: 1,
128     siteRes: this.isoData.site_res,
129     repliesRes: { state: "empty" },
130     mentionsRes: { state: "empty" },
131     messagesRes: { state: "empty" },
132     markAllAsReadRes: { state: "empty" },
133     finished: new Map(),
134     isIsomorphic: false,
135   };
136
137   constructor(props: any, context: any) {
138     super(props, context);
139
140     this.handleSortChange = this.handleSortChange.bind(this);
141     this.handlePageChange = this.handlePageChange.bind(this);
142
143     this.handleCreateComment = this.handleCreateComment.bind(this);
144     this.handleEditComment = this.handleEditComment.bind(this);
145     this.handleSaveComment = this.handleSaveComment.bind(this);
146     this.handleBlockPerson = this.handleBlockPerson.bind(this);
147     this.handleDeleteComment = this.handleDeleteComment.bind(this);
148     this.handleRemoveComment = this.handleRemoveComment.bind(this);
149     this.handleCommentVote = this.handleCommentVote.bind(this);
150     this.handleAddModToCommunity = this.handleAddModToCommunity.bind(this);
151     this.handleAddAdmin = this.handleAddAdmin.bind(this);
152     this.handlePurgePerson = this.handlePurgePerson.bind(this);
153     this.handlePurgeComment = this.handlePurgeComment.bind(this);
154     this.handleCommentReport = this.handleCommentReport.bind(this);
155     this.handleDistinguishComment = this.handleDistinguishComment.bind(this);
156     this.handleTransferCommunity = this.handleTransferCommunity.bind(this);
157     this.handleCommentReplyRead = this.handleCommentReplyRead.bind(this);
158     this.handlePersonMentionRead = this.handlePersonMentionRead.bind(this);
159     this.handleBanFromCommunity = this.handleBanFromCommunity.bind(this);
160     this.handleBanPerson = this.handleBanPerson.bind(this);
161
162     this.handleDeleteMessage = this.handleDeleteMessage.bind(this);
163     this.handleMarkMessageAsRead = this.handleMarkMessageAsRead.bind(this);
164     this.handleMessageReport = this.handleMessageReport.bind(this);
165     this.handleCreateMessage = this.handleCreateMessage.bind(this);
166     this.handleEditMessage = this.handleEditMessage.bind(this);
167
168     // Only fetch the data if coming from another route
169     if (FirstLoadService.isFirstLoad) {
170       const { mentionsRes, messagesRes, repliesRes } = this.isoData.routeData;
171
172       this.state = {
173         ...this.state,
174         repliesRes,
175         mentionsRes,
176         messagesRes,
177         isIsomorphic: true,
178       };
179     }
180   }
181
182   async componentDidMount() {
183     if (!this.state.isIsomorphic) {
184       await this.refetch();
185     }
186   }
187
188   get documentTitle(): string {
189     const mui = UserService.Instance.myUserInfo;
190     return mui
191       ? `@${mui.local_user_view.person.name} ${i18n.t("inbox")} - ${
192           this.state.siteRes.site_view.site.name
193         }`
194       : "";
195   }
196
197   get hasUnreads(): boolean {
198     if (this.state.unreadOrAll == UnreadOrAll.Unread) {
199       const { repliesRes, mentionsRes, messagesRes } = this.state;
200       const replyCount =
201         repliesRes.state == "success" ? repliesRes.data.replies.length : 0;
202       const mentionCount =
203         mentionsRes.state == "success" ? mentionsRes.data.mentions.length : 0;
204       const messageCount =
205         messagesRes.state == "success"
206           ? messagesRes.data.private_messages.length
207           : 0;
208
209       return replyCount + mentionCount + messageCount > 0;
210     } else {
211       return false;
212     }
213   }
214
215   render() {
216     const auth = myAuth();
217     const inboxRss = auth ? `/feeds/inbox/${auth}.xml` : undefined;
218     return (
219       <div className="container-lg">
220         <div className="row">
221           <div className="col-12">
222             <HtmlTags
223               title={this.documentTitle}
224               path={this.context.router.route.match.url}
225             />
226             <h5 className="mb-2">
227               {i18n.t("inbox")}
228               {inboxRss && (
229                 <small>
230                   <a href={inboxRss} title="RSS" rel={relTags}>
231                     <Icon icon="rss" classes="ms-2 text-muted small" />
232                   </a>
233                   <link
234                     rel="alternate"
235                     type="application/atom+xml"
236                     href={inboxRss}
237                   />
238                 </small>
239               )}
240             </h5>
241             {this.hasUnreads && (
242               <button
243                 className="btn btn-secondary mb-2"
244                 onClick={linkEvent(this, this.handleMarkAllAsRead)}
245               >
246                 {this.state.markAllAsReadRes.state == "loading" ? (
247                   <Spinner />
248                 ) : (
249                   i18n.t("mark_all_as_read")
250                 )}
251               </button>
252             )}
253             {this.selects()}
254             {this.section}
255             <Paginator
256               page={this.state.page}
257               onChange={this.handlePageChange}
258             />
259           </div>
260         </div>
261       </div>
262     );
263   }
264
265   get section() {
266     switch (this.state.messageType) {
267       case MessageType.All: {
268         return this.all();
269       }
270       case MessageType.Replies: {
271         return this.replies();
272       }
273       case MessageType.Mentions: {
274         return this.mentions();
275       }
276       case MessageType.Messages: {
277         return this.messages();
278       }
279       default: {
280         return null;
281       }
282     }
283   }
284
285   unreadOrAllRadios() {
286     return (
287       <div className="btn-group btn-group-toggle flex-wrap mb-2">
288         <label
289           className={`btn btn-outline-secondary pointer
290             ${this.state.unreadOrAll == UnreadOrAll.Unread && "active"}
291           `}
292         >
293           <input
294             type="radio"
295             className="btn-check"
296             value={UnreadOrAll.Unread}
297             checked={this.state.unreadOrAll == UnreadOrAll.Unread}
298             onChange={linkEvent(this, this.handleUnreadOrAllChange)}
299           />
300           {i18n.t("unread")}
301         </label>
302         <label
303           className={`btn btn-outline-secondary pointer
304             ${this.state.unreadOrAll == UnreadOrAll.All && "active"}
305           `}
306         >
307           <input
308             type="radio"
309             className="btn-check"
310             value={UnreadOrAll.All}
311             checked={this.state.unreadOrAll == UnreadOrAll.All}
312             onChange={linkEvent(this, this.handleUnreadOrAllChange)}
313           />
314           {i18n.t("all")}
315         </label>
316       </div>
317     );
318   }
319
320   messageTypeRadios() {
321     return (
322       <div className="btn-group btn-group-toggle flex-wrap mb-2">
323         <label
324           className={`btn btn-outline-secondary pointer
325             ${this.state.messageType == MessageType.All && "active"}
326           `}
327         >
328           <input
329             type="radio"
330             className="btn-check"
331             value={MessageType.All}
332             checked={this.state.messageType == MessageType.All}
333             onChange={linkEvent(this, this.handleMessageTypeChange)}
334           />
335           {i18n.t("all")}
336         </label>
337         <label
338           className={`btn btn-outline-secondary pointer
339             ${this.state.messageType == MessageType.Replies && "active"}
340           `}
341         >
342           <input
343             type="radio"
344             className="btn-check"
345             value={MessageType.Replies}
346             checked={this.state.messageType == MessageType.Replies}
347             onChange={linkEvent(this, this.handleMessageTypeChange)}
348           />
349           {i18n.t("replies")}
350         </label>
351         <label
352           className={`btn btn-outline-secondary pointer
353             ${this.state.messageType == MessageType.Mentions && "active"}
354           `}
355         >
356           <input
357             type="radio"
358             className="btn-check"
359             value={MessageType.Mentions}
360             checked={this.state.messageType == MessageType.Mentions}
361             onChange={linkEvent(this, this.handleMessageTypeChange)}
362           />
363           {i18n.t("mentions")}
364         </label>
365         <label
366           className={`btn btn-outline-secondary pointer
367             ${this.state.messageType == MessageType.Messages && "active"}
368           `}
369         >
370           <input
371             type="radio"
372             className="btn-check"
373             value={MessageType.Messages}
374             checked={this.state.messageType == MessageType.Messages}
375             onChange={linkEvent(this, this.handleMessageTypeChange)}
376           />
377           {i18n.t("messages")}
378         </label>
379       </div>
380     );
381   }
382
383   selects() {
384     return (
385       <div className="mb-2">
386         <span className="me-3">{this.unreadOrAllRadios()}</span>
387         <span className="me-3">{this.messageTypeRadios()}</span>
388         <CommentSortSelect
389           sort={this.state.sort}
390           onChange={this.handleSortChange}
391         />
392       </div>
393     );
394   }
395
396   replyToReplyType(r: CommentReplyView): ReplyType {
397     return {
398       id: r.comment_reply.id,
399       type_: ReplyEnum.Reply,
400       view: r,
401       published: r.comment.published,
402     };
403   }
404
405   mentionToReplyType(r: PersonMentionView): ReplyType {
406     return {
407       id: r.person_mention.id,
408       type_: ReplyEnum.Mention,
409       view: r,
410       published: r.comment.published,
411     };
412   }
413
414   messageToReplyType(r: PrivateMessageView): ReplyType {
415     return {
416       id: r.private_message.id,
417       type_: ReplyEnum.Message,
418       view: r,
419       published: r.private_message.published,
420     };
421   }
422
423   buildCombined(): ReplyType[] {
424     const replies: ReplyType[] =
425       this.state.repliesRes.state == "success"
426         ? this.state.repliesRes.data.replies.map(this.replyToReplyType)
427         : [];
428     const mentions: ReplyType[] =
429       this.state.mentionsRes.state == "success"
430         ? this.state.mentionsRes.data.mentions.map(this.mentionToReplyType)
431         : [];
432     const messages: ReplyType[] =
433       this.state.messagesRes.state == "success"
434         ? this.state.messagesRes.data.private_messages.map(
435             this.messageToReplyType
436           )
437         : [];
438
439     return [...replies, ...mentions, ...messages].sort((a, b) =>
440       b.published.localeCompare(a.published)
441     );
442   }
443
444   renderReplyType(i: ReplyType) {
445     switch (i.type_) {
446       case ReplyEnum.Reply:
447         return (
448           <CommentNodes
449             key={i.id}
450             nodes={[
451               { comment_view: i.view as CommentView, children: [], depth: 0 },
452             ]}
453             viewType={CommentViewType.Flat}
454             finished={this.state.finished}
455             noIndent
456             markable
457             showCommunity
458             showContext
459             enableDownvotes={enableDownvotes(this.state.siteRes)}
460             allLanguages={this.state.siteRes.all_languages}
461             siteLanguages={this.state.siteRes.discussion_languages}
462             onSaveComment={this.handleSaveComment}
463             onBlockPerson={this.handleBlockPerson}
464             onDeleteComment={this.handleDeleteComment}
465             onRemoveComment={this.handleRemoveComment}
466             onCommentVote={this.handleCommentVote}
467             onCommentReport={this.handleCommentReport}
468             onDistinguishComment={this.handleDistinguishComment}
469             onAddModToCommunity={this.handleAddModToCommunity}
470             onAddAdmin={this.handleAddAdmin}
471             onTransferCommunity={this.handleTransferCommunity}
472             onPurgeComment={this.handlePurgeComment}
473             onPurgePerson={this.handlePurgePerson}
474             onCommentReplyRead={this.handleCommentReplyRead}
475             onPersonMentionRead={this.handlePersonMentionRead}
476             onBanPersonFromCommunity={this.handleBanFromCommunity}
477             onBanPerson={this.handleBanPerson}
478             onCreateComment={this.handleCreateComment}
479             onEditComment={this.handleEditComment}
480           />
481         );
482       case ReplyEnum.Mention:
483         return (
484           <CommentNodes
485             key={i.id}
486             nodes={[
487               {
488                 comment_view: i.view as PersonMentionView,
489                 children: [],
490                 depth: 0,
491               },
492             ]}
493             finished={this.state.finished}
494             viewType={CommentViewType.Flat}
495             noIndent
496             markable
497             showCommunity
498             showContext
499             enableDownvotes={enableDownvotes(this.state.siteRes)}
500             allLanguages={this.state.siteRes.all_languages}
501             siteLanguages={this.state.siteRes.discussion_languages}
502             onSaveComment={this.handleSaveComment}
503             onBlockPerson={this.handleBlockPerson}
504             onDeleteComment={this.handleDeleteComment}
505             onRemoveComment={this.handleRemoveComment}
506             onCommentVote={this.handleCommentVote}
507             onCommentReport={this.handleCommentReport}
508             onDistinguishComment={this.handleDistinguishComment}
509             onAddModToCommunity={this.handleAddModToCommunity}
510             onAddAdmin={this.handleAddAdmin}
511             onTransferCommunity={this.handleTransferCommunity}
512             onPurgeComment={this.handlePurgeComment}
513             onPurgePerson={this.handlePurgePerson}
514             onCommentReplyRead={this.handleCommentReplyRead}
515             onPersonMentionRead={this.handlePersonMentionRead}
516             onBanPersonFromCommunity={this.handleBanFromCommunity}
517             onBanPerson={this.handleBanPerson}
518             onCreateComment={this.handleCreateComment}
519             onEditComment={this.handleEditComment}
520           />
521         );
522       case ReplyEnum.Message:
523         return (
524           <PrivateMessage
525             key={i.id}
526             private_message_view={i.view as PrivateMessageView}
527             onDelete={this.handleDeleteMessage}
528             onMarkRead={this.handleMarkMessageAsRead}
529             onReport={this.handleMessageReport}
530             onCreate={this.handleCreateMessage}
531             onEdit={this.handleEditMessage}
532           />
533         );
534       default:
535         return <div />;
536     }
537   }
538
539   all() {
540     if (
541       this.state.repliesRes.state == "loading" ||
542       this.state.mentionsRes.state == "loading" ||
543       this.state.messagesRes.state == "loading"
544     ) {
545       return (
546         <h5>
547           <Spinner large />
548         </h5>
549       );
550     } else {
551       return (
552         <div>{this.buildCombined().map(r => this.renderReplyType(r))}</div>
553       );
554     }
555   }
556
557   replies() {
558     switch (this.state.repliesRes.state) {
559       case "loading":
560         return (
561           <h5>
562             <Spinner large />
563           </h5>
564         );
565       case "success": {
566         const replies = this.state.repliesRes.data.replies;
567         return (
568           <div>
569             <CommentNodes
570               nodes={commentsToFlatNodes(replies)}
571               viewType={CommentViewType.Flat}
572               finished={this.state.finished}
573               noIndent
574               markable
575               showCommunity
576               showContext
577               enableDownvotes={enableDownvotes(this.state.siteRes)}
578               allLanguages={this.state.siteRes.all_languages}
579               siteLanguages={this.state.siteRes.discussion_languages}
580               onSaveComment={this.handleSaveComment}
581               onBlockPerson={this.handleBlockPerson}
582               onDeleteComment={this.handleDeleteComment}
583               onRemoveComment={this.handleRemoveComment}
584               onCommentVote={this.handleCommentVote}
585               onCommentReport={this.handleCommentReport}
586               onDistinguishComment={this.handleDistinguishComment}
587               onAddModToCommunity={this.handleAddModToCommunity}
588               onAddAdmin={this.handleAddAdmin}
589               onTransferCommunity={this.handleTransferCommunity}
590               onPurgeComment={this.handlePurgeComment}
591               onPurgePerson={this.handlePurgePerson}
592               onCommentReplyRead={this.handleCommentReplyRead}
593               onPersonMentionRead={this.handlePersonMentionRead}
594               onBanPersonFromCommunity={this.handleBanFromCommunity}
595               onBanPerson={this.handleBanPerson}
596               onCreateComment={this.handleCreateComment}
597               onEditComment={this.handleEditComment}
598             />
599           </div>
600         );
601       }
602     }
603   }
604
605   mentions() {
606     switch (this.state.mentionsRes.state) {
607       case "loading":
608         return (
609           <h5>
610             <Spinner large />
611           </h5>
612         );
613       case "success": {
614         const mentions = this.state.mentionsRes.data.mentions;
615         return (
616           <div>
617             {mentions.map(umv => (
618               <CommentNodes
619                 key={umv.person_mention.id}
620                 nodes={[{ comment_view: umv, children: [], depth: 0 }]}
621                 viewType={CommentViewType.Flat}
622                 finished={this.state.finished}
623                 noIndent
624                 markable
625                 showCommunity
626                 showContext
627                 enableDownvotes={enableDownvotes(this.state.siteRes)}
628                 allLanguages={this.state.siteRes.all_languages}
629                 siteLanguages={this.state.siteRes.discussion_languages}
630                 onSaveComment={this.handleSaveComment}
631                 onBlockPerson={this.handleBlockPerson}
632                 onDeleteComment={this.handleDeleteComment}
633                 onRemoveComment={this.handleRemoveComment}
634                 onCommentVote={this.handleCommentVote}
635                 onCommentReport={this.handleCommentReport}
636                 onDistinguishComment={this.handleDistinguishComment}
637                 onAddModToCommunity={this.handleAddModToCommunity}
638                 onAddAdmin={this.handleAddAdmin}
639                 onTransferCommunity={this.handleTransferCommunity}
640                 onPurgeComment={this.handlePurgeComment}
641                 onPurgePerson={this.handlePurgePerson}
642                 onCommentReplyRead={this.handleCommentReplyRead}
643                 onPersonMentionRead={this.handlePersonMentionRead}
644                 onBanPersonFromCommunity={this.handleBanFromCommunity}
645                 onBanPerson={this.handleBanPerson}
646                 onCreateComment={this.handleCreateComment}
647                 onEditComment={this.handleEditComment}
648               />
649             ))}
650           </div>
651         );
652       }
653     }
654   }
655
656   messages() {
657     switch (this.state.messagesRes.state) {
658       case "loading":
659         return (
660           <h5>
661             <Spinner large />
662           </h5>
663         );
664       case "success": {
665         const messages = this.state.messagesRes.data.private_messages;
666         return (
667           <div>
668             {messages.map(pmv => (
669               <PrivateMessage
670                 key={pmv.private_message.id}
671                 private_message_view={pmv}
672                 onDelete={this.handleDeleteMessage}
673                 onMarkRead={this.handleMarkMessageAsRead}
674                 onReport={this.handleMessageReport}
675                 onCreate={this.handleCreateMessage}
676                 onEdit={this.handleEditMessage}
677               />
678             ))}
679           </div>
680         );
681       }
682     }
683   }
684
685   async handlePageChange(page: number) {
686     this.setState({ page });
687     await this.refetch();
688   }
689
690   async handleUnreadOrAllChange(i: Inbox, event: any) {
691     i.setState({ unreadOrAll: Number(event.target.value), page: 1 });
692     await i.refetch();
693   }
694
695   async handleMessageTypeChange(i: Inbox, event: any) {
696     i.setState({ messageType: Number(event.target.value), page: 1 });
697     await i.refetch();
698   }
699
700   static async fetchInitialData({
701     client,
702     auth,
703   }: InitialFetchRequest): Promise<InboxData> {
704     const sort: CommentSortType = "New";
705
706     return {
707       mentionsRes: auth
708         ? await client.getPersonMentions({
709             sort,
710             unread_only: true,
711             page: 1,
712             limit: fetchLimit,
713             auth,
714           })
715         : { state: "empty" },
716       messagesRes: auth
717         ? await client.getPrivateMessages({
718             unread_only: true,
719             page: 1,
720             limit: fetchLimit,
721             auth,
722           })
723         : { state: "empty" },
724       repliesRes: auth
725         ? await client.getReplies({
726             sort,
727             unread_only: true,
728             page: 1,
729             limit: fetchLimit,
730             auth,
731           })
732         : { state: "empty" },
733     };
734   }
735
736   async refetch() {
737     const sort = this.state.sort;
738     const unread_only = this.state.unreadOrAll == UnreadOrAll.Unread;
739     const page = this.state.page;
740     const limit = fetchLimit;
741     const auth = myAuthRequired();
742
743     this.setState({ repliesRes: { state: "loading" } });
744     this.setState({
745       repliesRes: await HttpService.client.getReplies({
746         sort,
747         unread_only,
748         page,
749         limit,
750         auth,
751       }),
752     });
753
754     this.setState({ mentionsRes: { state: "loading" } });
755     this.setState({
756       mentionsRes: await HttpService.client.getPersonMentions({
757         sort,
758         unread_only,
759         page,
760         limit,
761         auth,
762       }),
763     });
764
765     this.setState({ messagesRes: { state: "loading" } });
766     this.setState({
767       messagesRes: await HttpService.client.getPrivateMessages({
768         unread_only,
769         page,
770         limit,
771         auth,
772       }),
773     });
774   }
775
776   async handleSortChange(val: CommentSortType) {
777     this.setState({ sort: val, page: 1 });
778     await this.refetch();
779   }
780
781   async handleMarkAllAsRead(i: Inbox) {
782     i.setState({ markAllAsReadRes: { state: "loading" } });
783
784     i.setState({
785       markAllAsReadRes: await HttpService.client.markAllAsRead({
786         auth: myAuthRequired(),
787       }),
788     });
789
790     if (i.state.markAllAsReadRes.state == "success") {
791       i.setState({
792         repliesRes: { state: "empty" },
793         mentionsRes: { state: "empty" },
794         messagesRes: { state: "empty" },
795       });
796     }
797   }
798
799   async handleAddModToCommunity(form: AddModToCommunity) {
800     // TODO not sure what to do here
801     HttpService.client.addModToCommunity(form);
802   }
803
804   async handlePurgePerson(form: PurgePerson) {
805     const purgePersonRes = await HttpService.client.purgePerson(form);
806     this.purgeItem(purgePersonRes);
807   }
808
809   async handlePurgeComment(form: PurgeComment) {
810     const purgeCommentRes = await HttpService.client.purgeComment(form);
811     this.purgeItem(purgeCommentRes);
812   }
813
814   async handlePurgePost(form: PurgePost) {
815     const purgeRes = await HttpService.client.purgePost(form);
816     this.purgeItem(purgeRes);
817   }
818
819   async handleBlockPerson(form: BlockPerson) {
820     const blockPersonRes = await HttpService.client.blockPerson(form);
821     if (blockPersonRes.state == "success") {
822       updatePersonBlock(blockPersonRes.data);
823     }
824   }
825
826   async handleCreateComment(form: CreateComment) {
827     const res = await HttpService.client.createComment(form);
828
829     if (res.state === "success") {
830       toast(i18n.t("reply_sent"));
831       this.findAndUpdateComment(res);
832     }
833
834     return res;
835   }
836
837   async handleEditComment(form: EditComment) {
838     const res = await HttpService.client.editComment(form);
839
840     if (res.state === "success") {
841       toast(i18n.t("edit"));
842       this.findAndUpdateComment(res);
843     } else if (res.state === "failed") {
844       toast(res.msg, "danger");
845     }
846
847     return res;
848   }
849
850   async handleDeleteComment(form: DeleteComment) {
851     const res = await HttpService.client.deleteComment(form);
852     if (res.state == "success") {
853       toast(i18n.t("deleted"));
854       this.findAndUpdateComment(res);
855     }
856   }
857
858   async handleRemoveComment(form: RemoveComment) {
859     const res = await HttpService.client.removeComment(form);
860     if (res.state == "success") {
861       toast(i18n.t("remove_comment"));
862       this.findAndUpdateComment(res);
863     }
864   }
865
866   async handleSaveComment(form: SaveComment) {
867     const res = await HttpService.client.saveComment(form);
868     this.findAndUpdateComment(res);
869   }
870
871   async handleCommentVote(form: CreateCommentLike) {
872     const res = await HttpService.client.likeComment(form);
873     this.findAndUpdateComment(res);
874   }
875
876   async handleCommentReport(form: CreateCommentReport) {
877     const reportRes = await HttpService.client.createCommentReport(form);
878     this.reportToast(reportRes);
879   }
880
881   async handleDistinguishComment(form: DistinguishComment) {
882     const res = await HttpService.client.distinguishComment(form);
883     this.findAndUpdateComment(res);
884   }
885
886   async handleAddAdmin(form: AddAdmin) {
887     const addAdminRes = await HttpService.client.addAdmin(form);
888
889     if (addAdminRes.state === "success") {
890       this.setState(s => ((s.siteRes.admins = addAdminRes.data.admins), s));
891     }
892   }
893
894   async handleTransferCommunity(form: TransferCommunity) {
895     await HttpService.client.transferCommunity(form);
896     toast(i18n.t("transfer_community"));
897   }
898
899   async handleCommentReplyRead(form: MarkCommentReplyAsRead) {
900     const res = await HttpService.client.markCommentReplyAsRead(form);
901     this.findAndUpdateCommentReply(res);
902   }
903
904   async handlePersonMentionRead(form: MarkPersonMentionAsRead) {
905     const res = await HttpService.client.markPersonMentionAsRead(form);
906     this.findAndUpdateMention(res);
907   }
908
909   async handleBanFromCommunity(form: BanFromCommunity) {
910     const banRes = await HttpService.client.banFromCommunity(form);
911     this.updateBanFromCommunity(banRes);
912   }
913
914   async handleBanPerson(form: BanPerson) {
915     const banRes = await HttpService.client.banPerson(form);
916     this.updateBan(banRes);
917   }
918
919   async handleDeleteMessage(form: DeletePrivateMessage) {
920     const res = await HttpService.client.deletePrivateMessage(form);
921     this.findAndUpdateMessage(res);
922   }
923
924   async handleEditMessage(form: EditPrivateMessage) {
925     const res = await HttpService.client.editPrivateMessage(form);
926     this.findAndUpdateMessage(res);
927   }
928
929   async handleMarkMessageAsRead(form: MarkPrivateMessageAsRead) {
930     const res = await HttpService.client.markPrivateMessageAsRead(form);
931     this.findAndUpdateMessage(res);
932   }
933
934   async handleMessageReport(form: CreatePrivateMessageReport) {
935     const res = await HttpService.client.createPrivateMessageReport(form);
936     this.reportToast(res);
937   }
938
939   async handleCreateMessage(form: CreatePrivateMessage) {
940     const res = await HttpService.client.createPrivateMessage(form);
941     this.setState(s => {
942       if (s.messagesRes.state == "success" && res.state == "success") {
943         s.messagesRes.data.private_messages.unshift(
944           res.data.private_message_view
945         );
946       }
947
948       return s;
949     });
950   }
951
952   findAndUpdateMessage(res: RequestState<PrivateMessageResponse>) {
953     this.setState(s => {
954       if (s.messagesRes.state === "success" && res.state === "success") {
955         s.messagesRes.data.private_messages = editPrivateMessage(
956           res.data.private_message_view,
957           s.messagesRes.data.private_messages
958         );
959       }
960       return s;
961     });
962   }
963
964   updateBanFromCommunity(banRes: RequestState<BanFromCommunityResponse>) {
965     // Maybe not necessary
966     if (banRes.state == "success") {
967       this.setState(s => {
968         if (s.repliesRes.state == "success") {
969           s.repliesRes.data.replies
970             .filter(c => c.creator.id == banRes.data.person_view.person.id)
971             .forEach(
972               c => (c.creator_banned_from_community = banRes.data.banned)
973             );
974         }
975         if (s.mentionsRes.state == "success") {
976           s.mentionsRes.data.mentions
977             .filter(c => c.creator.id == banRes.data.person_view.person.id)
978             .forEach(
979               c => (c.creator_banned_from_community = banRes.data.banned)
980             );
981         }
982         return s;
983       });
984     }
985   }
986
987   updateBan(banRes: RequestState<BanPersonResponse>) {
988     // Maybe not necessary
989     if (banRes.state == "success") {
990       this.setState(s => {
991         if (s.repliesRes.state == "success") {
992           s.repliesRes.data.replies
993             .filter(c => c.creator.id == banRes.data.person_view.person.id)
994             .forEach(c => (c.creator.banned = banRes.data.banned));
995         }
996         if (s.mentionsRes.state == "success") {
997           s.mentionsRes.data.mentions
998             .filter(c => c.creator.id == banRes.data.person_view.person.id)
999             .forEach(c => (c.creator.banned = banRes.data.banned));
1000         }
1001         return s;
1002       });
1003     }
1004   }
1005
1006   purgeItem(purgeRes: RequestState<PurgeItemResponse>) {
1007     if (purgeRes.state == "success") {
1008       toast(i18n.t("purge_success"));
1009       this.context.router.history.push(`/`);
1010     }
1011   }
1012
1013   reportToast(
1014     res: RequestState<PrivateMessageReportResponse | CommentReportResponse>
1015   ) {
1016     if (res.state == "success") {
1017       toast(i18n.t("report_created"));
1018     }
1019   }
1020
1021   // A weird case, since you have only replies and mentions, not comment responses
1022   findAndUpdateComment(res: RequestState<CommentResponse>) {
1023     if (res.state == "success") {
1024       this.setState(s => {
1025         if (s.repliesRes.state == "success") {
1026           s.repliesRes.data.replies = editWith(
1027             res.data.comment_view,
1028             s.repliesRes.data.replies
1029           );
1030         }
1031         if (s.mentionsRes.state == "success") {
1032           s.mentionsRes.data.mentions = editWith(
1033             res.data.comment_view,
1034             s.mentionsRes.data.mentions
1035           );
1036         }
1037         // Set finished for the parent
1038         s.finished.set(
1039           getCommentParentId(res.data.comment_view.comment) ?? 0,
1040           true
1041         );
1042         return s;
1043       });
1044     }
1045   }
1046
1047   findAndUpdateCommentReply(res: RequestState<CommentReplyResponse>) {
1048     this.setState(s => {
1049       if (s.repliesRes.state == "success" && res.state == "success") {
1050         s.repliesRes.data.replies = editCommentReply(
1051           res.data.comment_reply_view,
1052           s.repliesRes.data.replies
1053         );
1054       }
1055       return s;
1056     });
1057   }
1058
1059   findAndUpdateMention(res: RequestState<PersonMentionResponse>) {
1060     this.setState(s => {
1061       if (s.mentionsRes.state == "success" && res.state == "success") {
1062         s.mentionsRes.data.mentions = editMention(
1063           res.data.person_mention_view,
1064           s.mentionsRes.data.mentions
1065         );
1066       }
1067       return s;
1068     });
1069   }
1070 }