]> Untitled Git - lemmy-ui.git/blob - src/shared/components/person/inbox.tsx
28bb07166752a6dd30f50e758a0fe9c8165d09a8
[lemmy-ui.git] / src / shared / components / person / inbox.tsx
1 import {
2   commentsToFlatNodes,
3   editCommentReply,
4   editMention,
5   editPrivateMessage,
6   editWith,
7   enableDownvotes,
8   getCommentParentId,
9   myAuth,
10   myAuthRequired,
11   setIsoData,
12   updatePersonBlock,
13 } from "@utils/app";
14 import { capitalizeFirstLetter } from "@utils/helpers";
15 import { RouteDataResponse } from "@utils/types";
16 import { Component, linkEvent } from "inferno";
17 import {
18   AddAdmin,
19   AddModToCommunity,
20   BanFromCommunity,
21   BanFromCommunityResponse,
22   BanPerson,
23   BanPersonResponse,
24   BlockPerson,
25   CommentId,
26   CommentReplyResponse,
27   CommentReplyView,
28   CommentReportResponse,
29   CommentResponse,
30   CommentSortType,
31   CommentView,
32   CreateComment,
33   CreateCommentLike,
34   CreateCommentReport,
35   CreatePrivateMessage,
36   CreatePrivateMessageReport,
37   DeleteComment,
38   DeletePrivateMessage,
39   DistinguishComment,
40   EditComment,
41   EditPrivateMessage,
42   GetPersonMentionsResponse,
43   GetRepliesResponse,
44   GetSiteResponse,
45   MarkCommentReplyAsRead,
46   MarkPersonMentionAsRead,
47   MarkPrivateMessageAsRead,
48   PersonMentionResponse,
49   PersonMentionView,
50   PrivateMessageReportResponse,
51   PrivateMessageResponse,
52   PrivateMessageView,
53   PrivateMessagesResponse,
54   PurgeComment,
55   PurgeItemResponse,
56   PurgePerson,
57   PurgePost,
58   RemoveComment,
59   SaveComment,
60   TransferCommunity,
61 } from "lemmy-js-client";
62 import { fetchLimit, relTags } from "../../config";
63 import { CommentViewType, InitialFetchRequest } from "../../interfaces";
64 import { FirstLoadService, I18NextService, UserService } from "../../services";
65 import { HttpService, RequestState } from "../../services/HttpService";
66 import { toast } from "../../toast";
67 import { CommentNodes } from "../comment/comment-nodes";
68 import { CommentSortSelect } from "../common/comment-sort-select";
69 import { HtmlTags } from "../common/html-tags";
70 import { Icon, Spinner } from "../common/icon";
71 import { Paginator } from "../common/paginator";
72 import { PrivateMessage } from "../private_message/private-message";
73
74 enum UnreadOrAll {
75   Unread,
76   All,
77 }
78
79 enum MessageType {
80   All,
81   Replies,
82   Mentions,
83   Messages,
84 }
85
86 enum ReplyEnum {
87   Reply,
88   Mention,
89   Message,
90 }
91
92 type InboxData = RouteDataResponse<{
93   repliesRes: GetRepliesResponse;
94   mentionsRes: GetPersonMentionsResponse;
95   messagesRes: PrivateMessagesResponse;
96 }>;
97
98 type ReplyType = {
99   id: number;
100   type_: ReplyEnum;
101   view: CommentView | PrivateMessageView | PersonMentionView | CommentReplyView;
102   published: string;
103 };
104
105 interface InboxState {
106   unreadOrAll: UnreadOrAll;
107   messageType: MessageType;
108   repliesRes: RequestState<GetRepliesResponse>;
109   mentionsRes: RequestState<GetPersonMentionsResponse>;
110   messagesRes: RequestState<PrivateMessagesResponse>;
111   markAllAsReadRes: RequestState<GetRepliesResponse>;
112   sort: CommentSortType;
113   page: number;
114   siteRes: GetSiteResponse;
115   finished: Map<CommentId, boolean | undefined>;
116   isIsomorphic: boolean;
117 }
118
119 export class Inbox extends Component<any, InboxState> {
120   private isoData = setIsoData<InboxData>(this.context);
121   state: InboxState = {
122     unreadOrAll: UnreadOrAll.Unread,
123     messageType: MessageType.All,
124     sort: "New",
125     page: 1,
126     siteRes: this.isoData.site_res,
127     repliesRes: { state: "empty" },
128     mentionsRes: { state: "empty" },
129     messagesRes: { state: "empty" },
130     markAllAsReadRes: { state: "empty" },
131     finished: new Map(),
132     isIsomorphic: false,
133   };
134
135   constructor(props: any, context: any) {
136     super(props, context);
137
138     this.handleSortChange = this.handleSortChange.bind(this);
139     this.handlePageChange = this.handlePageChange.bind(this);
140
141     this.handleCreateComment = this.handleCreateComment.bind(this);
142     this.handleEditComment = this.handleEditComment.bind(this);
143     this.handleSaveComment = this.handleSaveComment.bind(this);
144     this.handleBlockPerson = this.handleBlockPerson.bind(this);
145     this.handleDeleteComment = this.handleDeleteComment.bind(this);
146     this.handleRemoveComment = this.handleRemoveComment.bind(this);
147     this.handleCommentVote = this.handleCommentVote.bind(this);
148     this.handleAddModToCommunity = this.handleAddModToCommunity.bind(this);
149     this.handleAddAdmin = this.handleAddAdmin.bind(this);
150     this.handlePurgePerson = this.handlePurgePerson.bind(this);
151     this.handlePurgeComment = this.handlePurgeComment.bind(this);
152     this.handleCommentReport = this.handleCommentReport.bind(this);
153     this.handleDistinguishComment = this.handleDistinguishComment.bind(this);
154     this.handleTransferCommunity = this.handleTransferCommunity.bind(this);
155     this.handleCommentReplyRead = this.handleCommentReplyRead.bind(this);
156     this.handlePersonMentionRead = this.handlePersonMentionRead.bind(this);
157     this.handleBanFromCommunity = this.handleBanFromCommunity.bind(this);
158     this.handleBanPerson = this.handleBanPerson.bind(this);
159
160     this.handleDeleteMessage = this.handleDeleteMessage.bind(this);
161     this.handleMarkMessageAsRead = this.handleMarkMessageAsRead.bind(this);
162     this.handleMessageReport = this.handleMessageReport.bind(this);
163     this.handleCreateMessage = this.handleCreateMessage.bind(this);
164     this.handleEditMessage = this.handleEditMessage.bind(this);
165
166     // Only fetch the data if coming from another route
167     if (FirstLoadService.isFirstLoad) {
168       const { mentionsRes, messagesRes, repliesRes } = this.isoData.routeData;
169
170       this.state = {
171         ...this.state,
172         repliesRes,
173         mentionsRes,
174         messagesRes,
175         isIsomorphic: true,
176       };
177     }
178   }
179
180   async componentDidMount() {
181     if (!this.state.isIsomorphic) {
182       await this.refetch();
183     }
184   }
185
186   get documentTitle(): string {
187     const mui = UserService.Instance.myUserInfo;
188     return mui
189       ? `@${mui.local_user_view.person.name} ${I18NextService.i18n.t(
190           "inbox"
191         )} - ${this.state.siteRes.site_view.site.name}`
192       : "";
193   }
194
195   get hasUnreads(): boolean {
196     if (this.state.unreadOrAll == UnreadOrAll.Unread) {
197       const { repliesRes, mentionsRes, messagesRes } = this.state;
198       const replyCount =
199         repliesRes.state == "success" ? repliesRes.data.replies.length : 0;
200       const mentionCount =
201         mentionsRes.state == "success" ? mentionsRes.data.mentions.length : 0;
202       const messageCount =
203         messagesRes.state == "success"
204           ? messagesRes.data.private_messages.length
205           : 0;
206
207       return replyCount + mentionCount + messageCount > 0;
208     } else {
209       return false;
210     }
211   }
212
213   render() {
214     const auth = myAuth();
215     const inboxRss = auth ? `/feeds/inbox/${auth}.xml` : undefined;
216     return (
217       <div className="inbox container-lg">
218         <div className="row">
219           <div className="col-12">
220             <HtmlTags
221               title={this.documentTitle}
222               path={this.context.router.route.match.url}
223             />
224             <h1 className="h4 mb-4">
225               {I18NextService.i18n.t("inbox")}
226               {inboxRss && (
227                 <small>
228                   <a href={inboxRss} title="RSS" rel={relTags}>
229                     <Icon icon="rss" classes="ms-2 text-muted small" />
230                   </a>
231                   <link
232                     rel="alternate"
233                     type="application/atom+xml"
234                     href={inboxRss}
235                   />
236                 </small>
237               )}
238             </h1>
239             {this.hasUnreads && (
240               <button
241                 className="btn btn-secondary mb-2 mb-sm-3"
242                 onClick={linkEvent(this, this.handleMarkAllAsRead)}
243               >
244                 {this.state.markAllAsReadRes.state == "loading" ? (
245                   <Spinner />
246                 ) : (
247                   capitalizeFirstLetter(
248                     I18NextService.i18n.t("mark_all_as_read")
249                   )
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">
288         <label
289           className={`btn btn-outline-secondary pointer
290             ${this.state.unreadOrAll == UnreadOrAll.Unread && "active"}
291           `}
292         >
293           <input
294             type="radio"
295             className="btn-check"
296             value={UnreadOrAll.Unread}
297             checked={this.state.unreadOrAll == UnreadOrAll.Unread}
298             onChange={linkEvent(this, this.handleUnreadOrAllChange)}
299           />
300           {I18NextService.i18n.t("unread")}
301         </label>
302         <label
303           className={`btn btn-outline-secondary pointer
304             ${this.state.unreadOrAll == UnreadOrAll.All && "active"}
305           `}
306         >
307           <input
308             type="radio"
309             className="btn-check"
310             value={UnreadOrAll.All}
311             checked={this.state.unreadOrAll == UnreadOrAll.All}
312             onChange={linkEvent(this, this.handleUnreadOrAllChange)}
313           />
314           {I18NextService.i18n.t("all")}
315         </label>
316       </div>
317     );
318   }
319
320   messageTypeRadios() {
321     return (
322       <div className="btn-group btn-group-toggle flex-wrap">
323         <label
324           className={`btn btn-outline-secondary pointer
325             ${this.state.messageType == MessageType.All && "active"}
326           `}
327         >
328           <input
329             type="radio"
330             className="btn-check"
331             value={MessageType.All}
332             checked={this.state.messageType == MessageType.All}
333             onChange={linkEvent(this, this.handleMessageTypeChange)}
334           />
335           {I18NextService.i18n.t("all")}
336         </label>
337         <label
338           className={`btn btn-outline-secondary pointer
339             ${this.state.messageType == MessageType.Replies && "active"}
340           `}
341         >
342           <input
343             type="radio"
344             className="btn-check"
345             value={MessageType.Replies}
346             checked={this.state.messageType == MessageType.Replies}
347             onChange={linkEvent(this, this.handleMessageTypeChange)}
348           />
349           {I18NextService.i18n.t("replies")}
350         </label>
351         <label
352           className={`btn btn-outline-secondary pointer
353             ${this.state.messageType == MessageType.Mentions && "active"}
354           `}
355         >
356           <input
357             type="radio"
358             className="btn-check"
359             value={MessageType.Mentions}
360             checked={this.state.messageType == MessageType.Mentions}
361             onChange={linkEvent(this, this.handleMessageTypeChange)}
362           />
363           {I18NextService.i18n.t("mentions")}
364         </label>
365         <label
366           className={`btn btn-outline-secondary pointer
367             ${this.state.messageType == MessageType.Messages && "active"}
368           `}
369         >
370           <input
371             type="radio"
372             className="btn-check"
373             value={MessageType.Messages}
374             checked={this.state.messageType == MessageType.Messages}
375             onChange={linkEvent(this, this.handleMessageTypeChange)}
376           />
377           {I18NextService.i18n.t("messages")}
378         </label>
379       </div>
380     );
381   }
382
383   selects() {
384     return (
385       <div className="row row-cols-auto g-2 g-sm-3 mb-2 mb-sm-3">
386         <div className="col">{this.unreadOrAllRadios()}</div>
387         <div className="col">{this.messageTypeRadios()}</div>
388         <div className="col">
389           <CommentSortSelect
390             sort={this.state.sort}
391             onChange={this.handleSortChange}
392           />
393         </div>
394       </div>
395     );
396   }
397
398   replyToReplyType(r: CommentReplyView): ReplyType {
399     return {
400       id: r.comment_reply.id,
401       type_: ReplyEnum.Reply,
402       view: r,
403       published: r.comment.published,
404     };
405   }
406
407   mentionToReplyType(r: PersonMentionView): ReplyType {
408     return {
409       id: r.person_mention.id,
410       type_: ReplyEnum.Mention,
411       view: r,
412       published: r.comment.published,
413     };
414   }
415
416   messageToReplyType(r: PrivateMessageView): ReplyType {
417     return {
418       id: r.private_message.id,
419       type_: ReplyEnum.Message,
420       view: r,
421       published: r.private_message.published,
422     };
423   }
424
425   buildCombined(): ReplyType[] {
426     const replies: ReplyType[] =
427       this.state.repliesRes.state == "success"
428         ? this.state.repliesRes.data.replies.map(this.replyToReplyType)
429         : [];
430     const mentions: ReplyType[] =
431       this.state.mentionsRes.state == "success"
432         ? this.state.mentionsRes.data.mentions.map(this.mentionToReplyType)
433         : [];
434     const messages: ReplyType[] =
435       this.state.messagesRes.state == "success"
436         ? this.state.messagesRes.data.private_messages.map(
437             this.messageToReplyType
438           )
439         : [];
440
441     return [...replies, ...mentions, ...messages].sort((a, b) =>
442       b.published.localeCompare(a.published)
443     );
444   }
445
446   renderReplyType(i: ReplyType) {
447     switch (i.type_) {
448       case ReplyEnum.Reply:
449         return (
450           <CommentNodes
451             key={i.id}
452             nodes={[
453               { comment_view: i.view as CommentView, children: [], depth: 0 },
454             ]}
455             viewType={CommentViewType.Flat}
456             finished={this.state.finished}
457             markable
458             showCommunity
459             showContext
460             enableDownvotes={enableDownvotes(this.state.siteRes)}
461             allLanguages={this.state.siteRes.all_languages}
462             siteLanguages={this.state.siteRes.discussion_languages}
463             onSaveComment={this.handleSaveComment}
464             onBlockPerson={this.handleBlockPerson}
465             onDeleteComment={this.handleDeleteComment}
466             onRemoveComment={this.handleRemoveComment}
467             onCommentVote={this.handleCommentVote}
468             onCommentReport={this.handleCommentReport}
469             onDistinguishComment={this.handleDistinguishComment}
470             onAddModToCommunity={this.handleAddModToCommunity}
471             onAddAdmin={this.handleAddAdmin}
472             onTransferCommunity={this.handleTransferCommunity}
473             onPurgeComment={this.handlePurgeComment}
474             onPurgePerson={this.handlePurgePerson}
475             onCommentReplyRead={this.handleCommentReplyRead}
476             onPersonMentionRead={this.handlePersonMentionRead}
477             onBanPersonFromCommunity={this.handleBanFromCommunity}
478             onBanPerson={this.handleBanPerson}
479             onCreateComment={this.handleCreateComment}
480             onEditComment={this.handleEditComment}
481           />
482         );
483       case ReplyEnum.Mention:
484         return (
485           <CommentNodes
486             key={i.id}
487             nodes={[
488               {
489                 comment_view: i.view as PersonMentionView,
490                 children: [],
491                 depth: 0,
492               },
493             ]}
494             finished={this.state.finished}
495             viewType={CommentViewType.Flat}
496             markable
497             showCommunity
498             showContext
499             enableDownvotes={enableDownvotes(this.state.siteRes)}
500             allLanguages={this.state.siteRes.all_languages}
501             siteLanguages={this.state.siteRes.discussion_languages}
502             onSaveComment={this.handleSaveComment}
503             onBlockPerson={this.handleBlockPerson}
504             onDeleteComment={this.handleDeleteComment}
505             onRemoveComment={this.handleRemoveComment}
506             onCommentVote={this.handleCommentVote}
507             onCommentReport={this.handleCommentReport}
508             onDistinguishComment={this.handleDistinguishComment}
509             onAddModToCommunity={this.handleAddModToCommunity}
510             onAddAdmin={this.handleAddAdmin}
511             onTransferCommunity={this.handleTransferCommunity}
512             onPurgeComment={this.handlePurgeComment}
513             onPurgePerson={this.handlePurgePerson}
514             onCommentReplyRead={this.handleCommentReplyRead}
515             onPersonMentionRead={this.handlePersonMentionRead}
516             onBanPersonFromCommunity={this.handleBanFromCommunity}
517             onBanPerson={this.handleBanPerson}
518             onCreateComment={this.handleCreateComment}
519             onEditComment={this.handleEditComment}
520           />
521         );
522       case ReplyEnum.Message:
523         return (
524           <PrivateMessage
525             key={i.id}
526             private_message_view={i.view as PrivateMessageView}
527             onDelete={this.handleDeleteMessage}
528             onMarkRead={this.handleMarkMessageAsRead}
529             onReport={this.handleMessageReport}
530             onCreate={this.handleCreateMessage}
531             onEdit={this.handleEditMessage}
532           />
533         );
534       default:
535         return <div />;
536     }
537   }
538
539   all() {
540     if (
541       this.state.repliesRes.state == "loading" ||
542       this.state.mentionsRes.state == "loading" ||
543       this.state.messagesRes.state == "loading"
544     ) {
545       return (
546         <h1 className="h4">
547           <Spinner large />
548         </h1>
549       );
550     } else {
551       return (
552         <div>{this.buildCombined().map(r => this.renderReplyType(r))}</div>
553       );
554     }
555   }
556
557   replies() {
558     switch (this.state.repliesRes.state) {
559       case "loading":
560         return (
561           <h1 className="h4">
562             <Spinner large />
563           </h1>
564         );
565       case "success": {
566         const replies = this.state.repliesRes.data.replies;
567         return (
568           <div>
569             <CommentNodes
570               nodes={commentsToFlatNodes(replies)}
571               viewType={CommentViewType.Flat}
572               finished={this.state.finished}
573               markable
574               showCommunity
575               showContext
576               enableDownvotes={enableDownvotes(this.state.siteRes)}
577               allLanguages={this.state.siteRes.all_languages}
578               siteLanguages={this.state.siteRes.discussion_languages}
579               onSaveComment={this.handleSaveComment}
580               onBlockPerson={this.handleBlockPerson}
581               onDeleteComment={this.handleDeleteComment}
582               onRemoveComment={this.handleRemoveComment}
583               onCommentVote={this.handleCommentVote}
584               onCommentReport={this.handleCommentReport}
585               onDistinguishComment={this.handleDistinguishComment}
586               onAddModToCommunity={this.handleAddModToCommunity}
587               onAddAdmin={this.handleAddAdmin}
588               onTransferCommunity={this.handleTransferCommunity}
589               onPurgeComment={this.handlePurgeComment}
590               onPurgePerson={this.handlePurgePerson}
591               onCommentReplyRead={this.handleCommentReplyRead}
592               onPersonMentionRead={this.handlePersonMentionRead}
593               onBanPersonFromCommunity={this.handleBanFromCommunity}
594               onBanPerson={this.handleBanPerson}
595               onCreateComment={this.handleCreateComment}
596               onEditComment={this.handleEditComment}
597             />
598           </div>
599         );
600       }
601     }
602   }
603
604   mentions() {
605     switch (this.state.mentionsRes.state) {
606       case "loading":
607         return (
608           <h1 className="h4">
609             <Spinner large />
610           </h1>
611         );
612       case "success": {
613         const mentions = this.state.mentionsRes.data.mentions;
614         return (
615           <div>
616             {mentions.map(umv => (
617               <CommentNodes
618                 key={umv.person_mention.id}
619                 nodes={[{ comment_view: umv, children: [], depth: 0 }]}
620                 viewType={CommentViewType.Flat}
621                 finished={this.state.finished}
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           <h1 className="h4">
659             <Spinner large />
660           </h1>
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       mentionsRes: auth
706         ? await client.getPersonMentions({
707             sort,
708             unread_only: true,
709             page: 1,
710             limit: fetchLimit,
711             auth,
712           })
713         : { state: "empty" },
714       messagesRes: auth
715         ? await client.getPrivateMessages({
716             unread_only: true,
717             page: 1,
718             limit: fetchLimit,
719             auth,
720           })
721         : { state: "empty" },
722       repliesRes: 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(I18NextService.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(I18NextService.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(I18NextService.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(I18NextService.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(I18NextService.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(I18NextService.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(I18NextService.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 }