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