1 import { Component } from "inferno";
2 import { Link } from "inferno-router";
3 import { Subscription } from "rxjs";
13 ModRemoveCommunityView,
14 ModBanFromCommunityView,
20 CommunityModeratorView,
21 } from "lemmy-js-client";
22 import { WebSocketService, UserService } from "../services";
33 import { MomentTime } from "./moment-time";
34 import { HtmlTags } from "./html-tags";
35 import moment from "moment";
36 import { i18n } from "../i18next";
37 import { InitialFetchRequest } from "shared/interfaces";
38 import { PersonListing } from "./person-listing";
39 import { CommunityLink } from "./community-link";
40 import { Spinner } from "./icon";
41 import { Paginator } from "./paginator";
62 | ModRemoveCommentView
63 | ModRemoveCommunityView
64 | ModBanFromCommunityView
71 interface ModlogState {
72 res: GetModlogResponse;
74 communityName?: string;
75 communityMods?: CommunityModeratorView[];
81 export class Modlog extends Component<any, ModlogState> {
82 private isoData = setIsoData(this.context);
83 private subscription: Subscription;
84 private emptyState: ModlogState = {
90 removed_communities: [],
91 banned_from_community: [],
93 added_to_community: [],
98 site_view: this.isoData.site_res.site_view,
101 constructor(props: any, context: any) {
102 super(props, context);
104 this.state = this.emptyState;
105 this.handlePageChange = this.handlePageChange.bind(this);
107 this.state.communityId = this.props.match.params.community_id
108 ? Number(this.props.match.params.community_id)
111 this.parseMessage = this.parseMessage.bind(this);
112 this.subscription = wsSubscribe(this.parseMessage);
114 // Only fetch the data if coming from another route
115 if (this.isoData.path == this.context.router.route.match.url) {
116 let data = this.isoData.routeData[0];
117 this.state.res = data;
118 this.state.loading = false;
120 // Getting the moderators
121 if (this.isoData.routeData[1]) {
122 this.state.communityMods = this.isoData.routeData[1].moderators;
129 componentWillUnmount() {
131 this.subscription.unsubscribe();
135 buildCombined(res: GetModlogResponse): ModlogType[] {
136 let removed_posts: ModlogType[] = res.removed_posts.map(r => ({
137 id: r.mod_remove_post.id,
138 type_: ModlogEnum.ModRemovePost,
140 when_: r.mod_remove_post.when_,
143 let locked_posts: ModlogType[] = res.locked_posts.map(r => ({
144 id: r.mod_lock_post.id,
145 type_: ModlogEnum.ModLockPost,
147 when_: r.mod_lock_post.when_,
150 let stickied_posts: ModlogType[] = res.stickied_posts.map(r => ({
151 id: r.mod_sticky_post.id,
152 type_: ModlogEnum.ModStickyPost,
154 when_: r.mod_sticky_post.when_,
157 let removed_comments: ModlogType[] = res.removed_comments.map(r => ({
158 id: r.mod_remove_comment.id,
159 type_: ModlogEnum.ModRemoveComment,
161 when_: r.mod_remove_comment.when_,
164 let removed_communities: ModlogType[] = res.removed_communities.map(r => ({
165 id: r.mod_remove_community.id,
166 type_: ModlogEnum.ModRemoveCommunity,
168 when_: r.mod_remove_community.when_,
171 let banned_from_community: ModlogType[] = res.banned_from_community.map(
173 id: r.mod_ban_from_community.id,
174 type_: ModlogEnum.ModBanFromCommunity,
176 when_: r.mod_ban_from_community.when_,
180 let added_to_community: ModlogType[] = res.added_to_community.map(r => ({
181 id: r.mod_add_community.id,
182 type_: ModlogEnum.ModAddCommunity,
184 when_: r.mod_add_community.when_,
187 let added: ModlogType[] = res.added.map(r => ({
189 type_: ModlogEnum.ModAdd,
191 when_: r.mod_add.when_,
194 let banned: ModlogType[] = res.banned.map(r => ({
196 type_: ModlogEnum.ModBan,
198 when_: r.mod_ban.when_,
201 let combined: ModlogType[] = [];
203 combined.push(...removed_posts);
204 combined.push(...locked_posts);
205 combined.push(...stickied_posts);
206 combined.push(...removed_comments);
207 combined.push(...removed_communities);
208 combined.push(...banned_from_community);
209 combined.push(...added_to_community);
210 combined.push(...added);
211 combined.push(...banned);
213 if (this.state.communityId && combined.length > 0) {
214 this.state.communityName = (
215 combined[0].view as ModRemovePostView
220 combined.sort((a, b) => b.when_.localeCompare(a.when_));
225 renderModlogType(i: ModlogType) {
227 case ModlogEnum.ModRemovePost: {
228 let mrpv = i.view as ModRemovePostView;
230 mrpv.mod_remove_post.removed ? "Removed " : "Restored ",
232 Post <Link to={`/post/${mrpv.post.id}`}>{mrpv.post.name}</Link>
234 mrpv.mod_remove_post.reason &&
235 ` reason: ${mrpv.mod_remove_post.reason}`,
238 case ModlogEnum.ModLockPost: {
239 let mlpv = i.view as ModLockPostView;
241 mlpv.mod_lock_post.locked ? "Locked " : "Unlocked ",
243 Post <Link to={`/post/${mlpv.post.id}`}>{mlpv.post.name}</Link>
247 case ModlogEnum.ModStickyPost: {
248 let mspv = i.view as ModStickyPostView;
250 mspv.mod_sticky_post.stickied ? "Stickied " : "Unstickied ",
252 Post <Link to={`/post/${mspv.post.id}`}>{mspv.post.name}</Link>
256 case ModlogEnum.ModRemoveComment: {
257 let mrc = i.view as ModRemoveCommentView;
259 mrc.mod_remove_comment.removed ? "Removed " : "Restored ",
262 <Link to={`/post/${mrc.post.id}/comment/${mrc.comment.id}`}>
263 {mrc.comment.content}
268 by <PersonListing person={mrc.commenter} />
270 mrc.mod_remove_comment.reason &&
271 ` reason: ${mrc.mod_remove_comment.reason}`,
274 case ModlogEnum.ModRemoveCommunity: {
275 let mrco = i.view as ModRemoveCommunityView;
277 mrco.mod_remove_community.removed ? "Removed " : "Restored ",
279 Community <CommunityLink community={mrco.community} />
281 mrco.mod_remove_community.reason &&
282 ` reason: ${mrco.mod_remove_community.reason}`,
283 mrco.mod_remove_community.expires &&
285 .utc(mrco.mod_remove_community.expires)
289 case ModlogEnum.ModBanFromCommunity: {
290 let mbfc = i.view as ModBanFromCommunityView;
293 {mbfc.mod_ban_from_community.banned ? "Banned " : "Unbanned "}{" "}
296 <PersonListing person={mbfc.banned_person} />
298 <span> from the community </span>,
300 <CommunityLink community={mbfc.community} />
303 {mbfc.mod_ban_from_community.reason &&
304 ` reason: ${mbfc.mod_ban_from_community.reason}`}
307 {mbfc.mod_ban_from_community.expires &&
309 .utc(mbfc.mod_ban_from_community.expires)
314 case ModlogEnum.ModAddCommunity: {
315 let mac = i.view as ModAddCommunityView;
318 {mac.mod_add_community.removed ? "Removed " : "Appointed "}{" "}
321 <PersonListing person={mac.modded_person} />
323 <span> as a mod to the community </span>,
325 <CommunityLink community={mac.community} />
329 case ModlogEnum.ModBan: {
330 let mb = i.view as ModBanView;
332 <span>{mb.mod_ban.banned ? "Banned " : "Unbanned "} </span>,
334 <PersonListing person={mb.banned_person} />
336 <div>{mb.mod_ban.reason && ` reason: ${mb.mod_ban.reason}`}</div>,
338 {mb.mod_ban.expires &&
339 ` expires: ${moment.utc(mb.mod_ban.expires).fromNow()}`}
343 case ModlogEnum.ModAdd: {
344 let ma = i.view as ModAddView;
346 <span>{ma.mod_add.removed ? "Removed " : "Appointed "} </span>,
348 <PersonListing person={ma.modded_person} />
350 <span> as an admin </span>,
359 let combined = this.buildCombined(this.state.res);
366 <MomentTime data={i} />
369 {this.isAdminOrMod ? (
370 <PersonListing person={i.view.moderator} />
372 <div>{i18n.t("mod")}</div>
375 <td>{this.renderModlogType(i)}</td>
382 get isAdminOrMod(): boolean {
384 UserService.Instance.localUserView &&
385 this.isoData.site_res.admins
386 .map(a => a.person.id)
387 .includes(UserService.Instance.localUserView.person.id);
389 UserService.Instance.localUserView &&
390 this.state.communityMods &&
391 this.state.communityMods
392 .map(m => m.moderator.id)
393 .includes(UserService.Instance.localUserView.person.id);
394 return isAdmin || isMod;
397 get documentTitle(): string {
398 return `Modlog - ${this.state.site_view.site.name}`;
403 <div class="container">
405 title={this.documentTitle}
406 path={this.context.router.route.match.url}
408 {this.state.loading ? (
415 {this.state.communityName && (
417 className="text-body"
418 to={`/c/${this.state.communityName}`}
420 /c/{this.state.communityName}{" "}
423 <span>{i18n.t("modlog")}</span>
425 <div class="table-responsive">
426 <table id="modlog_table" class="table table-sm table-hover">
427 <thead class="pointer">
429 <th> {i18n.t("time")}</th>
430 <th>{i18n.t("mod")}</th>
431 <th>{i18n.t("action")}</th>
437 page={this.state.page}
438 onChange={this.handlePageChange}
447 handlePageChange(val: number) {
448 this.setState({ page: val });
453 let modlogForm: GetModlog = {
454 community_id: this.state.communityId,
455 page: this.state.page,
458 WebSocketService.Instance.send(wsClient.getModlog(modlogForm));
460 if (this.state.communityId) {
461 let communityForm: GetCommunity = {
462 id: this.state.communityId,
463 name: this.state.communityName,
465 WebSocketService.Instance.send(wsClient.getCommunity(communityForm));
469 static fetchInitialData(req: InitialFetchRequest): Promise<any>[] {
470 let pathSplit = req.path.split("/");
471 let communityId = pathSplit[3];
472 let promises: Promise<any>[] = [];
474 let modlogForm: GetModlog = {
480 modlogForm.community_id = Number(communityId);
483 promises.push(req.client.getModlog(modlogForm));
486 let communityForm: GetCommunity = {
487 id: Number(communityId),
489 promises.push(req.client.getCommunity(communityForm));
494 parseMessage(msg: any) {
495 let op = wsUserOp(msg);
498 toast(i18n.t(msg.error), "danger");
500 } else if (op == UserOperation.GetModlog) {
501 let data = wsJsonToRes<GetModlogResponse>(msg).data;
502 this.state.loading = false;
503 window.scrollTo(0, 0);
504 this.state.res = data;
505 this.setState(this.state);
506 } else if (op == UserOperation.GetCommunity) {
507 let data = wsJsonToRes<GetCommunityResponse>(msg).data;
508 this.state.communityMods = data.moderators;