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