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