]> Untitled Git - lemmy-ui.git/blob - src/shared/components/modlog.tsx
Actually sending ws requests, some other fixes.
[lemmy-ui.git] / src / shared / components / modlog.tsx
1 import { Component, linkEvent } from 'inferno';
2 import { Link } from 'inferno-router';
3 import { Subscription } from 'rxjs';
4 import {
5   UserOperation,
6   GetModlog,
7   GetModlogResponse,
8   SiteView,
9   ModRemovePostView,
10   ModLockPostView,
11   ModStickyPostView,
12   ModRemoveCommentView,
13   ModRemoveCommunityView,
14   ModBanFromCommunityView,
15   ModBanView,
16   ModAddCommunityView,
17   ModAddView,
18 } from 'lemmy-js-client';
19 import { WebSocketService } from '../services';
20 import {
21   wsJsonToRes,
22   fetchLimit,
23   toast,
24   setIsoData,
25   wsSubscribe,
26   isBrowser,
27   wsUserOp,
28   wsClient,
29 } from '../utils';
30 import { MomentTime } from './moment-time';
31 import { HtmlTags } from './html-tags';
32 import moment from 'moment';
33 import { i18n } from '../i18next';
34 import { InitialFetchRequest } from 'shared/interfaces';
35 import { UserListing } from './user-listing';
36 import { CommunityLink } from './community-link';
37
38 enum ModlogEnum {
39   ModRemovePost,
40   ModLockPost,
41   ModStickyPost,
42   ModRemoveComment,
43   ModRemoveCommunity,
44   ModBanFromCommunity,
45   ModAddCommunity,
46   ModAdd,
47   ModBan,
48 }
49
50 type ModlogType = {
51   id: number;
52   type_: ModlogEnum;
53   view:
54     | ModRemovePostView
55     | ModLockPostView
56     | ModStickyPostView
57     | ModRemoveCommentView
58     | ModRemoveCommunityView
59     | ModBanFromCommunityView
60     | ModBanView
61     | ModAddCommunityView
62     | ModAddView;
63   when_: string;
64 };
65
66 interface ModlogState {
67   res: GetModlogResponse;
68   communityId?: number;
69   communityName?: string;
70   page: number;
71   site_view: SiteView;
72   loading: boolean;
73 }
74
75 export class Modlog extends Component<any, ModlogState> {
76   private isoData = setIsoData(this.context);
77   private subscription: Subscription;
78   private emptyState: ModlogState = {
79     res: {
80       removed_posts: [],
81       locked_posts: [],
82       stickied_posts: [],
83       removed_comments: [],
84       removed_communities: [],
85       banned_from_community: [],
86       banned: [],
87       added_to_community: [],
88       added: [],
89     },
90     page: 1,
91     loading: true,
92     site_view: this.isoData.site_res.site_view,
93   };
94
95   constructor(props: any, context: any) {
96     super(props, context);
97
98     this.state = this.emptyState;
99     this.state.communityId = this.props.match.params.community_id
100       ? Number(this.props.match.params.community_id)
101       : undefined;
102
103     this.parseMessage = this.parseMessage.bind(this);
104     this.subscription = wsSubscribe(this.parseMessage);
105
106     // Only fetch the data if coming from another route
107     if (this.isoData.path == this.context.router.route.match.url) {
108       let data = this.isoData.routeData[0];
109       this.state.res = data;
110       this.state.loading = false;
111     } else {
112       this.refetch();
113     }
114   }
115
116   componentWillUnmount() {
117     if (isBrowser()) {
118       this.subscription.unsubscribe();
119     }
120   }
121
122   buildCombined(res: GetModlogResponse): ModlogType[] {
123     let removed_posts: ModlogType[] = res.removed_posts.map(r => ({
124       id: r.mod_remove_post.id,
125       type_: ModlogEnum.ModRemovePost,
126       view: r,
127       when_: r.mod_remove_post.when_,
128     }));
129
130     let locked_posts: ModlogType[] = res.locked_posts.map(r => ({
131       id: r.mod_lock_post.id,
132       type_: ModlogEnum.ModLockPost,
133       view: r,
134       when_: r.mod_lock_post.when_,
135     }));
136
137     let stickied_posts: ModlogType[] = res.stickied_posts.map(r => ({
138       id: r.mod_sticky_post.id,
139       type_: ModlogEnum.ModStickyPost,
140       view: r,
141       when_: r.mod_sticky_post.when_,
142     }));
143
144     let removed_comments: ModlogType[] = res.removed_comments.map(r => ({
145       id: r.mod_remove_comment.id,
146       type_: ModlogEnum.ModRemoveComment,
147       view: r,
148       when_: r.mod_remove_comment.when_,
149     }));
150
151     let removed_communities: ModlogType[] = res.removed_communities.map(r => ({
152       id: r.mod_remove_community.id,
153       type_: ModlogEnum.ModRemoveCommunity,
154       view: r,
155       when_: r.mod_remove_community.when_,
156     }));
157
158     let banned_from_community: ModlogType[] = res.banned_from_community.map(
159       r => ({
160         id: r.mod_ban_from_community.id,
161         type_: ModlogEnum.ModBanFromCommunity,
162         view: r,
163         when_: r.mod_ban_from_community.when_,
164       })
165     );
166
167     let added_to_community: ModlogType[] = res.added_to_community.map(r => ({
168       id: r.mod_add_community.id,
169       type_: ModlogEnum.ModAddCommunity,
170       view: r,
171       when_: r.mod_add_community.when_,
172     }));
173
174     let added: ModlogType[] = res.added.map(r => ({
175       id: r.mod_add.id,
176       type_: ModlogEnum.ModAdd,
177       view: r,
178       when_: r.mod_add.when_,
179     }));
180
181     let banned: ModlogType[] = res.banned.map(r => ({
182       id: r.mod_ban.id,
183       type_: ModlogEnum.ModBan,
184       view: r,
185       when_: r.mod_ban.when_,
186     }));
187
188     let combined: ModlogType[] = [];
189
190     combined.push(...removed_posts);
191     combined.push(...locked_posts);
192     combined.push(...stickied_posts);
193     combined.push(...removed_comments);
194     combined.push(...removed_communities);
195     combined.push(...banned_from_community);
196     combined.push(...added_to_community);
197     combined.push(...added);
198     combined.push(...banned);
199
200     if (this.state.communityId && combined.length > 0) {
201       this.state.communityName = (combined[0]
202         .view as ModRemovePostView).community.name;
203     }
204
205     // Sort them by time
206     combined.sort((a, b) => b.when_.localeCompare(a.when_));
207
208     return combined;
209   }
210
211   renderModlogType(i: ModlogType) {
212     switch (i.type_) {
213       case ModlogEnum.ModRemovePost:
214         let mrpv = i.view as ModRemovePostView;
215         return [
216           mrpv.mod_remove_post.removed ? 'Removed ' : 'Restored ',
217           <span>
218             Post <Link to={`/post/${mrpv.post.id}`}>{mrpv.post.name}</Link>
219           </span>,
220           mrpv.mod_remove_post.reason &&
221             ` reason: ${mrpv.mod_remove_post.reason}`,
222         ];
223       case ModlogEnum.ModLockPost:
224         let mlpv = i.view as ModLockPostView;
225         return [
226           mlpv.mod_lock_post.locked ? 'Locked ' : 'Unlocked ',
227           <span>
228             Post <Link to={`/post/${mlpv.post.id}`}>{mlpv.post.name}</Link>
229           </span>,
230         ];
231       case ModlogEnum.ModStickyPost:
232         let mspv = i.view as ModStickyPostView;
233         return [
234           mspv.mod_sticky_post.stickied ? 'Stickied ' : 'Unstickied ',
235           <span>
236             Post <Link to={`/post/${mspv.post.id}`}>{mspv.post.name}</Link>
237           </span>,
238         ];
239       case ModlogEnum.ModRemoveComment:
240         let mrc = i.view as ModRemoveCommentView;
241         return [
242           mrc.mod_remove_comment.removed ? 'Removed ' : 'Restored ',
243           <span>
244             Comment{' '}
245             <Link to={`/post/${mrc.post.id}/comment/${mrc.comment.id}`}>
246               {mrc.comment.content}
247             </Link>
248           </span>,
249           <span>
250             {' '}
251             by <UserListing user={mrc.commenter} />
252           </span>,
253           mrc.mod_remove_comment.reason &&
254             ` reason: ${mrc.mod_remove_comment.reason}`,
255         ];
256       case ModlogEnum.ModRemoveCommunity:
257         let mrco = i.view as ModRemoveCommunityView;
258         return [
259           mrco.mod_remove_community.removed ? 'Removed ' : 'Restored ',
260           <span>
261             Community <CommunityLink community={mrco.community} />
262           </span>,
263           mrco.mod_remove_community.reason &&
264             ` reason: ${mrco.mod_remove_community.reason}`,
265           mrco.mod_remove_community.expires &&
266             ` expires: ${moment
267               .utc(mrco.mod_remove_community.expires)
268               .fromNow()}`,
269         ];
270       case ModlogEnum.ModBanFromCommunity:
271         let mbfc = i.view as ModBanFromCommunityView;
272         return [
273           <span>
274             {mbfc.mod_ban_from_community.banned ? 'Banned ' : 'Unbanned '}{' '}
275           </span>,
276           <span>
277             <UserListing user={mbfc.banned_user} />
278           </span>,
279           <span> from the community </span>,
280           <span>
281             <CommunityLink community={mbfc.community} />
282           </span>,
283           <div>
284             {mbfc.mod_ban_from_community.reason &&
285               ` reason: ${mbfc.mod_ban_from_community.reason}`}
286           </div>,
287           <div>
288             {mbfc.mod_ban_from_community.expires &&
289               ` expires: ${moment
290                 .utc(mbfc.mod_ban_from_community.expires)
291                 .fromNow()}`}
292           </div>,
293         ];
294       case ModlogEnum.ModAddCommunity:
295         let mac = i.view as ModAddCommunityView;
296         return [
297           <span>
298             {mac.mod_add_community.removed ? 'Removed ' : 'Appointed '}{' '}
299           </span>,
300           <span>
301             <UserListing user={mac.modded_user} />
302           </span>,
303           <span> as a mod to the community </span>,
304           <span>
305             <CommunityLink community={mac.community} />
306           </span>,
307         ];
308       case ModlogEnum.ModBan:
309         let mb = i.view as ModBanView;
310         return [
311           <span>{mb.mod_ban.banned ? 'Banned ' : 'Unbanned '} </span>,
312           <span>
313             <UserListing user={mb.banned_user} />
314           </span>,
315           <div>{mb.mod_ban.reason && ` reason: ${mb.mod_ban.reason}`}</div>,
316           <div>
317             {mb.mod_ban.expires &&
318               ` expires: ${moment.utc(mb.mod_ban.expires).fromNow()}`}
319           </div>,
320         ];
321       case ModlogEnum.ModAdd:
322         let ma = i.view as ModAddView;
323         return [
324           <span>{ma.mod_add.removed ? 'Removed ' : 'Appointed '} </span>,
325           <span>
326             <UserListing user={ma.modded_user} />
327           </span>,
328           <span> as an admin </span>,
329         ];
330       default:
331         return <div />;
332     }
333   }
334
335   combined() {
336     let combined = this.buildCombined(this.state.res);
337
338     return (
339       <tbody>
340         {combined.map(i => (
341           <tr>
342             <td>
343               <MomentTime data={i} />
344             </td>
345             <td>
346               <UserListing user={i.view.moderator} />
347             </td>
348             <td>{this.renderModlogType(i)}</td>
349           </tr>
350         ))}
351       </tbody>
352     );
353   }
354
355   get documentTitle(): string {
356     return `Modlog - ${this.state.site_view.site.name}`;
357   }
358
359   render() {
360     return (
361       <div class="container">
362         <HtmlTags
363           title={this.documentTitle}
364           path={this.context.router.route.match.url}
365         />
366         {this.state.loading ? (
367           <h5 class="">
368             <svg class="icon icon-spinner spin">
369               <use xlinkHref="#icon-spinner"></use>
370             </svg>
371           </h5>
372         ) : (
373           <div>
374             <h5>
375               {this.state.communityName && (
376                 <Link
377                   className="text-body"
378                   to={`/c/${this.state.communityName}`}
379                 >
380                   /c/{this.state.communityName}{' '}
381                 </Link>
382               )}
383               <span>{i18n.t('modlog')}</span>
384             </h5>
385             <div class="table-responsive">
386               <table id="modlog_table" class="table table-sm table-hover">
387                 <thead class="pointer">
388                   <tr>
389                     <th> {i18n.t('time')}</th>
390                     <th>{i18n.t('mod')}</th>
391                     <th>{i18n.t('action')}</th>
392                   </tr>
393                 </thead>
394                 {this.combined()}
395               </table>
396               {this.paginator()}
397             </div>
398           </div>
399         )}
400       </div>
401     );
402   }
403
404   paginator() {
405     return (
406       <div class="mt-2">
407         {this.state.page > 1 && (
408           <button
409             class="btn btn-secondary mr-1"
410             onClick={linkEvent(this, this.prevPage)}
411           >
412             {i18n.t('prev')}
413           </button>
414         )}
415         <button
416           class="btn btn-secondary"
417           onClick={linkEvent(this, this.nextPage)}
418         >
419           {i18n.t('next')}
420         </button>
421       </div>
422     );
423   }
424
425   nextPage(i: Modlog) {
426     i.state.page++;
427     i.setState(i.state);
428     i.refetch();
429   }
430
431   prevPage(i: Modlog) {
432     i.state.page--;
433     i.setState(i.state);
434     i.refetch();
435   }
436
437   refetch() {
438     let modlogForm: GetModlog = {
439       community_id: this.state.communityId,
440       page: this.state.page,
441       limit: fetchLimit,
442     };
443     WebSocketService.Instance.send(wsClient.getModlog(modlogForm));
444   }
445
446   static fetchInitialData(req: InitialFetchRequest): Promise<any>[] {
447     let pathSplit = req.path.split('/');
448     let communityId = pathSplit[3];
449     let promises: Promise<any>[] = [];
450
451     let modlogForm: GetModlog = {
452       page: 1,
453       limit: fetchLimit,
454     };
455
456     if (communityId) {
457       modlogForm.community_id = Number(communityId);
458     }
459
460     promises.push(req.client.getModlog(modlogForm));
461     return promises;
462   }
463
464   parseMessage(msg: any) {
465     let op = wsUserOp(msg);
466     if (msg.error) {
467       toast(i18n.t(msg.error), 'danger');
468       return;
469     } else if (op == UserOperation.GetModlog) {
470       let data = wsJsonToRes<GetModlogResponse>(msg).data;
471       this.state.loading = false;
472       window.scrollTo(0, 0);
473       this.state.res = data;
474       this.setState(this.state);
475     }
476   }
477 }