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