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