1 import { None, Option, Some } from "@sniptt/monads";
2 import { Component } from "inferno";
3 import { Link } from "inferno-router";
5 CommunityModeratorView,
13 ModBanFromCommunityView,
17 ModRemoveCommunityView,
20 ModTransferCommunityView,
25 } from "lemmy-js-client";
26 import moment from "moment";
27 import { Subscription } from "rxjs";
28 import { i18n } from "../i18next";
29 import { InitialFetchRequest } from "../interfaces";
30 import { WebSocketService } from "../services";
42 import { HtmlTags } from "./common/html-tags";
43 import { Spinner } from "./common/icon";
44 import { MomentTime } from "./common/moment-time";
45 import { Paginator } from "./common/paginator";
46 import { CommunityLink } from "./community/community-link";
47 import { PersonListing } from "./person/person-listing";
69 | ModRemoveCommentView
70 | ModRemoveCommunityView
71 | ModBanFromCommunityView
74 | ModTransferCommunityView
79 interface ModlogState {
80 res: Option<GetModlogResponse>;
81 communityId: Option<number>;
82 communityMods: Option<CommunityModeratorView[]>;
84 siteRes: GetSiteResponse;
88 export class Modlog extends Component<any, ModlogState> {
89 private isoData = setIsoData(
94 private subscription: Subscription;
95 private emptyState: ModlogState = {
101 siteRes: this.isoData.site_res,
104 constructor(props: any, context: any) {
105 super(props, context);
107 this.state = this.emptyState;
108 this.handlePageChange = this.handlePageChange.bind(this);
110 this.state.communityId = this.props.match.params.community_id
111 ? Some(Number(this.props.match.params.community_id))
114 this.parseMessage = this.parseMessage.bind(this);
115 this.subscription = wsSubscribe(this.parseMessage);
117 // Only fetch the data if coming from another route
118 if (this.isoData.path == this.context.router.route.match.url) {
119 this.state.res = Some(this.isoData.routeData[0] as GetModlogResponse);
121 // Getting the moderators
122 let communityRes = Some(
123 this.isoData.routeData[1] as GetCommunityResponse
125 this.state.communityMods = communityRes.map(c => c.moderators);
127 this.state.loading = false;
133 componentWillUnmount() {
135 this.subscription.unsubscribe();
139 buildCombined(res: GetModlogResponse): ModlogType[] {
140 let removed_posts: ModlogType[] = res.removed_posts.map(r => ({
141 id: r.mod_remove_post.id,
142 type_: ModlogEnum.ModRemovePost,
144 when_: r.mod_remove_post.when_,
147 let locked_posts: ModlogType[] = res.locked_posts.map(r => ({
148 id: r.mod_lock_post.id,
149 type_: ModlogEnum.ModLockPost,
151 when_: r.mod_lock_post.when_,
154 let stickied_posts: ModlogType[] = res.stickied_posts.map(r => ({
155 id: r.mod_sticky_post.id,
156 type_: ModlogEnum.ModStickyPost,
158 when_: r.mod_sticky_post.when_,
161 let removed_comments: ModlogType[] = res.removed_comments.map(r => ({
162 id: r.mod_remove_comment.id,
163 type_: ModlogEnum.ModRemoveComment,
165 when_: r.mod_remove_comment.when_,
168 let removed_communities: ModlogType[] = res.removed_communities.map(r => ({
169 id: r.mod_remove_community.id,
170 type_: ModlogEnum.ModRemoveCommunity,
172 when_: r.mod_remove_community.when_,
175 let banned_from_community: ModlogType[] = res.banned_from_community.map(
177 id: r.mod_ban_from_community.id,
178 type_: ModlogEnum.ModBanFromCommunity,
180 when_: r.mod_ban_from_community.when_,
184 let added_to_community: ModlogType[] = res.added_to_community.map(r => ({
185 id: r.mod_add_community.id,
186 type_: ModlogEnum.ModAddCommunity,
188 when_: r.mod_add_community.when_,
191 let transferred_to_community: ModlogType[] =
192 res.transferred_to_community.map(r => ({
193 id: r.mod_transfer_community.id,
194 type_: ModlogEnum.ModTransferCommunity,
196 when_: r.mod_transfer_community.when_,
199 let added: ModlogType[] = res.added.map(r => ({
201 type_: ModlogEnum.ModAdd,
203 when_: r.mod_add.when_,
206 let banned: ModlogType[] = res.banned.map(r => ({
208 type_: ModlogEnum.ModBan,
210 when_: r.mod_ban.when_,
213 let combined: ModlogType[] = [];
215 combined.push(...removed_posts);
216 combined.push(...locked_posts);
217 combined.push(...stickied_posts);
218 combined.push(...removed_comments);
219 combined.push(...removed_communities);
220 combined.push(...banned_from_community);
221 combined.push(...added_to_community);
222 combined.push(...transferred_to_community);
223 combined.push(...added);
224 combined.push(...banned);
227 combined.sort((a, b) => b.when_.localeCompare(a.when_));
232 renderModlogType(i: ModlogType) {
234 case ModlogEnum.ModRemovePost: {
235 let mrpv = i.view as ModRemovePostView;
237 mrpv.mod_remove_post.removed ? "Removed " : "Restored ",
239 Post <Link to={`/post/${mrpv.post.id}`}>{mrpv.post.name}</Link>
241 mrpv.mod_remove_post.reason &&
242 ` reason: ${mrpv.mod_remove_post.reason}`,
245 case ModlogEnum.ModLockPost: {
246 let mlpv = i.view as ModLockPostView;
248 mlpv.mod_lock_post.locked ? "Locked " : "Unlocked ",
250 Post <Link to={`/post/${mlpv.post.id}`}>{mlpv.post.name}</Link>
254 case ModlogEnum.ModStickyPost: {
255 let mspv = i.view as ModStickyPostView;
257 mspv.mod_sticky_post.stickied ? "Stickied " : "Unstickied ",
259 Post <Link to={`/post/${mspv.post.id}`}>{mspv.post.name}</Link>
263 case ModlogEnum.ModRemoveComment: {
264 let mrc = i.view as ModRemoveCommentView;
266 mrc.mod_remove_comment.removed ? "Removed " : "Restored ",
269 <Link to={`/post/${mrc.post.id}/comment/${mrc.comment.id}`}>
270 {mrc.comment.content}
275 by <PersonListing person={mrc.commenter} />
277 mrc.mod_remove_comment.reason &&
278 ` reason: ${mrc.mod_remove_comment.reason}`,
281 case ModlogEnum.ModRemoveCommunity: {
282 let mrco = i.view as ModRemoveCommunityView;
284 mrco.mod_remove_community.removed ? "Removed " : "Restored ",
286 Community <CommunityLink community={mrco.community} />
288 mrco.mod_remove_community.reason.isSome() &&
289 ` reason: ${mrco.mod_remove_community.reason.unwrap()}`,
290 mrco.mod_remove_community.expires.isSome() &&
292 .utc(mrco.mod_remove_community.expires.unwrap())
296 case ModlogEnum.ModBanFromCommunity: {
297 let mbfc = i.view as ModBanFromCommunityView;
300 {mbfc.mod_ban_from_community.banned ? "Banned " : "Unbanned "}{" "}
303 <PersonListing person={mbfc.banned_person} />
305 <span> from the community </span>,
307 <CommunityLink community={mbfc.community} />
310 {mbfc.mod_ban_from_community.reason.isSome() &&
311 ` reason: ${mbfc.mod_ban_from_community.reason.unwrap()}`}
314 {mbfc.mod_ban_from_community.expires.isSome() &&
316 .utc(mbfc.mod_ban_from_community.expires.unwrap())
321 case ModlogEnum.ModAddCommunity: {
322 let mac = i.view as ModAddCommunityView;
325 {mac.mod_add_community.removed ? "Removed " : "Appointed "}{" "}
328 <PersonListing person={mac.modded_person} />
330 <span> as a mod to the community </span>,
332 <CommunityLink community={mac.community} />
336 case ModlogEnum.ModTransferCommunity: {
337 let mtc = i.view as ModTransferCommunityView;
340 {mtc.mod_transfer_community.removed ? "Removed " : "Transferred "}{" "}
343 <CommunityLink community={mtc.community} />
347 <PersonListing person={mtc.modded_person} />
351 case ModlogEnum.ModBan: {
352 let mb = i.view as ModBanView;
354 <span>{mb.mod_ban.banned ? "Banned " : "Unbanned "} </span>,
356 <PersonListing person={mb.banned_person} />
359 {mb.mod_ban.reason.isSome() &&
360 ` reason: ${mb.mod_ban.reason.unwrap()}`}
363 {mb.mod_ban.expires.isSome() &&
364 ` expires: ${moment.utc(mb.mod_ban.expires.unwrap()).fromNow()}`}
368 case ModlogEnum.ModAdd: {
369 let ma = i.view as ModAddView;
372 {ma.mod_add.removed.isSome() && ma.mod_add.removed.unwrap()
377 <PersonListing person={ma.modded_person} />
379 <span> as an admin </span>,
388 let combined = this.state.res.map(this.buildCombined).unwrapOr([]);
395 <MomentTime published={i.when_} updated={None} />
398 {this.amAdminOrMod ? (
399 <PersonListing person={i.view.moderator} />
401 <div>{this.modOrAdminText(i.view.moderator)}</div>
404 <td>{this.renderModlogType(i)}</td>
411 get amAdminOrMod(): boolean {
413 amAdmin(Some(this.state.siteRes.admins)) ||
414 amMod(this.state.communityMods)
418 modOrAdminText(person: PersonSafe): Text {
420 this.isoData.site_res.admins.map(a => a.person.id).includes(person.id)
422 return i18n.t("admin");
424 return i18n.t("mod");
428 get documentTitle(): string {
429 return this.state.siteRes.site_view.match({
430 some: siteView => `Modlog - ${siteView.site.name}`,
437 <div class="container">
439 title={this.documentTitle}
440 path={this.context.router.route.match.url}
444 {this.state.loading ? (
450 <div class="table-responsive">
451 <table id="modlog_table" class="table table-sm table-hover">
452 <thead class="pointer">
454 <th> {i18n.t("time")}</th>
455 <th>{i18n.t("mod")}</th>
456 <th>{i18n.t("action")}</th>
462 page={this.state.page}
463 onChange={this.handlePageChange}
472 handlePageChange(val: number) {
473 this.setState({ page: val });
478 let modlogForm = new GetModlog({
479 community_id: this.state.communityId,
481 page: Some(this.state.page),
482 limit: Some(fetchLimit),
483 auth: auth(false).ok(),
485 WebSocketService.Instance.send(wsClient.getModlog(modlogForm));
487 this.state.communityId.match({
489 let communityForm = new GetCommunity({
492 auth: auth(false).ok(),
494 WebSocketService.Instance.send(wsClient.getCommunity(communityForm));
500 static fetchInitialData(req: InitialFetchRequest): Promise<any>[] {
501 let pathSplit = req.path.split("/");
502 let communityId = Some(pathSplit[3]).map(Number);
503 let promises: Promise<any>[] = [];
505 let modlogForm = new GetModlog({
507 limit: Some(fetchLimit),
508 community_id: communityId,
513 promises.push(req.client.getModlog(modlogForm));
515 if (communityId.isSome()) {
516 let communityForm = new GetCommunity({
521 promises.push(req.client.getCommunity(communityForm));
523 promises.push(Promise.resolve());
528 parseMessage(msg: any) {
529 let op = wsUserOp(msg);
532 toast(i18n.t(msg.error), "danger");
534 } else if (op == UserOperation.GetModlog) {
535 let data = wsJsonToRes<GetModlogResponse>(msg, GetModlogResponse);
536 this.state.loading = false;
537 window.scrollTo(0, 0);
538 this.state.res = Some(data);
539 this.setState(this.state);
540 } else if (op == UserOperation.GetCommunity) {
541 let data = wsJsonToRes<GetCommunityResponse>(msg, GetCommunityResponse);
542 this.state.communityMods = Some(data.moderators);