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