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