1 import { Component, linkEvent } 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";
61 | ModRemoveCommentView
62 | ModRemoveCommunityView
63 | ModBanFromCommunityView
70 interface ModlogState {
71 res: GetModlogResponse;
73 communityName?: string;
74 communityMods?: CommunityModeratorView[];
80 export class Modlog extends Component<any, ModlogState> {
81 private isoData = setIsoData(this.context);
82 private subscription: Subscription;
83 private emptyState: ModlogState = {
89 removed_communities: [],
90 banned_from_community: [],
92 added_to_community: [],
97 site_view: this.isoData.site_res.site_view,
100 constructor(props: any, context: any) {
101 super(props, context);
103 this.state = this.emptyState;
104 this.state.communityId = this.props.match.params.community_id
105 ? Number(this.props.match.params.community_id)
108 this.parseMessage = this.parseMessage.bind(this);
109 this.subscription = wsSubscribe(this.parseMessage);
111 // Only fetch the data if coming from another route
112 if (this.isoData.path == this.context.router.route.match.url) {
113 let data = this.isoData.routeData[0];
114 this.state.res = data;
115 this.state.loading = false;
117 // Getting the moderators
118 if (this.isoData.routeData[1]) {
119 this.state.communityMods = this.isoData.routeData[1].moderators;
126 componentWillUnmount() {
128 this.subscription.unsubscribe();
132 buildCombined(res: GetModlogResponse): ModlogType[] {
133 let removed_posts: ModlogType[] = res.removed_posts.map(r => ({
134 id: r.mod_remove_post.id,
135 type_: ModlogEnum.ModRemovePost,
137 when_: r.mod_remove_post.when_,
140 let locked_posts: ModlogType[] = res.locked_posts.map(r => ({
141 id: r.mod_lock_post.id,
142 type_: ModlogEnum.ModLockPost,
144 when_: r.mod_lock_post.when_,
147 let stickied_posts: ModlogType[] = res.stickied_posts.map(r => ({
148 id: r.mod_sticky_post.id,
149 type_: ModlogEnum.ModStickyPost,
151 when_: r.mod_sticky_post.when_,
154 let removed_comments: ModlogType[] = res.removed_comments.map(r => ({
155 id: r.mod_remove_comment.id,
156 type_: ModlogEnum.ModRemoveComment,
158 when_: r.mod_remove_comment.when_,
161 let removed_communities: ModlogType[] = res.removed_communities.map(r => ({
162 id: r.mod_remove_community.id,
163 type_: ModlogEnum.ModRemoveCommunity,
165 when_: r.mod_remove_community.when_,
168 let banned_from_community: ModlogType[] = res.banned_from_community.map(
170 id: r.mod_ban_from_community.id,
171 type_: ModlogEnum.ModBanFromCommunity,
173 when_: r.mod_ban_from_community.when_,
177 let added_to_community: ModlogType[] = res.added_to_community.map(r => ({
178 id: r.mod_add_community.id,
179 type_: ModlogEnum.ModAddCommunity,
181 when_: r.mod_add_community.when_,
184 let added: ModlogType[] = res.added.map(r => ({
186 type_: ModlogEnum.ModAdd,
188 when_: r.mod_add.when_,
191 let banned: ModlogType[] = res.banned.map(r => ({
193 type_: ModlogEnum.ModBan,
195 when_: r.mod_ban.when_,
198 let combined: ModlogType[] = [];
200 combined.push(...removed_posts);
201 combined.push(...locked_posts);
202 combined.push(...stickied_posts);
203 combined.push(...removed_comments);
204 combined.push(...removed_communities);
205 combined.push(...banned_from_community);
206 combined.push(...added_to_community);
207 combined.push(...added);
208 combined.push(...banned);
210 if (this.state.communityId && combined.length > 0) {
211 this.state.communityName = (combined[0]
212 .view as ModRemovePostView).community.name;
216 combined.sort((a, b) => b.when_.localeCompare(a.when_));
221 renderModlogType(i: ModlogType) {
223 case ModlogEnum.ModRemovePost: {
224 let mrpv = i.view as ModRemovePostView;
226 mrpv.mod_remove_post.removed ? "Removed " : "Restored ",
228 Post <Link to={`/post/${mrpv.post.id}`}>{mrpv.post.name}</Link>
230 mrpv.mod_remove_post.reason &&
231 ` reason: ${mrpv.mod_remove_post.reason}`,
234 case ModlogEnum.ModLockPost: {
235 let mlpv = i.view as ModLockPostView;
237 mlpv.mod_lock_post.locked ? "Locked " : "Unlocked ",
239 Post <Link to={`/post/${mlpv.post.id}`}>{mlpv.post.name}</Link>
243 case ModlogEnum.ModStickyPost: {
244 let mspv = i.view as ModStickyPostView;
246 mspv.mod_sticky_post.stickied ? "Stickied " : "Unstickied ",
248 Post <Link to={`/post/${mspv.post.id}`}>{mspv.post.name}</Link>
252 case ModlogEnum.ModRemoveComment: {
253 let mrc = i.view as ModRemoveCommentView;
255 mrc.mod_remove_comment.removed ? "Removed " : "Restored ",
258 <Link to={`/post/${mrc.post.id}/comment/${mrc.comment.id}`}>
259 {mrc.comment.content}
264 by <PersonListing person={mrc.commenter} />
266 mrc.mod_remove_comment.reason &&
267 ` reason: ${mrc.mod_remove_comment.reason}`,
270 case ModlogEnum.ModRemoveCommunity: {
271 let mrco = i.view as ModRemoveCommunityView;
273 mrco.mod_remove_community.removed ? "Removed " : "Restored ",
275 Community <CommunityLink community={mrco.community} />
277 mrco.mod_remove_community.reason &&
278 ` reason: ${mrco.mod_remove_community.reason}`,
279 mrco.mod_remove_community.expires &&
281 .utc(mrco.mod_remove_community.expires)
285 case ModlogEnum.ModBanFromCommunity: {
286 let mbfc = i.view as ModBanFromCommunityView;
289 {mbfc.mod_ban_from_community.banned ? "Banned " : "Unbanned "}{" "}
292 <PersonListing person={mbfc.banned_person} />
294 <span> from the community </span>,
296 <CommunityLink community={mbfc.community} />
299 {mbfc.mod_ban_from_community.reason &&
300 ` reason: ${mbfc.mod_ban_from_community.reason}`}
303 {mbfc.mod_ban_from_community.expires &&
305 .utc(mbfc.mod_ban_from_community.expires)
310 case ModlogEnum.ModAddCommunity: {
311 let mac = i.view as ModAddCommunityView;
314 {mac.mod_add_community.removed ? "Removed " : "Appointed "}{" "}
317 <PersonListing person={mac.modded_person} />
319 <span> as a mod to the community </span>,
321 <CommunityLink community={mac.community} />
325 case ModlogEnum.ModBan: {
326 let mb = i.view as ModBanView;
328 <span>{mb.mod_ban.banned ? "Banned " : "Unbanned "} </span>,
330 <PersonListing person={mb.banned_person} />
332 <div>{mb.mod_ban.reason && ` reason: ${mb.mod_ban.reason}`}</div>,
334 {mb.mod_ban.expires &&
335 ` expires: ${moment.utc(mb.mod_ban.expires).fromNow()}`}
339 case ModlogEnum.ModAdd: {
340 let ma = i.view as ModAddView;
342 <span>{ma.mod_add.removed ? "Removed " : "Appointed "} </span>,
344 <PersonListing person={ma.modded_person} />
346 <span> as an admin </span>,
355 let combined = this.buildCombined(this.state.res);
362 <MomentTime data={i} />
365 {this.isAdminOrMod ? (
366 <PersonListing person={i.view.moderator} />
368 <div>{i18n.t("mod")}</div>
371 <td>{this.renderModlogType(i)}</td>
378 get isAdminOrMod(): boolean {
380 UserService.Instance.localUserView &&
381 this.isoData.site_res.admins
382 .map(a => a.person.id)
383 .includes(UserService.Instance.localUserView.person.id);
385 UserService.Instance.localUserView &&
386 this.state.communityMods &&
387 this.state.communityMods
388 .map(m => m.moderator.id)
389 .includes(UserService.Instance.localUserView.person.id);
390 return isAdmin || isMod;
393 get documentTitle(): string {
394 return `Modlog - ${this.state.site_view.site.name}`;
399 <div class="container">
401 title={this.documentTitle}
402 path={this.context.router.route.match.url}
404 {this.state.loading ? (
411 {this.state.communityName && (
413 className="text-body"
414 to={`/c/${this.state.communityName}`}
416 /c/{this.state.communityName}{" "}
419 <span>{i18n.t("modlog")}</span>
421 <div class="table-responsive">
422 <table id="modlog_table" class="table table-sm table-hover">
423 <thead class="pointer">
425 <th> {i18n.t("time")}</th>
426 <th>{i18n.t("mod")}</th>
427 <th>{i18n.t("action")}</th>
443 {this.state.page > 1 && (
445 class="btn btn-secondary mr-1"
446 onClick={linkEvent(this, this.prevPage)}
452 class="btn btn-secondary"
453 onClick={linkEvent(this, this.nextPage)}
461 nextPage(i: Modlog) {
467 prevPage(i: Modlog) {
474 let modlogForm: GetModlog = {
475 community_id: this.state.communityId,
476 page: this.state.page,
479 WebSocketService.Instance.send(wsClient.getModlog(modlogForm));
481 if (this.state.communityId) {
482 let communityForm: GetCommunity = {
483 id: this.state.communityId,
484 name: this.state.communityName,
486 WebSocketService.Instance.send(wsClient.getCommunity(communityForm));
490 static fetchInitialData(req: InitialFetchRequest): Promise<any>[] {
491 let pathSplit = req.path.split("/");
492 let communityId = pathSplit[3];
493 let promises: Promise<any>[] = [];
495 let modlogForm: GetModlog = {
501 modlogForm.community_id = Number(communityId);
504 promises.push(req.client.getModlog(modlogForm));
507 let communityForm: GetCommunity = {
508 id: Number(communityId),
510 promises.push(req.client.getCommunity(communityForm));
515 parseMessage(msg: any) {
516 let op = wsUserOp(msg);
519 toast(i18n.t(msg.error), "danger");
521 } else if (op == UserOperation.GetModlog) {
522 let data = wsJsonToRes<GetModlogResponse>(msg).data;
523 this.state.loading = false;
524 window.scrollTo(0, 0);
525 this.state.res = data;
526 this.setState(this.state);
527 } else if (op == UserOperation.GetCommunity) {
528 let data = wsJsonToRes<GetCommunityResponse>(msg).data;
529 this.state.communityMods = data.moderators;