1 import { None, Option, Some } from "@sniptt/monads";
2 import { Component } from "inferno";
3 import { Link } from "inferno-router";
6 AdminPurgeCommunityView,
9 CommunityModeratorView,
17 ModBanFromCommunityView,
21 ModRemoveCommunityView,
24 ModTransferCommunityView,
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";
46 import { HtmlTags } from "./common/html-tags";
47 import { Spinner } from "./common/icon";
48 import { MomentTime } from "./common/moment-time";
49 import { Paginator } from "./common/paginator";
50 import { CommunityLink } from "./community/community-link";
51 import { PersonListing } from "./person/person-listing";
73 moderator: PersonSafe;
78 | ModRemoveCommentView
79 | ModRemoveCommunityView
80 | ModBanFromCommunityView
83 | ModTransferCommunityView
85 | AdminPurgePersonView
86 | AdminPurgeCommunityView
88 | AdminPurgeCommentView;
92 interface ModlogState {
93 res: Option<GetModlogResponse>;
94 communityId: Option<number>;
95 communityMods: Option<CommunityModeratorView[]>;
97 siteRes: GetSiteResponse;
101 export class Modlog extends Component<any, ModlogState> {
102 private isoData = setIsoData(
107 private subscription: Subscription;
108 private emptyState: ModlogState = {
114 siteRes: this.isoData.site_res,
117 constructor(props: any, context: any) {
118 super(props, context);
120 this.state = this.emptyState;
121 this.handlePageChange = this.handlePageChange.bind(this);
123 this.state.communityId = this.props.match.params.community_id
124 ? Some(Number(this.props.match.params.community_id))
127 this.parseMessage = this.parseMessage.bind(this);
128 this.subscription = wsSubscribe(this.parseMessage);
130 // Only fetch the data if coming from another route
131 if (this.isoData.path == this.context.router.route.match.url) {
132 this.state.res = Some(this.isoData.routeData[0] as GetModlogResponse);
134 if (this.isoData.routeData[1]) {
135 // Getting the moderators
136 let communityRes = Some(
137 this.isoData.routeData[1] as GetCommunityResponse
139 this.state.communityMods = communityRes.map(c => c.moderators);
142 this.state.loading = false;
148 componentWillUnmount() {
150 this.subscription.unsubscribe();
154 buildCombined(res: GetModlogResponse): ModlogType[] {
155 let removed_posts: ModlogType[] = res.removed_posts.map(r => ({
156 id: r.mod_remove_post.id,
157 type_: ModlogEnum.ModRemovePost,
159 moderator: r.moderator,
160 when_: r.mod_remove_post.when_,
163 let locked_posts: ModlogType[] = res.locked_posts.map(r => ({
164 id: r.mod_lock_post.id,
165 type_: ModlogEnum.ModLockPost,
167 moderator: r.moderator,
168 when_: r.mod_lock_post.when_,
171 let stickied_posts: ModlogType[] = res.stickied_posts.map(r => ({
172 id: r.mod_sticky_post.id,
173 type_: ModlogEnum.ModStickyPost,
175 moderator: r.moderator,
176 when_: r.mod_sticky_post.when_,
179 let removed_comments: ModlogType[] = res.removed_comments.map(r => ({
180 id: r.mod_remove_comment.id,
181 type_: ModlogEnum.ModRemoveComment,
183 moderator: r.moderator,
184 when_: r.mod_remove_comment.when_,
187 let removed_communities: ModlogType[] = res.removed_communities.map(r => ({
188 id: r.mod_remove_community.id,
189 type_: ModlogEnum.ModRemoveCommunity,
191 moderator: r.moderator,
192 when_: r.mod_remove_community.when_,
195 let banned_from_community: ModlogType[] = res.banned_from_community.map(
197 id: r.mod_ban_from_community.id,
198 type_: ModlogEnum.ModBanFromCommunity,
200 moderator: r.moderator,
201 when_: r.mod_ban_from_community.when_,
205 let added_to_community: ModlogType[] = res.added_to_community.map(r => ({
206 id: r.mod_add_community.id,
207 type_: ModlogEnum.ModAddCommunity,
209 moderator: r.moderator,
210 when_: r.mod_add_community.when_,
213 let transferred_to_community: ModlogType[] =
214 res.transferred_to_community.map(r => ({
215 id: r.mod_transfer_community.id,
216 type_: ModlogEnum.ModTransferCommunity,
218 moderator: r.moderator,
219 when_: r.mod_transfer_community.when_,
222 let added: ModlogType[] = res.added.map(r => ({
224 type_: ModlogEnum.ModAdd,
226 moderator: r.moderator,
227 when_: r.mod_add.when_,
230 let banned: ModlogType[] = res.banned.map(r => ({
232 type_: ModlogEnum.ModBan,
234 moderator: r.moderator,
235 when_: r.mod_ban.when_,
238 let purged_persons: ModlogType[] = res.admin_purged_persons.map(r => ({
239 id: r.admin_purge_person.id,
240 type_: ModlogEnum.AdminPurgePerson,
243 when_: r.admin_purge_person.when_,
246 let purged_communities: ModlogType[] = res.admin_purged_communities.map(
248 id: r.admin_purge_community.id,
249 type_: ModlogEnum.AdminPurgeCommunity,
252 when_: r.admin_purge_community.when_,
256 let purged_posts: ModlogType[] = res.admin_purged_posts.map(r => ({
257 id: r.admin_purge_post.id,
258 type_: ModlogEnum.AdminPurgePost,
261 when_: r.admin_purge_post.when_,
264 let purged_comments: ModlogType[] = res.admin_purged_comments.map(r => ({
265 id: r.admin_purge_comment.id,
266 type_: ModlogEnum.AdminPurgeComment,
269 when_: r.admin_purge_comment.when_,
272 let combined: ModlogType[] = [];
274 combined.push(...removed_posts);
275 combined.push(...locked_posts);
276 combined.push(...stickied_posts);
277 combined.push(...removed_comments);
278 combined.push(...removed_communities);
279 combined.push(...banned_from_community);
280 combined.push(...added_to_community);
281 combined.push(...transferred_to_community);
282 combined.push(...added);
283 combined.push(...banned);
284 combined.push(...purged_persons);
285 combined.push(...purged_communities);
286 combined.push(...purged_posts);
287 combined.push(...purged_comments);
290 combined.sort((a, b) => b.when_.localeCompare(a.when_));
295 renderModlogType(i: ModlogType) {
297 case ModlogEnum.ModRemovePost: {
298 let mrpv = i.view as ModRemovePostView;
300 mrpv.mod_remove_post.removed.unwrapOr(false)
304 Post <Link to={`/post/${mrpv.post.id}`}>{mrpv.post.name}</Link>
306 mrpv.mod_remove_post.reason.match({
307 some: reason => <div>reason: {reason}</div>,
312 case ModlogEnum.ModLockPost: {
313 let mlpv = i.view as ModLockPostView;
315 mlpv.mod_lock_post.locked.unwrapOr(false) ? "Locked " : "Unlocked ",
317 Post <Link to={`/post/${mlpv.post.id}`}>{mlpv.post.name}</Link>
321 case ModlogEnum.ModStickyPost: {
322 let mspv = i.view as ModStickyPostView;
324 mspv.mod_sticky_post.stickied.unwrapOr(false)
328 Post <Link to={`/post/${mspv.post.id}`}>{mspv.post.name}</Link>
332 case ModlogEnum.ModRemoveComment: {
333 let mrc = i.view as ModRemoveCommentView;
335 mrc.mod_remove_comment.removed.unwrapOr(false)
340 <Link to={`/post/${mrc.post.id}/comment/${mrc.comment.id}`}>
341 {mrc.comment.content}
346 by <PersonListing person={mrc.commenter} />
348 mrc.mod_remove_comment.reason.match({
349 some: reason => <div>reason: {reason}</div>,
354 case ModlogEnum.ModRemoveCommunity: {
355 let mrco = i.view as ModRemoveCommunityView;
357 mrco.mod_remove_community.removed.unwrapOr(false)
361 Community <CommunityLink community={mrco.community} />
363 mrco.mod_remove_community.reason.match({
364 some: reason => <div>reason: {reason}</div>,
367 mrco.mod_remove_community.expires.match({
369 <div>expires: {moment.utc(expires).fromNow()}</div>
375 case ModlogEnum.ModBanFromCommunity: {
376 let mbfc = i.view as ModBanFromCommunityView;
379 {mbfc.mod_ban_from_community.banned.unwrapOr(false)
384 <PersonListing person={mbfc.banned_person} />
386 <span> from the community </span>,
388 <CommunityLink community={mbfc.community} />
390 mbfc.mod_ban_from_community.reason.match({
391 some: reason => <div>reason: {reason}</div>,
394 mbfc.mod_ban_from_community.expires.match({
396 <div>expires: {moment.utc(expires).fromNow()}</div>
402 case ModlogEnum.ModAddCommunity: {
403 let mac = i.view as ModAddCommunityView;
406 {mac.mod_add_community.removed.unwrapOr(false)
411 <PersonListing person={mac.modded_person} />
413 <span> as a mod to the community </span>,
415 <CommunityLink community={mac.community} />
419 case ModlogEnum.ModTransferCommunity: {
420 let mtc = i.view as ModTransferCommunityView;
423 {mtc.mod_transfer_community.removed.unwrapOr(false)
425 : "Transferred "}{" "}
428 <CommunityLink community={mtc.community} />
432 <PersonListing person={mtc.modded_person} />
436 case ModlogEnum.ModBan: {
437 let mb = i.view as ModBanView;
440 {mb.mod_ban.banned.unwrapOr(false) ? "Banned " : "Unbanned "}{" "}
443 <PersonListing person={mb.banned_person} />
445 mb.mod_ban.reason.match({
446 some: reason => <div>reason: {reason}</div>,
449 mb.mod_ban.expires.match({
451 <div>expires: {moment.utc(expires).fromNow()}</div>
457 case ModlogEnum.ModAdd: {
458 let ma = i.view as ModAddView;
461 {ma.mod_add.removed.unwrapOr(false) ? "Removed " : "Appointed "}{" "}
464 <PersonListing person={ma.modded_person} />
466 <span> as an admin </span>,
469 case ModlogEnum.AdminPurgePerson: {
470 let ap = i.view as AdminPurgePersonView;
472 <span>Purged a Person</span>,
473 ap.admin_purge_person.reason.match({
474 some: reason => <div>reason: {reason}</div>,
479 case ModlogEnum.AdminPurgeCommunity: {
480 let ap = i.view as AdminPurgeCommunityView;
482 <span>Purged a Community</span>,
483 ap.admin_purge_community.reason.match({
484 some: reason => <div>reason: {reason}</div>,
489 case ModlogEnum.AdminPurgePost: {
490 let ap = i.view as AdminPurgePostView;
492 <span>Purged a Post from from </span>,
493 <CommunityLink community={ap.community} />,
494 ap.admin_purge_post.reason.match({
495 some: reason => <div>reason: {reason}</div>,
500 case ModlogEnum.AdminPurgeComment: {
501 let ap = i.view as AdminPurgeCommentView;
504 Purged a Comment from{" "}
505 <Link to={`/post/${ap.post.id}`}>{ap.post.name}</Link>
507 ap.admin_purge_comment.reason.match({
508 some: reason => <div>reason: {reason}</div>,
519 let combined = this.state.res.map(this.buildCombined).unwrapOr([]);
526 <MomentTime published={i.when_} updated={None} />
529 {this.amAdminOrMod ? (
530 <PersonListing person={i.moderator} />
532 <div>{this.modOrAdminText(i.moderator)}</div>
535 <td>{this.renderModlogType(i)}</td>
542 get amAdminOrMod(): boolean {
544 amAdmin(Some(this.state.siteRes.admins)) ||
545 amMod(this.state.communityMods)
549 modOrAdminText(person: PersonSafe): string {
551 this.isoData.site_res.admins.map(a => a.person.id).includes(person.id)
553 return i18n.t("admin");
555 return i18n.t("mod");
559 get documentTitle(): string {
560 return this.state.siteRes.site_view.match({
561 some: siteView => `Modlog - ${siteView.site.name}`,
568 <div class="container">
570 title={this.documentTitle}
571 path={this.context.router.route.match.url}
575 {this.state.loading ? (
581 <div class="table-responsive">
582 <table id="modlog_table" class="table table-sm table-hover">
583 <thead class="pointer">
585 <th> {i18n.t("time")}</th>
586 <th>{i18n.t("mod")}</th>
587 <th>{i18n.t("action")}</th>
593 page={this.state.page}
594 onChange={this.handlePageChange}
603 handlePageChange(val: number) {
604 this.setState({ page: val });
609 let modlogForm = new GetModlog({
610 community_id: this.state.communityId,
612 page: Some(this.state.page),
613 limit: Some(fetchLimit),
614 auth: auth(false).ok(),
616 WebSocketService.Instance.send(wsClient.getModlog(modlogForm));
618 this.state.communityId.match({
620 let communityForm = new GetCommunity({
623 auth: auth(false).ok(),
625 WebSocketService.Instance.send(wsClient.getCommunity(communityForm));
631 static fetchInitialData(req: InitialFetchRequest): Promise<any>[] {
632 let pathSplit = req.path.split("/");
633 let communityId = Some(pathSplit[3]).map(Number);
634 let promises: Promise<any>[] = [];
636 let modlogForm = new GetModlog({
638 limit: Some(fetchLimit),
639 community_id: communityId,
644 promises.push(req.client.getModlog(modlogForm));
646 if (communityId.isSome()) {
647 let communityForm = new GetCommunity({
652 promises.push(req.client.getCommunity(communityForm));
654 promises.push(Promise.resolve());
659 parseMessage(msg: any) {
660 let op = wsUserOp(msg);
663 toast(i18n.t(msg.error), "danger");
665 } else if (op == UserOperation.GetModlog) {
666 let data = wsJsonToRes<GetModlogResponse>(msg, GetModlogResponse);
667 this.state.loading = false;
668 window.scrollTo(0, 0);
669 this.state.res = Some(data);
670 this.setState(this.state);
671 } else if (op == UserOperation.GetCommunity) {
672 let data = wsJsonToRes<GetCommunityResponse>(msg, GetCommunityResponse);
673 this.state.communityMods = Some(data.moderators);