]> Untitled Git - lemmy-ui.git/blob - src/shared/components/modlog.tsx
Adding private message reporting. Fixes #782 (#806)
[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 this.state.siteRes.site_view.match({
636       some: siteView => `Modlog - ${siteView.site.name}`,
637       none: "",
638     });
639   }
640
641   render() {
642     return (
643       <div className="container">
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.ModStickyPost}>
686                     Stickying 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.match({
712                 some: site_view =>
713                   !site_view.site.hide_modlog_mod_names.unwrapOr(false) && (
714                     <div className="form-group col-sm-6">
715                       <select
716                         id="filter-mod"
717                         className="form-control"
718                         value={toUndefined(this.state.filter_mod)}
719                       >
720                         <option>{i18n.t("filter_by_mod")}</option>
721                       </select>
722                     </div>
723                   ),
724                 none: <></>,
725               })}
726               <div className="form-group col-sm-6">
727                 <select
728                   id="filter-user"
729                   className="form-control"
730                   value={toUndefined(this.state.filter_user)}
731                 >
732                   <option>{i18n.t("filter_by_user")}</option>
733                 </select>
734               </div>
735             </div>
736             <div className="table-responsive">
737               <table id="modlog_table" className="table table-sm table-hover">
738                 <thead className="pointer">
739                   <tr>
740                     <th> {i18n.t("time")}</th>
741                     <th>{i18n.t("mod")}</th>
742                     <th>{i18n.t("action")}</th>
743                   </tr>
744                 </thead>
745                 {this.combined()}
746               </table>
747               <Paginator
748                 page={this.state.page}
749                 onChange={this.handlePageChange}
750               />
751             </div>
752           </div>
753         )}
754       </div>
755     );
756   }
757
758   handleFilterActionChange(i: Modlog, event: any) {
759     i.setState({ filter_action: event.target.value });
760     i.refetch();
761   }
762
763   handlePageChange(val: number) {
764     this.setState({ page: val });
765     this.refetch();
766   }
767
768   refetch() {
769     let modlogForm = new GetModlog({
770       community_id: this.state.communityId,
771       page: Some(this.state.page),
772       limit: Some(fetchLimit),
773       auth: auth(false).ok(),
774       type_: this.state.filter_action,
775       other_person_id: this.state.filter_user,
776       mod_person_id: this.state.filter_mod,
777     });
778     WebSocketService.Instance.send(wsClient.getModlog(modlogForm));
779
780     this.state.communityId.match({
781       some: id => {
782         let communityForm = new GetCommunity({
783           id: Some(id),
784           name: None,
785           auth: auth(false).ok(),
786         });
787         WebSocketService.Instance.send(wsClient.getCommunity(communityForm));
788       },
789       none: void 0,
790     });
791   }
792
793   setupUserFilter() {
794     if (isBrowser()) {
795       let selectId: any = document.getElementById("filter-user");
796       if (selectId) {
797         this.userChoices = new Choices(selectId, choicesConfig);
798         this.userChoices.passedElement.element.addEventListener(
799           "choice",
800           (e: any) => {
801             this.setState({ filter_user: Some(Number(e.detail.choice.value)) });
802             this.refetch();
803           },
804           false
805         );
806         this.userChoices.passedElement.element.addEventListener(
807           "search",
808           debounce(async (e: any) => {
809             try {
810               let users = (await fetchUsers(e.detail.value)).users;
811               this.userChoices.setChoices(
812                 users.map(u => {
813                   return {
814                     value: u.person.id.toString(),
815                     label: u.person.name,
816                   };
817                 }),
818                 "value",
819                 "label",
820                 true
821               );
822             } catch (err) {
823               console.log(err);
824             }
825           }),
826           false
827         );
828       }
829     }
830   }
831
832   setupModFilter() {
833     if (isBrowser()) {
834       let selectId: any = document.getElementById("filter-mod");
835       if (selectId) {
836         this.modChoices = new Choices(selectId, choicesConfig);
837         this.modChoices.passedElement.element.addEventListener(
838           "choice",
839           (e: any) => {
840             this.setState({ filter_mod: Some(Number(e.detail.choice.value)) });
841             this.refetch();
842           },
843           false
844         );
845         this.modChoices.passedElement.element.addEventListener(
846           "search",
847           debounce(async (e: any) => {
848             try {
849               let mods = (await fetchUsers(e.detail.value)).users;
850               this.modChoices.setChoices(
851                 mods.map(u => {
852                   return {
853                     value: u.person.id.toString(),
854                     label: u.person.name,
855                   };
856                 }),
857                 "value",
858                 "label",
859                 true
860               );
861             } catch (err) {
862               console.log(err);
863             }
864           }),
865           false
866         );
867       }
868     }
869   }
870
871   static fetchInitialData(req: InitialFetchRequest): Promise<any>[] {
872     let pathSplit = req.path.split("/");
873     let communityId = Some(pathSplit[3]).map(Number);
874     let promises: Promise<any>[] = [];
875
876     let modlogForm = new GetModlog({
877       page: Some(1),
878       limit: Some(fetchLimit),
879       community_id: communityId,
880       mod_person_id: None,
881       auth: req.auth,
882       type_: ModlogActionType.All,
883       other_person_id: None,
884     });
885
886     promises.push(req.client.getModlog(modlogForm));
887
888     if (communityId.isSome()) {
889       let communityForm = new GetCommunity({
890         id: communityId,
891         name: None,
892         auth: req.auth,
893       });
894       promises.push(req.client.getCommunity(communityForm));
895     } else {
896       promises.push(Promise.resolve());
897     }
898     return promises;
899   }
900
901   parseMessage(msg: any) {
902     let op = wsUserOp(msg);
903     console.log(msg);
904     if (msg.error) {
905       toast(i18n.t(msg.error), "danger");
906       return;
907     } else if (op == UserOperation.GetModlog) {
908       let data = wsJsonToRes<GetModlogResponse>(msg, GetModlogResponse);
909       window.scrollTo(0, 0);
910       this.setState({ res: Some(data), loading: false });
911       this.setupUserFilter();
912       this.setupModFilter();
913     } else if (op == UserOperation.GetCommunity) {
914       let data = wsJsonToRes<GetCommunityResponse>(msg, GetCommunityResponse);
915       this.setState({
916         communityMods: Some(data.moderators),
917         communityName: Some(data.community_view.community.name),
918       });
919     }
920   }
921 }