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