]> Untitled Git - lemmy-ui.git/blob - src/shared/components/person/reports.tsx
Adding a few more 0.18.0 API changes. (#1324)
[lemmy-ui.git] / src / shared / components / person / reports.tsx
1 import { Component, linkEvent } from "inferno";
2 import {
3   CommentReportResponse,
4   CommentReportView,
5   GetSiteResponse,
6   ListCommentReports,
7   ListCommentReportsResponse,
8   ListPostReports,
9   ListPostReportsResponse,
10   ListPrivateMessageReports,
11   ListPrivateMessageReportsResponse,
12   PostReportResponse,
13   PostReportView,
14   PrivateMessageReportResponse,
15   PrivateMessageReportView,
16   ResolveCommentReport,
17   ResolvePostReport,
18   ResolvePrivateMessageReport,
19 } from "lemmy-js-client";
20 import { i18n } from "../../i18next";
21 import { InitialFetchRequest } from "../../interfaces";
22 import { HttpService, UserService } from "../../services";
23 import { FirstLoadService } from "../../services/FirstLoadService";
24 import { RequestState } from "../../services/HttpService";
25 import {
26   amAdmin,
27   editCommentReport,
28   editPostReport,
29   editPrivateMessageReport,
30   fetchLimit,
31   myAuthRequired,
32   setIsoData,
33 } from "../../utils";
34 import { CommentReport } from "../comment/comment-report";
35 import { HtmlTags } from "../common/html-tags";
36 import { Spinner } from "../common/icon";
37 import { Paginator } from "../common/paginator";
38 import { PostReport } from "../post/post-report";
39 import { PrivateMessageReport } from "../private_message/private-message-report";
40
41 enum UnreadOrAll {
42   Unread,
43   All,
44 }
45
46 enum MessageType {
47   All,
48   CommentReport,
49   PostReport,
50   PrivateMessageReport,
51 }
52
53 enum MessageEnum {
54   CommentReport,
55   PostReport,
56   PrivateMessageReport,
57 }
58
59 type ItemType = {
60   id: number;
61   type_: MessageEnum;
62   view: CommentReportView | PostReportView | PrivateMessageReportView;
63   published: string;
64 };
65
66 interface ReportsState {
67   commentReportsRes: RequestState<ListCommentReportsResponse>;
68   postReportsRes: RequestState<ListPostReportsResponse>;
69   messageReportsRes: RequestState<ListPrivateMessageReportsResponse>;
70   unreadOrAll: UnreadOrAll;
71   messageType: MessageType;
72   siteRes: GetSiteResponse;
73   page: number;
74   isIsomorphic: boolean;
75 }
76
77 export class Reports extends Component<any, ReportsState> {
78   private isoData = setIsoData(this.context);
79   state: ReportsState = {
80     commentReportsRes: { state: "empty" },
81     postReportsRes: { state: "empty" },
82     messageReportsRes: { state: "empty" },
83     unreadOrAll: UnreadOrAll.Unread,
84     messageType: MessageType.All,
85     page: 1,
86     siteRes: this.isoData.site_res,
87     isIsomorphic: false,
88   };
89
90   constructor(props: any, context: any) {
91     super(props, context);
92
93     this.handlePageChange = this.handlePageChange.bind(this);
94     this.handleResolveCommentReport =
95       this.handleResolveCommentReport.bind(this);
96     this.handleResolvePostReport = this.handleResolvePostReport.bind(this);
97     this.handleResolvePrivateMessageReport =
98       this.handleResolvePrivateMessageReport.bind(this);
99
100     // Only fetch the data if coming from another route
101     if (FirstLoadService.isFirstLoad) {
102       const [commentReportsRes, postReportsRes, messageReportsRes] =
103         this.isoData.routeData;
104       this.state = {
105         ...this.state,
106         commentReportsRes,
107         postReportsRes,
108         isIsomorphic: true,
109       };
110
111       if (amAdmin()) {
112         this.state = {
113           ...this.state,
114           messageReportsRes,
115         };
116       }
117     }
118   }
119
120   async componentDidMount() {
121     if (!this.state.isIsomorphic) {
122       await this.refetch();
123     }
124   }
125
126   get documentTitle(): string {
127     const mui = UserService.Instance.myUserInfo;
128     return mui
129       ? `@${mui.local_user_view.person.name} ${i18n.t("reports")} - ${
130           this.state.siteRes.site_view.site.name
131         }`
132       : "";
133   }
134
135   render() {
136     return (
137       <div className="container-lg">
138         <div className="row">
139           <div className="col-12">
140             <HtmlTags
141               title={this.documentTitle}
142               path={this.context.router.route.match.url}
143             />
144             <h5 className="mb-2">{i18n.t("reports")}</h5>
145             {this.selects()}
146             {this.section}
147             <Paginator
148               page={this.state.page}
149               onChange={this.handlePageChange}
150             />
151           </div>
152         </div>
153       </div>
154     );
155   }
156
157   get section() {
158     switch (this.state.messageType) {
159       case MessageType.All: {
160         return this.all();
161       }
162       case MessageType.CommentReport: {
163         return this.commentReports();
164       }
165       case MessageType.PostReport: {
166         return this.postReports();
167       }
168       case MessageType.PrivateMessageReport: {
169         return this.privateMessageReports();
170       }
171
172       default: {
173         return null;
174       }
175     }
176   }
177
178   unreadOrAllRadios() {
179     return (
180       <div className="btn-group btn-group-toggle flex-wrap mb-2">
181         <label
182           className={`btn btn-outline-secondary pointer
183             ${this.state.unreadOrAll == UnreadOrAll.Unread && "active"}
184           `}
185         >
186           <input
187             type="radio"
188             value={UnreadOrAll.Unread}
189             checked={this.state.unreadOrAll == UnreadOrAll.Unread}
190             onChange={linkEvent(this, this.handleUnreadOrAllChange)}
191           />
192           {i18n.t("unread")}
193         </label>
194         <label
195           className={`btn btn-outline-secondary pointer
196             ${this.state.unreadOrAll == UnreadOrAll.All && "active"}
197           `}
198         >
199           <input
200             type="radio"
201             value={UnreadOrAll.All}
202             checked={this.state.unreadOrAll == UnreadOrAll.All}
203             onChange={linkEvent(this, this.handleUnreadOrAllChange)}
204           />
205           {i18n.t("all")}
206         </label>
207       </div>
208     );
209   }
210
211   messageTypeRadios() {
212     return (
213       <div className="btn-group btn-group-toggle flex-wrap mb-2">
214         <label
215           className={`btn btn-outline-secondary pointer
216             ${this.state.messageType == MessageType.All && "active"}
217           `}
218         >
219           <input
220             type="radio"
221             value={MessageType.All}
222             checked={this.state.messageType == MessageType.All}
223             onChange={linkEvent(this, this.handleMessageTypeChange)}
224           />
225           {i18n.t("all")}
226         </label>
227         <label
228           className={`btn btn-outline-secondary pointer
229             ${this.state.messageType == MessageType.CommentReport && "active"}
230           `}
231         >
232           <input
233             type="radio"
234             value={MessageType.CommentReport}
235             checked={this.state.messageType == MessageType.CommentReport}
236             onChange={linkEvent(this, this.handleMessageTypeChange)}
237           />
238           {i18n.t("comments")}
239         </label>
240         <label
241           className={`btn btn-outline-secondary pointer
242             ${this.state.messageType == MessageType.PostReport && "active"}
243           `}
244         >
245           <input
246             type="radio"
247             value={MessageType.PostReport}
248             checked={this.state.messageType == MessageType.PostReport}
249             onChange={linkEvent(this, this.handleMessageTypeChange)}
250           />
251           {i18n.t("posts")}
252         </label>
253         {amAdmin() && (
254           <label
255             className={`btn btn-outline-secondary pointer
256             ${
257               this.state.messageType == MessageType.PrivateMessageReport &&
258               "active"
259             }
260           `}
261           >
262             <input
263               type="radio"
264               value={MessageType.PrivateMessageReport}
265               checked={
266                 this.state.messageType == MessageType.PrivateMessageReport
267               }
268               onChange={linkEvent(this, this.handleMessageTypeChange)}
269             />
270             {i18n.t("messages")}
271           </label>
272         )}
273       </div>
274     );
275   }
276
277   selects() {
278     return (
279       <div className="mb-2">
280         <span className="mr-3">{this.unreadOrAllRadios()}</span>
281         <span className="mr-3">{this.messageTypeRadios()}</span>
282       </div>
283     );
284   }
285
286   commentReportToItemType(r: CommentReportView): ItemType {
287     return {
288       id: r.comment_report.id,
289       type_: MessageEnum.CommentReport,
290       view: r,
291       published: r.comment_report.published,
292     };
293   }
294
295   postReportToItemType(r: PostReportView): ItemType {
296     return {
297       id: r.post_report.id,
298       type_: MessageEnum.PostReport,
299       view: r,
300       published: r.post_report.published,
301     };
302   }
303
304   privateMessageReportToItemType(r: PrivateMessageReportView): ItemType {
305     return {
306       id: r.private_message_report.id,
307       type_: MessageEnum.PrivateMessageReport,
308       view: r,
309       published: r.private_message_report.published,
310     };
311   }
312
313   get buildCombined(): ItemType[] {
314     const commentRes = this.state.commentReportsRes;
315     const comments =
316       commentRes.state == "success"
317         ? commentRes.data.comment_reports.map(this.commentReportToItemType)
318         : [];
319
320     const postRes = this.state.postReportsRes;
321     const posts =
322       postRes.state == "success"
323         ? postRes.data.post_reports.map(this.postReportToItemType)
324         : [];
325     const pmRes = this.state.messageReportsRes;
326     const privateMessages =
327       pmRes.state == "success"
328         ? pmRes.data.private_message_reports.map(
329             this.privateMessageReportToItemType
330           )
331         : [];
332
333     return [...comments, ...posts, ...privateMessages].sort((a, b) =>
334       b.published.localeCompare(a.published)
335     );
336   }
337
338   renderItemType(i: ItemType) {
339     switch (i.type_) {
340       case MessageEnum.CommentReport:
341         return (
342           <CommentReport
343             key={i.id}
344             report={i.view as CommentReportView}
345             onResolveReport={this.handleResolveCommentReport}
346           />
347         );
348       case MessageEnum.PostReport:
349         return (
350           <PostReport
351             key={i.id}
352             report={i.view as PostReportView}
353             onResolveReport={this.handleResolvePostReport}
354           />
355         );
356       case MessageEnum.PrivateMessageReport:
357         return (
358           <PrivateMessageReport
359             key={i.id}
360             report={i.view as PrivateMessageReportView}
361             onResolveReport={this.handleResolvePrivateMessageReport}
362           />
363         );
364       default:
365         return <div />;
366     }
367   }
368
369   all() {
370     return (
371       <div>
372         {this.buildCombined.map(i => (
373           <>
374             <hr />
375             {this.renderItemType(i)}
376           </>
377         ))}
378       </div>
379     );
380   }
381
382   commentReports() {
383     const res = this.state.commentReportsRes;
384     switch (res.state) {
385       case "loading":
386         return (
387           <h5>
388             <Spinner large />
389           </h5>
390         );
391       case "success": {
392         const reports = res.data.comment_reports;
393         return (
394           <div>
395             {reports.map(cr => (
396               <>
397                 <hr />
398                 <CommentReport
399                   key={cr.comment_report.id}
400                   report={cr}
401                   onResolveReport={this.handleResolveCommentReport}
402                 />
403               </>
404             ))}
405           </div>
406         );
407       }
408     }
409   }
410
411   postReports() {
412     const res = this.state.postReportsRes;
413     switch (res.state) {
414       case "loading":
415         return (
416           <h5>
417             <Spinner large />
418           </h5>
419         );
420       case "success": {
421         const reports = res.data.post_reports;
422         return (
423           <div>
424             {reports.map(pr => (
425               <>
426                 <hr />
427                 <PostReport
428                   key={pr.post_report.id}
429                   report={pr}
430                   onResolveReport={this.handleResolvePostReport}
431                 />
432               </>
433             ))}
434           </div>
435         );
436       }
437     }
438   }
439
440   privateMessageReports() {
441     const res = this.state.messageReportsRes;
442     switch (res.state) {
443       case "loading":
444         return (
445           <h5>
446             <Spinner large />
447           </h5>
448         );
449       case "success": {
450         const reports = res.data.private_message_reports;
451         return (
452           <div>
453             {reports.map(pmr => (
454               <>
455                 <hr />
456                 <PrivateMessageReport
457                   key={pmr.private_message_report.id}
458                   report={pmr}
459                   onResolveReport={this.handleResolvePrivateMessageReport}
460                 />
461               </>
462             ))}
463           </div>
464         );
465       }
466     }
467   }
468
469   async handlePageChange(page: number) {
470     this.setState({ page });
471     await this.refetch();
472   }
473
474   async handleUnreadOrAllChange(i: Reports, event: any) {
475     i.setState({ unreadOrAll: Number(event.target.value), page: 1 });
476     await i.refetch();
477   }
478
479   async handleMessageTypeChange(i: Reports, event: any) {
480     i.setState({ messageType: Number(event.target.value), page: 1 });
481     await i.refetch();
482   }
483
484   static fetchInitialData({
485     auth,
486     client,
487   }: InitialFetchRequest): Promise<any>[] {
488     const promises: Promise<RequestState<any>>[] = [];
489
490     const unresolved_only = true;
491     const page = 1;
492     const limit = fetchLimit;
493
494     if (auth) {
495       const commentReportsForm: ListCommentReports = {
496         unresolved_only,
497         page,
498         limit,
499         auth,
500       };
501       promises.push(client.listCommentReports(commentReportsForm));
502
503       const postReportsForm: ListPostReports = {
504         unresolved_only,
505         page,
506         limit,
507         auth,
508       };
509       promises.push(client.listPostReports(postReportsForm));
510
511       if (amAdmin()) {
512         const privateMessageReportsForm: ListPrivateMessageReports = {
513           unresolved_only,
514           page,
515           limit,
516           auth,
517         };
518         promises.push(
519           client.listPrivateMessageReports(privateMessageReportsForm)
520         );
521       } else {
522         promises.push(Promise.resolve({ state: "empty" }));
523       }
524     } else {
525       promises.push(
526         Promise.resolve({ state: "empty" }),
527         Promise.resolve({ state: "empty" }),
528         Promise.resolve({ state: "empty" })
529       );
530     }
531
532     return promises;
533   }
534
535   async refetch() {
536     const unresolved_only = this.state.unreadOrAll == UnreadOrAll.Unread;
537     const page = this.state.page;
538     const limit = fetchLimit;
539     const auth = myAuthRequired();
540
541     this.setState({
542       commentReportsRes: { state: "loading" },
543       postReportsRes: { state: "loading" },
544       messageReportsRes: { state: "loading" },
545     });
546
547     const form:
548       | ListCommentReports
549       | ListPostReports
550       | ListPrivateMessageReports = {
551       unresolved_only,
552       page,
553       limit,
554       auth,
555     };
556
557     this.setState({
558       commentReportsRes: await HttpService.client.listCommentReports(form),
559       postReportsRes: await HttpService.client.listPostReports(form),
560     });
561
562     if (amAdmin()) {
563       this.setState({
564         messageReportsRes: await HttpService.client.listPrivateMessageReports(
565           form
566         ),
567       });
568     }
569   }
570
571   async handleResolveCommentReport(form: ResolveCommentReport) {
572     const res = await HttpService.client.resolveCommentReport(form);
573     this.findAndUpdateCommentReport(res);
574   }
575
576   async handleResolvePostReport(form: ResolvePostReport) {
577     const res = await HttpService.client.resolvePostReport(form);
578     this.findAndUpdatePostReport(res);
579   }
580
581   async handleResolvePrivateMessageReport(form: ResolvePrivateMessageReport) {
582     const res = await HttpService.client.resolvePrivateMessageReport(form);
583     this.findAndUpdatePrivateMessageReport(res);
584   }
585
586   findAndUpdateCommentReport(res: RequestState<CommentReportResponse>) {
587     this.setState(s => {
588       if (s.commentReportsRes.state == "success" && res.state == "success") {
589         s.commentReportsRes.data.comment_reports = editCommentReport(
590           res.data.comment_report_view,
591           s.commentReportsRes.data.comment_reports
592         );
593       }
594       return s;
595     });
596   }
597
598   findAndUpdatePostReport(res: RequestState<PostReportResponse>) {
599     this.setState(s => {
600       if (s.postReportsRes.state == "success" && res.state == "success") {
601         s.postReportsRes.data.post_reports = editPostReport(
602           res.data.post_report_view,
603           s.postReportsRes.data.post_reports
604         );
605       }
606       return s;
607     });
608   }
609
610   findAndUpdatePrivateMessageReport(
611     res: RequestState<PrivateMessageReportResponse>
612   ) {
613     this.setState(s => {
614       if (s.messageReportsRes.state == "success" && res.state == "success") {
615         s.messageReportsRes.data.private_message_reports =
616           editPrivateMessageReport(
617             res.data.private_message_report_view,
618             s.messageReportsRes.data.private_message_reports
619           );
620       }
621       return s;
622     });
623   }
624 }