]> Untitled Git - lemmy-ui.git/blob - src/shared/components/modlog.tsx
Adding new site setup fields. (#840)
[lemmy-ui.git] / src / shared / components / modlog.tsx
1 import { None, Option, Some } from "@sniptt/monads";
2 import { Component, linkEvent } from "inferno";
3 import { Link } from "inferno-router";
4 import {
5   AdminPurgeCommentView,
6   AdminPurgeCommunityView,
7   AdminPurgePersonView,
8   AdminPurgePostView,
9   CommunityModeratorView,
10   GetCommunity,
11   GetCommunityResponse,
12   GetModlog,
13   GetModlogResponse,
14   GetSiteResponse,
15   ModAddCommunityView,
16   ModAddView,
17   ModBanFromCommunityView,
18   ModBanView,
19   ModLockPostView,
20   ModlogActionType,
21   ModRemoveCommentView,
22   ModRemoveCommunityView,
23   ModRemovePostView,
24   ModStickyPostView,
25   ModTransferCommunityView,
26   PersonSafe,
27   toUndefined,
28   UserOperation,
29   wsJsonToRes,
30   wsUserOp,
31 } from "lemmy-js-client";
32 import moment from "moment";
33 import { Subscription } from "rxjs";
34 import { i18n } from "../i18next";
35 import { InitialFetchRequest } from "../interfaces";
36 import { WebSocketService } from "../services";
37 import {
38   amAdmin,
39   amMod,
40   auth,
41   choicesConfig,
42   debounce,
43   fetchLimit,
44   fetchUsers,
45   isBrowser,
46   setIsoData,
47   toast,
48   wsClient,
49   wsSubscribe,
50 } from "../utils";
51 import { HtmlTags } from "./common/html-tags";
52 import { Spinner } from "./common/icon";
53 import { MomentTime } from "./common/moment-time";
54 import { Paginator } from "./common/paginator";
55 import { CommunityLink } from "./community/community-link";
56 import { PersonListing } from "./person/person-listing";
57 type ModlogType = {
58   id: number;
59   type_: ModlogActionType;
60   moderator: Option<PersonSafe>;
61   view:
62     | ModRemovePostView
63     | ModLockPostView
64     | ModStickyPostView
65     | ModRemoveCommentView
66     | ModRemoveCommunityView
67     | ModBanFromCommunityView
68     | ModBanView
69     | ModAddCommunityView
70     | ModTransferCommunityView
71     | ModAddView
72     | AdminPurgePersonView
73     | AdminPurgeCommunityView
74     | AdminPurgePostView
75     | AdminPurgeCommentView;
76   when_: string;
77 };
78 var Choices: any;
79 if (isBrowser()) {
80   Choices = require("choices.js");
81 }
82
83 interface ModlogState {
84   res: Option<GetModlogResponse>;
85   communityId: Option<number>;
86   communityMods: Option<CommunityModeratorView[]>;
87   communityName: Option<string>;
88   page: number;
89   siteRes: GetSiteResponse;
90   loading: boolean;
91   filter_action: ModlogActionType;
92   filter_user: Option<number>;
93   filter_mod: Option<number>;
94 }
95
96 export class Modlog extends Component<any, ModlogState> {
97   private isoData = setIsoData(
98     this.context,
99     GetModlogResponse,
100     GetCommunityResponse
101   );
102   private subscription: Subscription;
103   private userChoices: any;
104   private modChoices: any;
105   private emptyState: ModlogState = {
106     res: None,
107     communityId: None,
108     communityMods: None,
109     communityName: None,
110     page: 1,
111     loading: true,
112     siteRes: this.isoData.site_res,
113     filter_action: ModlogActionType.All,
114     filter_user: None,
115     filter_mod: None,
116   };
117
118   constructor(props: any, context: any) {
119     super(props, context);
120     this.state = this.emptyState;
121     this.handlePageChange = this.handlePageChange.bind(this);
122
123     this.parseMessage = this.parseMessage.bind(this);
124     this.subscription = wsSubscribe(this.parseMessage);
125
126     this.state = {
127       ...this.state,
128       communityId: this.props.match.params.community_id
129         ? Some(Number(this.props.match.params.community_id))
130         : None,
131     };
132
133     // Only fetch the data if coming from another route
134     if (this.isoData.path == this.context.router.route.match.url) {
135       this.state = {
136         ...this.state,
137         res: Some(this.isoData.routeData[0] as GetModlogResponse),
138       };
139
140       if (this.isoData.routeData[1]) {
141         // Getting the moderators
142         let communityRes = Some(
143           this.isoData.routeData[1] as GetCommunityResponse
144         );
145         this.state = {
146           ...this.state,
147           communityMods: communityRes.map(c => c.moderators),
148         };
149       }
150
151       this.state = { ...this.state, loading: false };
152     } else {
153       this.refetch();
154     }
155   }
156
157   componentDidMount() {
158     this.setupUserFilter();
159     this.setupModFilter();
160   }
161
162   componentWillUnmount() {
163     if (isBrowser()) {
164       this.subscription.unsubscribe();
165     }
166   }
167
168   buildCombined(res: GetModlogResponse): ModlogType[] {
169     let removed_posts: ModlogType[] = res.removed_posts.map(r => ({
170       id: r.mod_remove_post.id,
171       type_: ModlogActionType.ModRemovePost,
172       view: r,
173       moderator: r.moderator,
174       when_: r.mod_remove_post.when_,
175     }));
176
177     let locked_posts: ModlogType[] = res.locked_posts.map(r => ({
178       id: r.mod_lock_post.id,
179       type_: ModlogActionType.ModLockPost,
180       view: r,
181       moderator: r.moderator,
182       when_: r.mod_lock_post.when_,
183     }));
184
185     let stickied_posts: ModlogType[] = res.stickied_posts.map(r => ({
186       id: r.mod_sticky_post.id,
187       type_: ModlogActionType.ModStickyPost,
188       view: r,
189       moderator: r.moderator,
190       when_: r.mod_sticky_post.when_,
191     }));
192
193     let removed_comments: ModlogType[] = res.removed_comments.map(r => ({
194       id: r.mod_remove_comment.id,
195       type_: ModlogActionType.ModRemoveComment,
196       view: r,
197       moderator: r.moderator,
198       when_: r.mod_remove_comment.when_,
199     }));
200
201     let removed_communities: ModlogType[] = res.removed_communities.map(r => ({
202       id: r.mod_remove_community.id,
203       type_: ModlogActionType.ModRemoveCommunity,
204       view: r,
205       moderator: r.moderator,
206       when_: r.mod_remove_community.when_,
207     }));
208
209     let banned_from_community: ModlogType[] = res.banned_from_community.map(
210       r => ({
211         id: r.mod_ban_from_community.id,
212         type_: ModlogActionType.ModBanFromCommunity,
213         view: r,
214         moderator: r.moderator,
215         when_: r.mod_ban_from_community.when_,
216       })
217     );
218
219     let added_to_community: ModlogType[] = res.added_to_community.map(r => ({
220       id: r.mod_add_community.id,
221       type_: ModlogActionType.ModAddCommunity,
222       view: r,
223       moderator: r.moderator,
224       when_: r.mod_add_community.when_,
225     }));
226
227     let transferred_to_community: ModlogType[] =
228       res.transferred_to_community.map(r => ({
229         id: r.mod_transfer_community.id,
230         type_: ModlogActionType.ModTransferCommunity,
231         view: r,
232         moderator: r.moderator,
233         when_: r.mod_transfer_community.when_,
234       }));
235
236     let added: ModlogType[] = res.added.map(r => ({
237       id: r.mod_add.id,
238       type_: ModlogActionType.ModAdd,
239       view: r,
240       moderator: r.moderator,
241       when_: r.mod_add.when_,
242     }));
243
244     let banned: ModlogType[] = res.banned.map(r => ({
245       id: r.mod_ban.id,
246       type_: ModlogActionType.ModBan,
247       view: r,
248       moderator: r.moderator,
249       when_: r.mod_ban.when_,
250     }));
251
252     let purged_persons: ModlogType[] = res.admin_purged_persons.map(r => ({
253       id: r.admin_purge_person.id,
254       type_: ModlogActionType.AdminPurgePerson,
255       view: r,
256       moderator: r.admin,
257       when_: r.admin_purge_person.when_,
258     }));
259
260     let purged_communities: ModlogType[] = res.admin_purged_communities.map(
261       r => ({
262         id: r.admin_purge_community.id,
263         type_: ModlogActionType.AdminPurgeCommunity,
264         view: r,
265         moderator: r.admin,
266         when_: r.admin_purge_community.when_,
267       })
268     );
269
270     let purged_posts: ModlogType[] = res.admin_purged_posts.map(r => ({
271       id: r.admin_purge_post.id,
272       type_: ModlogActionType.AdminPurgePost,
273       view: r,
274       moderator: r.admin,
275       when_: r.admin_purge_post.when_,
276     }));
277
278     let purged_comments: ModlogType[] = res.admin_purged_comments.map(r => ({
279       id: r.admin_purge_comment.id,
280       type_: ModlogActionType.AdminPurgeComment,
281       view: r,
282       moderator: r.admin,
283       when_: r.admin_purge_comment.when_,
284     }));
285
286     let combined: ModlogType[] = [];
287
288     combined.push(...removed_posts);
289     combined.push(...locked_posts);
290     combined.push(...stickied_posts);
291     combined.push(...removed_comments);
292     combined.push(...removed_communities);
293     combined.push(...banned_from_community);
294     combined.push(...added_to_community);
295     combined.push(...transferred_to_community);
296     combined.push(...added);
297     combined.push(...banned);
298     combined.push(...purged_persons);
299     combined.push(...purged_communities);
300     combined.push(...purged_posts);
301     combined.push(...purged_comments);
302
303     // Sort them by time
304     combined.sort((a, b) => b.when_.localeCompare(a.when_));
305
306     return combined;
307   }
308
309   renderModlogType(i: ModlogType) {
310     switch (i.type_) {
311       case ModlogActionType.ModRemovePost: {
312         let mrpv = i.view as ModRemovePostView;
313         return (
314           <>
315             <span>
316               {mrpv.mod_remove_post.removed.unwrapOr(false)
317                 ? "Removed "
318                 : "Restored "}
319             </span>
320             <span>
321               Post <Link to={`/post/${mrpv.post.id}`}>{mrpv.post.name}</Link>
322             </span>
323             <span>
324               {mrpv.mod_remove_post.reason.match({
325                 some: reason => <div>reason: {reason}</div>,
326                 none: <></>,
327               })}
328             </span>
329           </>
330         );
331       }
332       case ModlogActionType.ModLockPost: {
333         let mlpv = i.view as ModLockPostView;
334         return (
335           <>
336             <span>
337               {mlpv.mod_lock_post.locked.unwrapOr(false)
338                 ? "Locked "
339                 : "Unlocked "}
340             </span>
341             <span>
342               Post <Link to={`/post/${mlpv.post.id}`}>{mlpv.post.name}</Link>
343             </span>
344           </>
345         );
346       }
347       case ModlogActionType.ModStickyPost: {
348         let mspv = i.view as ModStickyPostView;
349         return (
350           <>
351             <span>
352               {mspv.mod_sticky_post.stickied.unwrapOr(false)
353                 ? "Stickied "
354                 : "Unstickied "}
355             </span>
356             <span>
357               Post <Link to={`/post/${mspv.post.id}`}>{mspv.post.name}</Link>
358             </span>
359           </>
360         );
361       }
362       case ModlogActionType.ModRemoveComment: {
363         let mrc = i.view as ModRemoveCommentView;
364         return (
365           <>
366             <span>
367               {mrc.mod_remove_comment.removed.unwrapOr(false)
368                 ? "Removed "
369                 : "Restored "}
370             </span>
371             <span>
372               Comment{" "}
373               <Link to={`/post/${mrc.post.id}/comment/${mrc.comment.id}`}>
374                 {mrc.comment.content}
375               </Link>
376             </span>
377             <span>
378               {" "}
379               by <PersonListing person={mrc.commenter} />
380             </span>
381             <span>
382               {mrc.mod_remove_comment.reason.match({
383                 some: reason => <div>reason: {reason}</div>,
384                 none: <></>,
385               })}
386             </span>
387           </>
388         );
389       }
390       case ModlogActionType.ModRemoveCommunity: {
391         let mrco = i.view as ModRemoveCommunityView;
392         return (
393           <>
394             <span>
395               {mrco.mod_remove_community.removed.unwrapOr(false)
396                 ? "Removed "
397                 : "Restored "}
398             </span>
399             <span>
400               Community <CommunityLink community={mrco.community} />
401             </span>
402             <span>
403               {mrco.mod_remove_community.reason.match({
404                 some: reason => <div>reason: {reason}</div>,
405                 none: <></>,
406               })}
407             </span>
408             <span>
409               {mrco.mod_remove_community.expires.match({
410                 some: expires => (
411                   <div>expires: {moment.utc(expires).fromNow()}</div>
412                 ),
413                 none: <></>,
414               })}
415             </span>
416           </>
417         );
418       }
419       case ModlogActionType.ModBanFromCommunity: {
420         let mbfc = i.view as ModBanFromCommunityView;
421         return (
422           <>
423             <span>
424               {mbfc.mod_ban_from_community.banned.unwrapOr(false)
425                 ? "Banned "
426                 : "Unbanned "}{" "}
427             </span>
428             <span>
429               <PersonListing person={mbfc.banned_person} />
430             </span>
431             <span> from the community </span>
432             <span>
433               <CommunityLink community={mbfc.community} />
434             </span>
435             <span>
436               {mbfc.mod_ban_from_community.reason.match({
437                 some: reason => <div>reason: {reason}</div>,
438                 none: <></>,
439               })}
440             </span>
441             <span>
442               {mbfc.mod_ban_from_community.expires.match({
443                 some: expires => (
444                   <div>expires: {moment.utc(expires).fromNow()}</div>
445                 ),
446                 none: <></>,
447               })}
448             </span>
449           </>
450         );
451       }
452       case ModlogActionType.ModAddCommunity: {
453         let mac = i.view as ModAddCommunityView;
454         return (
455           <>
456             <span>
457               {mac.mod_add_community.removed.unwrapOr(false)
458                 ? "Removed "
459                 : "Appointed "}{" "}
460             </span>
461             <span>
462               <PersonListing person={mac.modded_person} />
463             </span>
464             <span> as a mod to the community </span>
465             <span>
466               <CommunityLink community={mac.community} />
467             </span>
468           </>
469         );
470       }
471       case ModlogActionType.ModTransferCommunity: {
472         let mtc = i.view as ModTransferCommunityView;
473         return (
474           <>
475             <span>
476               {mtc.mod_transfer_community.removed.unwrapOr(false)
477                 ? "Removed "
478                 : "Transferred "}{" "}
479             </span>
480             <span>
481               <CommunityLink community={mtc.community} />
482             </span>
483             <span> to </span>
484             <span>
485               <PersonListing person={mtc.modded_person} />
486             </span>
487           </>
488         );
489       }
490       case ModlogActionType.ModBan: {
491         let mb = i.view as ModBanView;
492         return (
493           <>
494             <span>
495               {mb.mod_ban.banned.unwrapOr(false) ? "Banned " : "Unbanned "}{" "}
496             </span>
497             <span>
498               <PersonListing person={mb.banned_person} />
499             </span>
500             <span>
501               {mb.mod_ban.reason.match({
502                 some: reason => <div>reason: {reason}</div>,
503                 none: <></>,
504               })}
505             </span>
506             <span>
507               {mb.mod_ban.expires.match({
508                 some: expires => (
509                   <div>expires: {moment.utc(expires).fromNow()}</div>
510                 ),
511                 none: <></>,
512               })}
513             </span>
514           </>
515         );
516       }
517       case ModlogActionType.ModAdd: {
518         let ma = i.view as ModAddView;
519         return (
520           <>
521             <span>
522               {ma.mod_add.removed.unwrapOr(false) ? "Removed " : "Appointed "}{" "}
523             </span>
524             <span>
525               <PersonListing person={ma.modded_person} />
526             </span>
527             <span> as an admin </span>
528           </>
529         );
530       }
531       case ModlogActionType.AdminPurgePerson: {
532         let ap = i.view as AdminPurgePersonView;
533         return (
534           <>
535             <span>Purged a Person</span>
536             <span>
537               {ap.admin_purge_person.reason.match({
538                 some: reason => <div>reason: {reason}</div>,
539                 none: <></>,
540               })}
541             </span>
542           </>
543         );
544       }
545       case ModlogActionType.AdminPurgeCommunity: {
546         let ap = i.view as AdminPurgeCommunityView;
547         return (
548           <>
549             <span>Purged a Community</span>
550             <span>
551               {ap.admin_purge_community.reason.match({
552                 some: reason => <div>reason: {reason}</div>,
553                 none: <></>,
554               })}
555             </span>
556           </>
557         );
558       }
559       case ModlogActionType.AdminPurgePost: {
560         let ap = i.view as AdminPurgePostView;
561         return (
562           <>
563             <span>Purged a Post from from </span>
564             <CommunityLink community={ap.community} />
565             <span>
566               {ap.admin_purge_post.reason.match({
567                 some: reason => <div>reason: {reason}</div>,
568                 none: <></>,
569               })}
570             </span>
571           </>
572         );
573       }
574       case ModlogActionType.AdminPurgeComment: {
575         let ap = i.view as AdminPurgeCommentView;
576         return (
577           <>
578             <span>
579               Purged a Comment from{" "}
580               <Link to={`/post/${ap.post.id}`}>{ap.post.name}</Link>
581             </span>
582             <span>
583               {ap.admin_purge_comment.reason.match({
584                 some: reason => <div>reason: {reason}</div>,
585                 none: <></>,
586               })}
587             </span>
588           </>
589         );
590       }
591       default:
592         return <div />;
593     }
594   }
595
596   combined() {
597     let combined = this.state.res.map(this.buildCombined).unwrapOr([]);
598
599     return (
600       <tbody>
601         {combined.map(i => (
602           <tr key={i.id}>
603             <td>
604               <MomentTime published={i.when_} updated={None} />
605             </td>
606             <td>
607               {this.amAdminOrMod ? (
608                 <PersonListing person={i.moderator.unwrap()} />
609               ) : (
610                 <div>{this.modOrAdminText(i.moderator)}</div>
611               )}
612             </td>
613             <td>{this.renderModlogType(i)}</td>
614           </tr>
615         ))}
616       </tbody>
617     );
618   }
619
620   get amAdminOrMod(): boolean {
621     return amAdmin() || amMod(this.state.communityMods);
622   }
623
624   modOrAdminText(person: Option<PersonSafe>): string {
625     return person.match({
626       some: res =>
627         this.isoData.site_res.admins.map(a => a.person.id).includes(res.id)
628           ? i18n.t("admin")
629           : i18n.t("mod"),
630       none: i18n.t("mod"),
631     });
632   }
633
634   get documentTitle(): string {
635     return `Modlog - ${this.state.siteRes.site_view.site.name}`;
636   }
637
638   render() {
639     return (
640       <div className="container-lg">
641         <HtmlTags
642           title={this.documentTitle}
643           path={this.context.router.route.match.url}
644           description={None}
645           image={None}
646         />
647         {this.state.loading ? (
648           <h5>
649             <Spinner large />
650           </h5>
651         ) : (
652           <div>
653             <h5>
654               {this.state.communityName.match({
655                 some: name => (
656                   <Link className="text-body" to={`/c/${name}`}>
657                     /c/{name}{" "}
658                   </Link>
659                 ),
660                 none: <></>,
661               })}
662               <span>{i18n.t("modlog")}</span>
663             </h5>
664             <div className="form-row">
665               <div className="form-group col-sm-6">
666                 <select
667                   value={this.state.filter_action}
668                   onChange={linkEvent(this, this.handleFilterActionChange)}
669                   className="custom-select mb-2"
670                   aria-label="action"
671                 >
672                   <option disabled aria-hidden="true">
673                     {i18n.t("filter_by_action")}
674                   </option>
675                   <option value={ModlogActionType.All}>{i18n.t("all")}</option>
676                   <option value={ModlogActionType.ModRemovePost}>
677                     Removing Posts
678                   </option>
679                   <option value={ModlogActionType.ModLockPost}>
680                     Locking Posts
681                   </option>
682                   <option value={ModlogActionType.ModStickyPost}>
683                     Stickying Posts
684                   </option>
685                   <option value={ModlogActionType.ModRemoveComment}>
686                     Removing Comments
687                   </option>
688                   <option value={ModlogActionType.ModRemoveCommunity}>
689                     Removing Communities
690                   </option>
691                   <option value={ModlogActionType.ModBanFromCommunity}>
692                     Banning From Communities
693                   </option>
694                   <option value={ModlogActionType.ModAddCommunity}>
695                     Adding Mod to Community
696                   </option>
697                   <option value={ModlogActionType.ModTransferCommunity}>
698                     Transfering Communities
699                   </option>
700                   <option value={ModlogActionType.ModAdd}>
701                     Adding Mod to Site
702                   </option>
703                   <option value={ModlogActionType.ModBan}>
704                     Banning From Site
705                   </option>
706                 </select>
707               </div>
708               {!this.state.siteRes.site_view.local_site
709                 .hide_modlog_mod_names && (
710                 <div className="form-group col-sm-6">
711                   <select
712                     id="filter-mod"
713                     className="form-control"
714                     value={toUndefined(this.state.filter_mod)}
715                   >
716                     <option>{i18n.t("filter_by_mod")}</option>
717                   </select>
718                 </div>
719               )}
720               <div className="form-group col-sm-6">
721                 <select
722                   id="filter-user"
723                   className="form-control"
724                   value={toUndefined(this.state.filter_user)}
725                 >
726                   <option>{i18n.t("filter_by_user")}</option>
727                 </select>
728               </div>
729             </div>
730             <div className="table-responsive">
731               <table id="modlog_table" className="table table-sm table-hover">
732                 <thead className="pointer">
733                   <tr>
734                     <th> {i18n.t("time")}</th>
735                     <th>{i18n.t("mod")}</th>
736                     <th>{i18n.t("action")}</th>
737                   </tr>
738                 </thead>
739                 {this.combined()}
740               </table>
741               <Paginator
742                 page={this.state.page}
743                 onChange={this.handlePageChange}
744               />
745             </div>
746           </div>
747         )}
748       </div>
749     );
750   }
751
752   handleFilterActionChange(i: Modlog, event: any) {
753     i.setState({ filter_action: event.target.value });
754     i.refetch();
755   }
756
757   handlePageChange(val: number) {
758     this.setState({ page: val });
759     this.refetch();
760   }
761
762   refetch() {
763     let modlogForm = new GetModlog({
764       community_id: this.state.communityId,
765       page: Some(this.state.page),
766       limit: Some(fetchLimit),
767       auth: auth(false).ok(),
768       type_: this.state.filter_action,
769       other_person_id: this.state.filter_user,
770       mod_person_id: this.state.filter_mod,
771     });
772     WebSocketService.Instance.send(wsClient.getModlog(modlogForm));
773
774     this.state.communityId.match({
775       some: id => {
776         let communityForm = new GetCommunity({
777           id: Some(id),
778           name: None,
779           auth: auth(false).ok(),
780         });
781         WebSocketService.Instance.send(wsClient.getCommunity(communityForm));
782       },
783       none: void 0,
784     });
785   }
786
787   setupUserFilter() {
788     if (isBrowser()) {
789       let selectId: any = document.getElementById("filter-user");
790       if (selectId) {
791         this.userChoices = new Choices(selectId, choicesConfig);
792         this.userChoices.passedElement.element.addEventListener(
793           "choice",
794           (e: any) => {
795             this.setState({ filter_user: Some(Number(e.detail.choice.value)) });
796             this.refetch();
797           },
798           false
799         );
800         this.userChoices.passedElement.element.addEventListener(
801           "search",
802           debounce(async (e: any) => {
803             try {
804               let users = (await fetchUsers(e.detail.value)).users;
805               this.userChoices.setChoices(
806                 users.map(u => {
807                   return {
808                     value: u.person.id.toString(),
809                     label: u.person.name,
810                   };
811                 }),
812                 "value",
813                 "label",
814                 true
815               );
816             } catch (err) {
817               console.log(err);
818             }
819           }),
820           false
821         );
822       }
823     }
824   }
825
826   setupModFilter() {
827     if (isBrowser()) {
828       let selectId: any = document.getElementById("filter-mod");
829       if (selectId) {
830         this.modChoices = new Choices(selectId, choicesConfig);
831         this.modChoices.passedElement.element.addEventListener(
832           "choice",
833           (e: any) => {
834             this.setState({ filter_mod: Some(Number(e.detail.choice.value)) });
835             this.refetch();
836           },
837           false
838         );
839         this.modChoices.passedElement.element.addEventListener(
840           "search",
841           debounce(async (e: any) => {
842             try {
843               let mods = (await fetchUsers(e.detail.value)).users;
844               this.modChoices.setChoices(
845                 mods.map(u => {
846                   return {
847                     value: u.person.id.toString(),
848                     label: u.person.name,
849                   };
850                 }),
851                 "value",
852                 "label",
853                 true
854               );
855             } catch (err) {
856               console.log(err);
857             }
858           }),
859           false
860         );
861       }
862     }
863   }
864
865   static fetchInitialData(req: InitialFetchRequest): Promise<any>[] {
866     let pathSplit = req.path.split("/");
867     let communityId = Some(pathSplit[3]).map(Number);
868     let promises: Promise<any>[] = [];
869
870     let modlogForm = new GetModlog({
871       page: Some(1),
872       limit: Some(fetchLimit),
873       community_id: communityId,
874       mod_person_id: None,
875       auth: req.auth,
876       type_: ModlogActionType.All,
877       other_person_id: None,
878     });
879
880     promises.push(req.client.getModlog(modlogForm));
881
882     if (communityId.isSome()) {
883       let communityForm = new GetCommunity({
884         id: communityId,
885         name: None,
886         auth: req.auth,
887       });
888       promises.push(req.client.getCommunity(communityForm));
889     } else {
890       promises.push(Promise.resolve());
891     }
892     return promises;
893   }
894
895   parseMessage(msg: any) {
896     let op = wsUserOp(msg);
897     console.log(msg);
898     if (msg.error) {
899       toast(i18n.t(msg.error), "danger");
900       return;
901     } else if (op == UserOperation.GetModlog) {
902       let data = wsJsonToRes<GetModlogResponse>(msg, GetModlogResponse);
903       window.scrollTo(0, 0);
904       this.setState({ res: Some(data), loading: false });
905       this.setupUserFilter();
906       this.setupModFilter();
907     } else if (op == UserOperation.GetCommunity) {
908       let data = wsJsonToRes<GetCommunityResponse>(msg, GetCommunityResponse);
909       this.setState({
910         communityMods: Some(data.moderators),
911         communityName: Some(data.community_view.community.name),
912       });
913     }
914   }
915 }