]> Untitled Git - lemmy-ui.git/blob - src/shared/components/modlog.tsx
Add FeaturedPost Support (#873)
[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   ModFeaturePostView,
20   ModLockPostView,
21   ModlogActionType,
22   ModRemoveCommentView,
23   ModRemoveCommunityView,
24   ModRemovePostView,
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     | ModFeaturePostView
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 featured_posts: ModlogType[] = res.featured_posts.map(r => ({
186       id: r.mod_feature_post.id,
187       type_: ModlogActionType.ModFeaturePost,
188       view: r,
189       moderator: r.moderator,
190       when_: r.mod_feature_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(...featured_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.ModFeaturePost: {
348         let mspv = i.view as ModFeaturePostView;
349         return (
350           <>
351             <span>
352               {mspv.mod_feature_post.featured ? "Featured " : "Unfeatured "}
353             </span>
354             <span>
355               Post <Link to={`/post/${mspv.post.id}`}>{mspv.post.name}</Link>
356             </span>
357             <span>
358               {mspv.mod_feature_post.is_featured_community
359                 ? " In Community"
360                 : " In Local"}
361             </span>
362           </>
363         );
364       }
365       case ModlogActionType.ModRemoveComment: {
366         let mrc = i.view as ModRemoveCommentView;
367         return (
368           <>
369             <span>
370               {mrc.mod_remove_comment.removed.unwrapOr(false)
371                 ? "Removed "
372                 : "Restored "}
373             </span>
374             <span>
375               Comment{" "}
376               <Link to={`/post/${mrc.post.id}/comment/${mrc.comment.id}`}>
377                 {mrc.comment.content}
378               </Link>
379             </span>
380             <span>
381               {" "}
382               by <PersonListing person={mrc.commenter} />
383             </span>
384             <span>
385               {mrc.mod_remove_comment.reason.match({
386                 some: reason => <div>reason: {reason}</div>,
387                 none: <></>,
388               })}
389             </span>
390           </>
391         );
392       }
393       case ModlogActionType.ModRemoveCommunity: {
394         let mrco = i.view as ModRemoveCommunityView;
395         return (
396           <>
397             <span>
398               {mrco.mod_remove_community.removed.unwrapOr(false)
399                 ? "Removed "
400                 : "Restored "}
401             </span>
402             <span>
403               Community <CommunityLink community={mrco.community} />
404             </span>
405             <span>
406               {mrco.mod_remove_community.reason.match({
407                 some: reason => <div>reason: {reason}</div>,
408                 none: <></>,
409               })}
410             </span>
411             <span>
412               {mrco.mod_remove_community.expires.match({
413                 some: expires => (
414                   <div>expires: {moment.utc(expires).fromNow()}</div>
415                 ),
416                 none: <></>,
417               })}
418             </span>
419           </>
420         );
421       }
422       case ModlogActionType.ModBanFromCommunity: {
423         let mbfc = i.view as ModBanFromCommunityView;
424         return (
425           <>
426             <span>
427               {mbfc.mod_ban_from_community.banned.unwrapOr(false)
428                 ? "Banned "
429                 : "Unbanned "}{" "}
430             </span>
431             <span>
432               <PersonListing person={mbfc.banned_person} />
433             </span>
434             <span> from the community </span>
435             <span>
436               <CommunityLink community={mbfc.community} />
437             </span>
438             <span>
439               {mbfc.mod_ban_from_community.reason.match({
440                 some: reason => <div>reason: {reason}</div>,
441                 none: <></>,
442               })}
443             </span>
444             <span>
445               {mbfc.mod_ban_from_community.expires.match({
446                 some: expires => (
447                   <div>expires: {moment.utc(expires).fromNow()}</div>
448                 ),
449                 none: <></>,
450               })}
451             </span>
452           </>
453         );
454       }
455       case ModlogActionType.ModAddCommunity: {
456         let mac = i.view as ModAddCommunityView;
457         return (
458           <>
459             <span>
460               {mac.mod_add_community.removed.unwrapOr(false)
461                 ? "Removed "
462                 : "Appointed "}{" "}
463             </span>
464             <span>
465               <PersonListing person={mac.modded_person} />
466             </span>
467             <span> as a mod to the community </span>
468             <span>
469               <CommunityLink community={mac.community} />
470             </span>
471           </>
472         );
473       }
474       case ModlogActionType.ModTransferCommunity: {
475         let mtc = i.view as ModTransferCommunityView;
476         return (
477           <>
478             <span>
479               {mtc.mod_transfer_community.removed.unwrapOr(false)
480                 ? "Removed "
481                 : "Transferred "}{" "}
482             </span>
483             <span>
484               <CommunityLink community={mtc.community} />
485             </span>
486             <span> to </span>
487             <span>
488               <PersonListing person={mtc.modded_person} />
489             </span>
490           </>
491         );
492       }
493       case ModlogActionType.ModBan: {
494         let mb = i.view as ModBanView;
495         return (
496           <>
497             <span>
498               {mb.mod_ban.banned.unwrapOr(false) ? "Banned " : "Unbanned "}{" "}
499             </span>
500             <span>
501               <PersonListing person={mb.banned_person} />
502             </span>
503             <span>
504               {mb.mod_ban.reason.match({
505                 some: reason => <div>reason: {reason}</div>,
506                 none: <></>,
507               })}
508             </span>
509             <span>
510               {mb.mod_ban.expires.match({
511                 some: expires => (
512                   <div>expires: {moment.utc(expires).fromNow()}</div>
513                 ),
514                 none: <></>,
515               })}
516             </span>
517           </>
518         );
519       }
520       case ModlogActionType.ModAdd: {
521         let ma = i.view as ModAddView;
522         return (
523           <>
524             <span>
525               {ma.mod_add.removed.unwrapOr(false) ? "Removed " : "Appointed "}{" "}
526             </span>
527             <span>
528               <PersonListing person={ma.modded_person} />
529             </span>
530             <span> as an admin </span>
531           </>
532         );
533       }
534       case ModlogActionType.AdminPurgePerson: {
535         let ap = i.view as AdminPurgePersonView;
536         return (
537           <>
538             <span>Purged a Person</span>
539             <span>
540               {ap.admin_purge_person.reason.match({
541                 some: reason => <div>reason: {reason}</div>,
542                 none: <></>,
543               })}
544             </span>
545           </>
546         );
547       }
548       case ModlogActionType.AdminPurgeCommunity: {
549         let ap = i.view as AdminPurgeCommunityView;
550         return (
551           <>
552             <span>Purged a Community</span>
553             <span>
554               {ap.admin_purge_community.reason.match({
555                 some: reason => <div>reason: {reason}</div>,
556                 none: <></>,
557               })}
558             </span>
559           </>
560         );
561       }
562       case ModlogActionType.AdminPurgePost: {
563         let ap = i.view as AdminPurgePostView;
564         return (
565           <>
566             <span>Purged a Post from from </span>
567             <CommunityLink community={ap.community} />
568             <span>
569               {ap.admin_purge_post.reason.match({
570                 some: reason => <div>reason: {reason}</div>,
571                 none: <></>,
572               })}
573             </span>
574           </>
575         );
576       }
577       case ModlogActionType.AdminPurgeComment: {
578         let ap = i.view as AdminPurgeCommentView;
579         return (
580           <>
581             <span>
582               Purged a Comment from{" "}
583               <Link to={`/post/${ap.post.id}`}>{ap.post.name}</Link>
584             </span>
585             <span>
586               {ap.admin_purge_comment.reason.match({
587                 some: reason => <div>reason: {reason}</div>,
588                 none: <></>,
589               })}
590             </span>
591           </>
592         );
593       }
594       default:
595         return <div />;
596     }
597   }
598
599   combined() {
600     let combined = this.state.res.map(this.buildCombined).unwrapOr([]);
601
602     return (
603       <tbody>
604         {combined.map(i => (
605           <tr key={i.id}>
606             <td>
607               <MomentTime published={i.when_} updated={None} />
608             </td>
609             <td>
610               {this.amAdminOrMod ? (
611                 <PersonListing person={i.moderator.unwrap()} />
612               ) : (
613                 <div>{this.modOrAdminText(i.moderator)}</div>
614               )}
615             </td>
616             <td>{this.renderModlogType(i)}</td>
617           </tr>
618         ))}
619       </tbody>
620     );
621   }
622
623   get amAdminOrMod(): boolean {
624     return amAdmin() || amMod(this.state.communityMods);
625   }
626
627   modOrAdminText(person: Option<PersonSafe>): string {
628     return person.match({
629       some: res =>
630         this.isoData.site_res.admins.map(a => a.person.id).includes(res.id)
631           ? i18n.t("admin")
632           : i18n.t("mod"),
633       none: i18n.t("mod"),
634     });
635   }
636
637   get documentTitle(): string {
638     return `Modlog - ${this.state.siteRes.site_view.site.name}`;
639   }
640
641   render() {
642     return (
643       <div className="container-lg">
644         <HtmlTags
645           title={this.documentTitle}
646           path={this.context.router.route.match.url}
647           description={None}
648           image={None}
649         />
650         {this.state.loading ? (
651           <h5>
652             <Spinner large />
653           </h5>
654         ) : (
655           <div>
656             <h5>
657               {this.state.communityName.match({
658                 some: name => (
659                   <Link className="text-body" to={`/c/${name}`}>
660                     /c/{name}{" "}
661                   </Link>
662                 ),
663                 none: <></>,
664               })}
665               <span>{i18n.t("modlog")}</span>
666             </h5>
667             <div className="form-row">
668               <div className="form-group col-sm-6">
669                 <select
670                   value={this.state.filter_action}
671                   onChange={linkEvent(this, this.handleFilterActionChange)}
672                   className="custom-select mb-2"
673                   aria-label="action"
674                 >
675                   <option disabled aria-hidden="true">
676                     {i18n.t("filter_by_action")}
677                   </option>
678                   <option value={ModlogActionType.All}>{i18n.t("all")}</option>
679                   <option value={ModlogActionType.ModRemovePost}>
680                     Removing Posts
681                   </option>
682                   <option value={ModlogActionType.ModLockPost}>
683                     Locking Posts
684                   </option>
685                   <option value={ModlogActionType.ModFeaturePost}>
686                     Featuring Posts
687                   </option>
688                   <option value={ModlogActionType.ModRemoveComment}>
689                     Removing Comments
690                   </option>
691                   <option value={ModlogActionType.ModRemoveCommunity}>
692                     Removing Communities
693                   </option>
694                   <option value={ModlogActionType.ModBanFromCommunity}>
695                     Banning From Communities
696                   </option>
697                   <option value={ModlogActionType.ModAddCommunity}>
698                     Adding Mod to Community
699                   </option>
700                   <option value={ModlogActionType.ModTransferCommunity}>
701                     Transfering Communities
702                   </option>
703                   <option value={ModlogActionType.ModAdd}>
704                     Adding Mod to Site
705                   </option>
706                   <option value={ModlogActionType.ModBan}>
707                     Banning From Site
708                   </option>
709                 </select>
710               </div>
711               {!this.state.siteRes.site_view.local_site
712                 .hide_modlog_mod_names && (
713                 <div className="form-group col-sm-6">
714                   <select
715                     id="filter-mod"
716                     className="form-control"
717                     value={toUndefined(this.state.filter_mod)}
718                   >
719                     <option>{i18n.t("filter_by_mod")}</option>
720                   </select>
721                 </div>
722               )}
723               <div className="form-group col-sm-6">
724                 <select
725                   id="filter-user"
726                   className="form-control"
727                   value={toUndefined(this.state.filter_user)}
728                 >
729                   <option>{i18n.t("filter_by_user")}</option>
730                 </select>
731               </div>
732             </div>
733             <div className="table-responsive">
734               <table id="modlog_table" className="table table-sm table-hover">
735                 <thead className="pointer">
736                   <tr>
737                     <th> {i18n.t("time")}</th>
738                     <th>{i18n.t("mod")}</th>
739                     <th>{i18n.t("action")}</th>
740                   </tr>
741                 </thead>
742                 {this.combined()}
743               </table>
744               <Paginator
745                 page={this.state.page}
746                 onChange={this.handlePageChange}
747               />
748             </div>
749           </div>
750         )}
751       </div>
752     );
753   }
754
755   handleFilterActionChange(i: Modlog, event: any) {
756     i.setState({ filter_action: event.target.value });
757     i.refetch();
758   }
759
760   handlePageChange(val: number) {
761     this.setState({ page: val });
762     this.refetch();
763   }
764
765   refetch() {
766     let modlogForm = new GetModlog({
767       community_id: this.state.communityId,
768       page: Some(this.state.page),
769       limit: Some(fetchLimit),
770       auth: auth(false).ok(),
771       type_: this.state.filter_action,
772       other_person_id: this.state.filter_user,
773       mod_person_id: this.state.filter_mod,
774     });
775     WebSocketService.Instance.send(wsClient.getModlog(modlogForm));
776
777     this.state.communityId.match({
778       some: id => {
779         let communityForm = new GetCommunity({
780           id: Some(id),
781           name: None,
782           auth: auth(false).ok(),
783         });
784         WebSocketService.Instance.send(wsClient.getCommunity(communityForm));
785       },
786       none: void 0,
787     });
788   }
789
790   setupUserFilter() {
791     if (isBrowser()) {
792       let selectId: any = document.getElementById("filter-user");
793       if (selectId) {
794         this.userChoices = new Choices(selectId, choicesConfig);
795         this.userChoices.passedElement.element.addEventListener(
796           "choice",
797           (e: any) => {
798             this.setState({ filter_user: Some(Number(e.detail.choice.value)) });
799             this.refetch();
800           },
801           false
802         );
803         this.userChoices.passedElement.element.addEventListener(
804           "search",
805           debounce(async (e: any) => {
806             try {
807               let users = (await fetchUsers(e.detail.value)).users;
808               this.userChoices.setChoices(
809                 users.map(u => {
810                   return {
811                     value: u.person.id.toString(),
812                     label: u.person.name,
813                   };
814                 }),
815                 "value",
816                 "label",
817                 true
818               );
819             } catch (err) {
820               console.log(err);
821             }
822           }),
823           false
824         );
825       }
826     }
827   }
828
829   setupModFilter() {
830     if (isBrowser()) {
831       let selectId: any = document.getElementById("filter-mod");
832       if (selectId) {
833         this.modChoices = new Choices(selectId, choicesConfig);
834         this.modChoices.passedElement.element.addEventListener(
835           "choice",
836           (e: any) => {
837             this.setState({ filter_mod: Some(Number(e.detail.choice.value)) });
838             this.refetch();
839           },
840           false
841         );
842         this.modChoices.passedElement.element.addEventListener(
843           "search",
844           debounce(async (e: any) => {
845             try {
846               let mods = (await fetchUsers(e.detail.value)).users;
847               this.modChoices.setChoices(
848                 mods.map(u => {
849                   return {
850                     value: u.person.id.toString(),
851                     label: u.person.name,
852                   };
853                 }),
854                 "value",
855                 "label",
856                 true
857               );
858             } catch (err) {
859               console.log(err);
860             }
861           }),
862           false
863         );
864       }
865     }
866   }
867
868   static fetchInitialData(req: InitialFetchRequest): Promise<any>[] {
869     let pathSplit = req.path.split("/");
870     let communityId = Some(pathSplit[3]).map(Number);
871     let promises: Promise<any>[] = [];
872
873     let modlogForm = new GetModlog({
874       page: Some(1),
875       limit: Some(fetchLimit),
876       community_id: communityId,
877       mod_person_id: None,
878       auth: req.auth,
879       type_: ModlogActionType.All,
880       other_person_id: None,
881     });
882
883     promises.push(req.client.getModlog(modlogForm));
884
885     if (communityId.isSome()) {
886       let communityForm = new GetCommunity({
887         id: communityId,
888         name: None,
889         auth: req.auth,
890       });
891       promises.push(req.client.getCommunity(communityForm));
892     } else {
893       promises.push(Promise.resolve());
894     }
895     return promises;
896   }
897
898   parseMessage(msg: any) {
899     let op = wsUserOp(msg);
900     console.log(msg);
901     if (msg.error) {
902       toast(i18n.t(msg.error), "danger");
903       return;
904     } else if (op == UserOperation.GetModlog) {
905       let data = wsJsonToRes<GetModlogResponse>(msg, GetModlogResponse);
906       window.scrollTo(0, 0);
907       this.setState({ res: Some(data), loading: false });
908       this.setupUserFilter();
909       this.setupModFilter();
910     } else if (op == UserOperation.GetCommunity) {
911       let data = wsJsonToRes<GetCommunityResponse>(msg, GetCommunityResponse);
912       this.setState({
913         communityMods: Some(data.moderators),
914         communityName: Some(data.community_view.community.name),
915       });
916     }
917   }
918 }