* Starting on admin purge.
* Updating translations.
* Finishing up item purging.
-Subproject commit 29c689af8d16417c1b84d9491f6bcea888720a87
+Subproject commit de5d4f3a758f8e8b41869c90d97e53ab50577f90
"eslint-plugin-prettier": "^4.0.0",
"husky": "^7.0.4",
"import-sort-style-module": "^6.0.0",
- "lemmy-js-client": "0.17.0-rc.32",
+ "lemmy-js-client": "0.17.0-rc.33",
"lint-staged": "^12.4.1",
"mini-css-extract-plugin": "^2.6.0",
"node-fetch": "^2.6.1",
MarkPersonMentionAsRead,
PersonMentionView,
PersonViewSafe,
+ PurgeComment,
+ PurgePerson,
RemoveComment,
SaveComment,
toUndefined,
} from "lemmy-js-client";
import moment from "moment";
import { i18n } from "../../i18next";
-import { BanType, CommentNode as CommentNodeI } from "../../interfaces";
+import {
+ BanType,
+ CommentNode as CommentNodeI,
+ PurgeType,
+} from "../../interfaces";
import { UserService, WebSocketService } from "../../services";
import {
amCommunityCreator,
showScores,
wsClient,
} from "../../utils";
-import { Icon, Spinner } from "../common/icon";
+import { Icon, PurgeWarning, Spinner } from "../common/icon";
import { MomentTime } from "../common/moment-time";
import { CommunityLink } from "../community/community-link";
import { PersonListing } from "../person/person-listing";
banReason: Option<string>;
banExpireDays: Option<number>;
banType: BanType;
+ showPurgeDialog: boolean;
+ purgeReason: Option<string>;
+ purgeType: PurgeType;
+ purgeLoading: boolean;
showConfirmTransferSite: boolean;
showConfirmTransferCommunity: boolean;
showConfirmAppointAsMod: boolean;
banReason: None,
banExpireDays: None,
banType: BanType.Community,
+ showPurgeDialog: false,
+ purgeLoading: false,
+ purgeReason: None,
+ purgeType: PurgeType.Person,
collapsed: false,
viewSource: false,
showAdvanced: false,
let node = this.props.node;
let cv = this.props.node.comment_view;
+ let purgeTypeText: string;
+ if (this.state.purgeType == PurgeType.Comment) {
+ purgeTypeText = i18n.t("purge_comment");
+ } else if (this.state.purgeType == PurgeType.Person) {
+ purgeTypeText = `${i18n.t("purge")} ${cv.creator.name}`;
+ }
+
let canMod_ = canMod(
this.props.moderators,
this.props.admins,
{/* Admins can ban from all, and appoint other admins */}
{canAdmin_ && (
<>
- {!isAdmin_ &&
- (!isBanned(cv.creator) ? (
+ {!isAdmin_ && (
+ <>
<button
class="btn btn-link btn-animate text-muted"
onClick={linkEvent(
this,
- this.handleModBanShow
+ this.handlePurgePersonShow
)}
- aria-label={i18n.t("ban_from_site")}
+ aria-label={i18n.t("purge_user")}
>
- {i18n.t("ban_from_site")}
+ {i18n.t("purge_user")}
</button>
- ) : (
<button
class="btn btn-link btn-animate text-muted"
onClick={linkEvent(
this,
- this.handleModBanSubmit
+ this.handlePurgeCommentShow
)}
- aria-label={i18n.t("unban_from_site")}
+ aria-label={i18n.t("purge_comment")}
>
- {i18n.t("unban_from_site")}
+ {i18n.t("purge_comment")}
</button>
- ))}
+
+ {!isBanned(cv.creator) ? (
+ <button
+ class="btn btn-link btn-animate text-muted"
+ onClick={linkEvent(
+ this,
+ this.handleModBanShow
+ )}
+ aria-label={i18n.t("ban_from_site")}
+ >
+ {i18n.t("ban_from_site")}
+ </button>
+ ) : (
+ <button
+ class="btn btn-link btn-animate text-muted"
+ onClick={linkEvent(
+ this,
+ this.handleModBanSubmit
+ )}
+ aria-label={i18n.t("unban_from_site")}
+ >
+ {i18n.t("unban_from_site")}
+ </button>
+ )}
+ </>
+ )}
{!isBanned(cv.creator) &&
cv.creator.local &&
(!this.state.showConfirmAppointAsAdmin ? (
</div>
</form>
)}
+
+ {this.state.showPurgeDialog && (
+ <form onSubmit={linkEvent(this, this.handlePurgeSubmit)}>
+ <PurgeWarning />
+ <label class="sr-only" htmlFor="purge-reason">
+ {i18n.t("reason")}
+ </label>
+ <input
+ type="text"
+ id="purge-reason"
+ class="form-control my-3"
+ placeholder={i18n.t("reason")}
+ value={toUndefined(this.state.purgeReason)}
+ onInput={linkEvent(this, this.handlePurgeReasonChange)}
+ />
+ <div class="form-group row col-12">
+ {this.state.purgeLoading ? (
+ <Spinner />
+ ) : (
+ <button
+ type="submit"
+ class="btn btn-secondary"
+ aria-label={purgeTypeText}
+ >
+ {purgeTypeText}
+ </button>
+ )}
+ </div>
+ </form>
+ )}
{this.state.showReply && (
<CommentForm
node={Left(node)}
i.setState(i.state);
}
+ handlePurgePersonShow(i: CommentNode) {
+ i.state.showPurgeDialog = true;
+ i.state.purgeType = PurgeType.Person;
+ i.state.showRemoveDialog = false;
+ i.setState(i.state);
+ }
+
+ handlePurgeCommentShow(i: CommentNode) {
+ i.state.showPurgeDialog = true;
+ i.state.purgeType = PurgeType.Comment;
+ i.state.showRemoveDialog = false;
+ i.setState(i.state);
+ }
+
+ handlePurgeReasonChange(i: CommentNode, event: any) {
+ i.state.purgeReason = Some(event.target.value);
+ i.setState(i.state);
+ }
+
+ handlePurgeSubmit(i: CommentNode, event: any) {
+ event.preventDefault();
+
+ if (i.state.purgeType == PurgeType.Person) {
+ let form = new PurgePerson({
+ person_id: i.props.node.comment_view.creator.id,
+ reason: i.state.purgeReason,
+ auth: auth().unwrap(),
+ });
+ WebSocketService.Instance.send(wsClient.purgePerson(form));
+ } else if (i.state.purgeType == PurgeType.Comment) {
+ let form = new PurgeComment({
+ comment_id: i.props.node.comment_view.comment.id,
+ reason: i.state.purgeReason,
+ auth: auth().unwrap(),
+ });
+ WebSocketService.Instance.send(wsClient.purgeComment(form));
+ }
+
+ i.state.purgeLoading = true;
+ i.setState(i.state);
+ }
+
handleShowConfirmAppointAsMod(i: CommentNode) {
i.state.showConfirmAppointAsMod = true;
i.setState(i.state);
import classNames from "classnames";
import { Component } from "inferno";
+import { i18n } from "../../i18next";
interface IconProps {
icon: string;
);
}
}
+
+export class PurgeWarning extends Component<any, any> {
+ constructor(props: any, context: any) {
+ super(props, context);
+ }
+
+ render() {
+ return (
+ <div class="mt-2 alert alert-danger" role="alert">
+ <Icon icon="alert-triangle" classes="icon-inline mr-2" />
+ {i18n.t("purge_warning")}
+ </div>
+ );
+ }
+}
PostReportResponse,
PostResponse,
PostView,
+ PurgeItemResponse,
SortType,
toOption,
UserOperation,
if (data) {
toast(i18n.t("report_created"));
}
+ } else if (op == UserOperation.PurgeCommunity) {
+ let data = wsJsonToRes<PurgeItemResponse>(msg, PurgeItemResponse);
+ if (data.success) {
+ toast(i18n.t("purge_success"));
+ this.context.router.history.push(`/`);
+ }
}
}
}
-import { Option, Some } from "@sniptt/monads";
+import { None, Option, Some } from "@sniptt/monads";
import { Component, linkEvent } from "inferno";
import { Link } from "inferno-router";
import {
DeleteCommunity,
FollowCommunity,
PersonViewSafe,
+ PurgeCommunity,
RemoveCommunity,
SubscribedType,
toUndefined,
wsClient,
} from "../../utils";
import { BannerIconHeader } from "../common/banner-icon-header";
-import { Icon } from "../common/icon";
+import { Icon, PurgeWarning, Spinner } from "../common/icon";
import { CommunityForm } from "../community/community-form";
import { CommunityLink } from "../community/community-link";
import { PersonListing } from "../person/person-listing";
removeExpires: Option<string>;
showEdit: boolean;
showRemoveDialog: boolean;
+ showPurgeDialog: boolean;
+ purgeReason: Option<string>;
+ purgeLoading: boolean;
showConfirmLeaveModTeam: boolean;
}
private emptyState: SidebarState = {
showEdit: false,
showRemoveDialog: false,
- removeReason: null,
- removeExpires: null,
+ removeReason: None,
+ removeExpires: None,
+ showPurgeDialog: false,
+ purgeReason: None,
+ purgeLoading: false,
showConfirmLeaveModTeam: false,
};
{i18n.t("restore")}
</button>
)}
+ <button
+ class="btn btn-link text-muted d-inline-block"
+ onClick={linkEvent(this, this.handlePurgeCommunityShow)}
+ aria-label={i18n.t("purge_community")}
+ >
+ {i18n.t("purge_community")}
+ </button>
</li>
)}
</ul>
{this.state.showRemoveDialog && (
<form onSubmit={linkEvent(this, this.handleModRemoveSubmit)}>
- <div class="form-group row">
+ <div class="form-group">
<label class="col-form-label" htmlFor="remove-reason">
{i18n.t("reason")}
</label>
{/* <label class="col-form-label">Expires</label> */}
{/* <input type="date" class="form-control mr-2" placeholder={i18n.t('expires')} value={this.state.removeExpires} onInput={linkEvent(this, this.handleModRemoveExpiresChange)} /> */}
{/* </div> */}
- <div class="form-group row">
+ <div class="form-group">
<button type="submit" class="btn btn-secondary">
{i18n.t("remove_community")}
</button>
</div>
</form>
)}
+ {this.state.showPurgeDialog && (
+ <form onSubmit={linkEvent(this, this.handlePurgeSubmit)}>
+ <div class="form-group">
+ <PurgeWarning />
+ </div>
+ <div class="form-group">
+ <label class="sr-only" htmlFor="purge-reason">
+ {i18n.t("reason")}
+ </label>
+ <input
+ type="text"
+ id="purge-reason"
+ class="form-control mr-2"
+ placeholder={i18n.t("reason")}
+ value={toUndefined(this.state.purgeReason)}
+ onInput={linkEvent(this, this.handlePurgeReasonChange)}
+ />
+ </div>
+ <div class="form-group">
+ {this.state.purgeLoading ? (
+ <Spinner />
+ ) : (
+ <button
+ type="submit"
+ class="btn btn-secondary"
+ aria-label={i18n.t("purge_community")}
+ >
+ {i18n.t("purge_community")}
+ </button>
+ )}
+ </div>
+ </form>
+ )}
</>
);
}
}
handleModRemoveReasonChange(i: Sidebar, event: any) {
- i.state.removeReason = event.target.value;
+ i.state.removeReason = Some(event.target.value);
i.setState(i.state);
}
handleModRemoveExpiresChange(i: Sidebar, event: any) {
- console.log(event.target.value);
- i.state.removeExpires = event.target.value;
+ i.state.removeExpires = Some(event.target.value);
i.setState(i.state);
}
i.state.showRemoveDialog = false;
i.setState(i.state);
}
+
+ handlePurgeCommunityShow(i: Sidebar) {
+ i.state.showPurgeDialog = true;
+ i.state.showRemoveDialog = false;
+ i.setState(i.state);
+ }
+
+ handlePurgeReasonChange(i: Sidebar, event: any) {
+ i.state.purgeReason = Some(event.target.value);
+ i.setState(i.state);
+ }
+
+ handlePurgeSubmit(i: Sidebar, event: any) {
+ event.preventDefault();
+
+ let form = new PurgeCommunity({
+ community_id: i.props.community_view.community.id,
+ reason: i.state.purgeReason,
+ auth: auth().unwrap(),
+ });
+ WebSocketService.Instance.send(wsClient.purgeCommunity(form));
+
+ i.state.purgeLoading = true;
+ i.setState(i.state);
+ }
}
PostReportResponse,
PostResponse,
PostView,
+ PurgeItemResponse,
SiteResponse,
SortType,
UserOperation,
if (data) {
toast(i18n.t("report_created"));
}
+ } else if (
+ op == UserOperation.PurgePerson ||
+ op == UserOperation.PurgePost ||
+ op == UserOperation.PurgeComment ||
+ op == UserOperation.PurgeCommunity
+ ) {
+ let data = wsJsonToRes<PurgeItemResponse>(msg, PurgeItemResponse);
+ if (data.success) {
+ toast(i18n.t("purge_success"));
+ this.context.router.history.push(`/`);
+ }
}
}
}
import { Component } from "inferno";
import { Link } from "inferno-router";
import {
+ AdminPurgeCommentView,
+ AdminPurgeCommunityView,
+ AdminPurgePersonView,
+ AdminPurgePostView,
CommunityModeratorView,
GetCommunity,
GetCommunityResponse,
ModTransferCommunity,
ModAdd,
ModBan,
+ AdminPurgePerson,
+ AdminPurgeCommunity,
+ AdminPurgePost,
+ AdminPurgeComment,
}
type ModlogType = {
id: number;
type_: ModlogEnum;
+ moderator: PersonSafe;
view:
| ModRemovePostView
| ModLockPostView
| ModBanView
| ModAddCommunityView
| ModTransferCommunityView
- | ModAddView;
+ | ModAddView
+ | AdminPurgePersonView
+ | AdminPurgeCommunityView
+ | AdminPurgePostView
+ | AdminPurgeCommentView;
when_: string;
};
if (this.isoData.path == this.context.router.route.match.url) {
this.state.res = Some(this.isoData.routeData[0] as GetModlogResponse);
- // Getting the moderators
- let communityRes = Some(
- this.isoData.routeData[1] as GetCommunityResponse
- );
- this.state.communityMods = communityRes.map(c => c.moderators);
+ if (this.isoData.routeData[1]) {
+ // Getting the moderators
+ let communityRes = Some(
+ this.isoData.routeData[1] as GetCommunityResponse
+ );
+ this.state.communityMods = communityRes.map(c => c.moderators);
+ }
this.state.loading = false;
} else {
id: r.mod_remove_post.id,
type_: ModlogEnum.ModRemovePost,
view: r,
+ moderator: r.moderator,
when_: r.mod_remove_post.when_,
}));
id: r.mod_lock_post.id,
type_: ModlogEnum.ModLockPost,
view: r,
+ moderator: r.moderator,
when_: r.mod_lock_post.when_,
}));
id: r.mod_sticky_post.id,
type_: ModlogEnum.ModStickyPost,
view: r,
+ moderator: r.moderator,
when_: r.mod_sticky_post.when_,
}));
id: r.mod_remove_comment.id,
type_: ModlogEnum.ModRemoveComment,
view: r,
+ moderator: r.moderator,
when_: r.mod_remove_comment.when_,
}));
id: r.mod_remove_community.id,
type_: ModlogEnum.ModRemoveCommunity,
view: r,
+ moderator: r.moderator,
when_: r.mod_remove_community.when_,
}));
id: r.mod_ban_from_community.id,
type_: ModlogEnum.ModBanFromCommunity,
view: r,
+ moderator: r.moderator,
when_: r.mod_ban_from_community.when_,
})
);
id: r.mod_add_community.id,
type_: ModlogEnum.ModAddCommunity,
view: r,
+ moderator: r.moderator,
when_: r.mod_add_community.when_,
}));
id: r.mod_transfer_community.id,
type_: ModlogEnum.ModTransferCommunity,
view: r,
+ moderator: r.moderator,
when_: r.mod_transfer_community.when_,
}));
id: r.mod_add.id,
type_: ModlogEnum.ModAdd,
view: r,
+ moderator: r.moderator,
when_: r.mod_add.when_,
}));
id: r.mod_ban.id,
type_: ModlogEnum.ModBan,
view: r,
+ moderator: r.moderator,
when_: r.mod_ban.when_,
}));
+ let purged_persons: ModlogType[] = res.admin_purged_persons.map(r => ({
+ id: r.admin_purge_person.id,
+ type_: ModlogEnum.AdminPurgePerson,
+ view: r,
+ moderator: r.admin,
+ when_: r.admin_purge_person.when_,
+ }));
+
+ let purged_communities: ModlogType[] = res.admin_purged_communities.map(
+ r => ({
+ id: r.admin_purge_community.id,
+ type_: ModlogEnum.AdminPurgeCommunity,
+ view: r,
+ moderator: r.admin,
+ when_: r.admin_purge_community.when_,
+ })
+ );
+
+ let purged_posts: ModlogType[] = res.admin_purged_posts.map(r => ({
+ id: r.admin_purge_post.id,
+ type_: ModlogEnum.AdminPurgePost,
+ view: r,
+ moderator: r.admin,
+ when_: r.admin_purge_post.when_,
+ }));
+
+ let purged_comments: ModlogType[] = res.admin_purged_comments.map(r => ({
+ id: r.admin_purge_comment.id,
+ type_: ModlogEnum.AdminPurgeComment,
+ view: r,
+ moderator: r.admin,
+ when_: r.admin_purge_comment.when_,
+ }));
+
let combined: ModlogType[] = [];
combined.push(...removed_posts);
combined.push(...transferred_to_community);
combined.push(...added);
combined.push(...banned);
+ combined.push(...purged_persons);
+ combined.push(...purged_communities);
+ combined.push(...purged_posts);
+ combined.push(...purged_comments);
// Sort them by time
combined.sort((a, b) => b.when_.localeCompare(a.when_));
case ModlogEnum.ModRemovePost: {
let mrpv = i.view as ModRemovePostView;
return [
- mrpv.mod_remove_post.removed ? "Removed " : "Restored ",
+ mrpv.mod_remove_post.removed.unwrapOr(false)
+ ? "Removed "
+ : "Restored ",
<span>
Post <Link to={`/post/${mrpv.post.id}`}>{mrpv.post.name}</Link>
</span>,
- mrpv.mod_remove_post.reason &&
- ` reason: ${mrpv.mod_remove_post.reason}`,
+ mrpv.mod_remove_post.reason.match({
+ some: reason => <div>reason: {reason}</div>,
+ none: <></>,
+ }),
];
}
case ModlogEnum.ModLockPost: {
let mlpv = i.view as ModLockPostView;
return [
- mlpv.mod_lock_post.locked ? "Locked " : "Unlocked ",
+ mlpv.mod_lock_post.locked.unwrapOr(false) ? "Locked " : "Unlocked ",
<span>
Post <Link to={`/post/${mlpv.post.id}`}>{mlpv.post.name}</Link>
</span>,
case ModlogEnum.ModStickyPost: {
let mspv = i.view as ModStickyPostView;
return [
- mspv.mod_sticky_post.stickied ? "Stickied " : "Unstickied ",
+ mspv.mod_sticky_post.stickied.unwrapOr(false)
+ ? "Stickied "
+ : "Unstickied ",
<span>
Post <Link to={`/post/${mspv.post.id}`}>{mspv.post.name}</Link>
</span>,
case ModlogEnum.ModRemoveComment: {
let mrc = i.view as ModRemoveCommentView;
return [
- mrc.mod_remove_comment.removed ? "Removed " : "Restored ",
+ mrc.mod_remove_comment.removed.unwrapOr(false)
+ ? "Removed "
+ : "Restored ",
<span>
Comment{" "}
<Link to={`/post/${mrc.post.id}/comment/${mrc.comment.id}`}>
{" "}
by <PersonListing person={mrc.commenter} />
</span>,
- mrc.mod_remove_comment.reason &&
- ` reason: ${mrc.mod_remove_comment.reason}`,
+ mrc.mod_remove_comment.reason.match({
+ some: reason => <div>reason: {reason}</div>,
+ none: <></>,
+ }),
];
}
case ModlogEnum.ModRemoveCommunity: {
let mrco = i.view as ModRemoveCommunityView;
return [
- mrco.mod_remove_community.removed ? "Removed " : "Restored ",
+ mrco.mod_remove_community.removed.unwrapOr(false)
+ ? "Removed "
+ : "Restored ",
<span>
Community <CommunityLink community={mrco.community} />
</span>,
- mrco.mod_remove_community.reason.isSome() &&
- ` reason: ${mrco.mod_remove_community.reason.unwrap()}`,
- mrco.mod_remove_community.expires.isSome() &&
- ` expires: ${moment
- .utc(mrco.mod_remove_community.expires.unwrap())
- .fromNow()}`,
+ mrco.mod_remove_community.reason.match({
+ some: reason => <div>reason: {reason}</div>,
+ none: <></>,
+ }),
+ mrco.mod_remove_community.expires.match({
+ some: expires => (
+ <div>expires: {moment.utc(expires).fromNow()}</div>
+ ),
+ none: <></>,
+ }),
];
}
case ModlogEnum.ModBanFromCommunity: {
let mbfc = i.view as ModBanFromCommunityView;
return [
<span>
- {mbfc.mod_ban_from_community.banned ? "Banned " : "Unbanned "}{" "}
+ {mbfc.mod_ban_from_community.banned.unwrapOr(false)
+ ? "Banned "
+ : "Unbanned "}{" "}
</span>,
<span>
<PersonListing person={mbfc.banned_person} />
<span>
<CommunityLink community={mbfc.community} />
</span>,
- <div>
- {mbfc.mod_ban_from_community.reason.isSome() &&
- ` reason: ${mbfc.mod_ban_from_community.reason.unwrap()}`}
- </div>,
- <div>
- {mbfc.mod_ban_from_community.expires.isSome() &&
- ` expires: ${moment
- .utc(mbfc.mod_ban_from_community.expires.unwrap())
- .fromNow()}`}
- </div>,
+ mbfc.mod_ban_from_community.reason.match({
+ some: reason => <div>reason: {reason}</div>,
+ none: <></>,
+ }),
+ mbfc.mod_ban_from_community.expires.match({
+ some: expires => (
+ <div>expires: {moment.utc(expires).fromNow()}</div>
+ ),
+ none: <></>,
+ }),
];
}
case ModlogEnum.ModAddCommunity: {
let mac = i.view as ModAddCommunityView;
return [
<span>
- {mac.mod_add_community.removed ? "Removed " : "Appointed "}{" "}
+ {mac.mod_add_community.removed.unwrapOr(false)
+ ? "Removed "
+ : "Appointed "}{" "}
</span>,
<span>
<PersonListing person={mac.modded_person} />
let mtc = i.view as ModTransferCommunityView;
return [
<span>
- {mtc.mod_transfer_community.removed ? "Removed " : "Transferred "}{" "}
+ {mtc.mod_transfer_community.removed.unwrapOr(false)
+ ? "Removed "
+ : "Transferred "}{" "}
</span>,
<span>
<CommunityLink community={mtc.community} />
case ModlogEnum.ModBan: {
let mb = i.view as ModBanView;
return [
- <span>{mb.mod_ban.banned ? "Banned " : "Unbanned "} </span>,
+ <span>
+ {mb.mod_ban.banned.unwrapOr(false) ? "Banned " : "Unbanned "}{" "}
+ </span>,
<span>
<PersonListing person={mb.banned_person} />
</span>,
- <div>
- {mb.mod_ban.reason.isSome() &&
- ` reason: ${mb.mod_ban.reason.unwrap()}`}
- </div>,
- <div>
- {mb.mod_ban.expires.isSome() &&
- ` expires: ${moment.utc(mb.mod_ban.expires.unwrap()).fromNow()}`}
- </div>,
+ mb.mod_ban.reason.match({
+ some: reason => <div>reason: {reason}</div>,
+ none: <></>,
+ }),
+ mb.mod_ban.expires.match({
+ some: expires => (
+ <div>expires: {moment.utc(expires).fromNow()}</div>
+ ),
+ none: <></>,
+ }),
];
}
case ModlogEnum.ModAdd: {
let ma = i.view as ModAddView;
return [
<span>
- {ma.mod_add.removed.isSome() && ma.mod_add.removed.unwrap()
- ? "Removed "
- : "Appointed "}{" "}
+ {ma.mod_add.removed.unwrapOr(false) ? "Removed " : "Appointed "}{" "}
</span>,
<span>
<PersonListing person={ma.modded_person} />
<span> as an admin </span>,
];
}
+ case ModlogEnum.AdminPurgePerson: {
+ let ap = i.view as AdminPurgePersonView;
+ return [
+ <span>Purged a Person</span>,
+ ap.admin_purge_person.reason.match({
+ some: reason => <div>reason: {reason}</div>,
+ none: <></>,
+ }),
+ ];
+ }
+ case ModlogEnum.AdminPurgeCommunity: {
+ let ap = i.view as AdminPurgeCommunityView;
+ return [
+ <span>Purged a Community</span>,
+ ap.admin_purge_community.reason.match({
+ some: reason => <div>reason: {reason}</div>,
+ none: <></>,
+ }),
+ ];
+ }
+ case ModlogEnum.AdminPurgePost: {
+ let ap = i.view as AdminPurgePostView;
+ return [
+ <span>Purged a Post from from </span>,
+ <CommunityLink community={ap.community} />,
+ ap.admin_purge_post.reason.match({
+ some: reason => <div>reason: {reason}</div>,
+ none: <></>,
+ }),
+ ];
+ }
+ case ModlogEnum.AdminPurgeComment: {
+ let ap = i.view as AdminPurgeCommentView;
+ return [
+ <span>
+ Purged a Comment from{" "}
+ <Link to={`/post/${ap.post.id}`}>{ap.post.name}</Link>
+ </span>,
+ ap.admin_purge_comment.reason.match({
+ some: reason => <div>reason: {reason}</div>,
+ none: <></>,
+ }),
+ ];
+ }
default:
return <div />;
}
</td>
<td>
{this.amAdminOrMod ? (
- <PersonListing person={i.view.moderator} />
+ <PersonListing person={i.moderator} />
) : (
- <div>{this.modOrAdminText(i.view.moderator)}</div>
+ <div>{this.modOrAdminText(i.moderator)}</div>
)}
</td>
<td>{this.renderModlogType(i)}</td>
);
}
- modOrAdminText(person: PersonSafe): Text {
+ modOrAdminText(person: PersonSafe): string {
if (
this.isoData.site_res.admins.map(a => a.person.id).includes(person.id)
) {
GetPersonDetailsResponse,
GetSiteResponse,
PostResponse,
+ PurgeItemResponse,
SortType,
toUndefined,
UserOperation,
updatePersonBlock(data);
this.setPersonBlock();
this.setState(this.state);
+ } else if (
+ op == UserOperation.PurgePerson ||
+ op == UserOperation.PurgePost ||
+ op == UserOperation.PurgeComment ||
+ op == UserOperation.PurgeCommunity
+ ) {
+ let data = wsJsonToRes<PurgeItemResponse>(msg, PurgeItemResponse);
+ if (data.success) {
+ toast(i18n.t("purge_success"));
+ this.context.router.history.push(`/`);
+ }
}
}
}
LockPost,
PersonViewSafe,
PostView,
+ PurgePerson,
+ PurgePost,
RemovePost,
SavePost,
StickyPost,
} from "lemmy-js-client";
import { externalHost } from "../../env";
import { i18n } from "../../i18next";
-import { BanType } from "../../interfaces";
+import { BanType, PurgeType } from "../../interfaces";
import { UserService, WebSocketService } from "../../services";
import {
amCommunityCreator,
showScores,
wsClient,
} from "../../utils";
-import { Icon } from "../common/icon";
+import { Icon, PurgeWarning, Spinner } from "../common/icon";
import { MomentTime } from "../common/moment-time";
import { PictrsImage } from "../common/pictrs-image";
import { CommunityLink } from "../community/community-link";
interface PostListingState {
showEdit: boolean;
showRemoveDialog: boolean;
+ showPurgeDialog: boolean;
+ purgeReason: Option<string>;
+ purgeType: PurgeType;
+ purgeLoading: boolean;
removeReason: Option<string>;
showBanDialog: boolean;
banReason: Option<string>;
private emptyState: PostListingState = {
showEdit: false,
showRemoveDialog: false,
+ showPurgeDialog: false,
+ purgeReason: None,
+ purgeType: PurgeType.Person,
+ purgeLoading: false,
removeReason: None,
showBanDialog: false,
banReason: None,
{/* Admins can ban from all, and appoint other admins */}
{this.canAdmin_ && (
<>
- {!this.creatorIsAdmin_ &&
- (!isBanned(post_view.creator) ? (
+ {!this.creatorIsAdmin_ && (
+ <>
+ {!isBanned(post_view.creator) ? (
+ <button
+ class="btn btn-link btn-animate text-muted py-0"
+ onClick={linkEvent(this, this.handleModBanShow)}
+ aria-label={i18n.t("ban_from_site")}
+ >
+ {i18n.t("ban_from_site")}
+ </button>
+ ) : (
+ <button
+ class="btn btn-link btn-animate text-muted py-0"
+ onClick={linkEvent(this, this.handleModBanSubmit)}
+ aria-label={i18n.t("unban_from_site")}
+ >
+ {i18n.t("unban_from_site")}
+ </button>
+ )}
<button
class="btn btn-link btn-animate text-muted py-0"
- onClick={linkEvent(this, this.handleModBanShow)}
- aria-label={i18n.t("ban_from_site")}
+ onClick={linkEvent(this, this.handlePurgePersonShow)}
+ aria-label={i18n.t("purge_user")}
>
- {i18n.t("ban_from_site")}
+ {i18n.t("purge_user")}
</button>
- ) : (
<button
class="btn btn-link btn-animate text-muted py-0"
- onClick={linkEvent(this, this.handleModBanSubmit)}
- aria-label={i18n.t("unban_from_site")}
+ onClick={linkEvent(this, this.handlePurgePostShow)}
+ aria-label={i18n.t("purge_post")}
>
- {i18n.t("unban_from_site")}
+ {i18n.t("purge_post")}
</button>
- ))}
+ </>
+ )}
{!isBanned(post_view.creator) && post_view.creator.local && (
<button
class="btn btn-link btn-animate text-muted py-0"
removeAndBanDialogs() {
let post = this.props.post_view;
+ let purgeTypeText: string;
+ if (this.state.purgeType == PurgeType.Post) {
+ purgeTypeText = i18n.t("purge_post");
+ } else if (this.state.purgeType == PurgeType.Person) {
+ purgeTypeText = `${i18n.t("purge")} ${post.creator.name}`;
+ }
return (
<>
{this.state.showRemoveDialog && (
</button>
</form>
)}
+ {this.state.showPurgeDialog && (
+ <form
+ class="form-inline"
+ onSubmit={linkEvent(this, this.handlePurgeSubmit)}
+ >
+ <PurgeWarning />
+ <label class="sr-only" htmlFor="purge-reason">
+ {i18n.t("reason")}
+ </label>
+ <input
+ type="text"
+ id="purge-reason"
+ class="form-control mr-2"
+ placeholder={i18n.t("reason")}
+ value={toUndefined(this.state.purgeReason)}
+ onInput={linkEvent(this, this.handlePurgeReasonChange)}
+ />
+ {this.state.purgeLoading ? (
+ <Spinner />
+ ) : (
+ <button
+ type="submit"
+ class="btn btn-secondary"
+ aria-label={purgeTypeText}
+ >
+ {purgeTypeText}
+ </button>
+ )}
+ </form>
+ )}
</>
);
}
i.setState(i.state);
}
+ handlePurgePersonShow(i: PostListing) {
+ i.state.showPurgeDialog = true;
+ i.state.purgeType = PurgeType.Person;
+ i.state.showRemoveDialog = false;
+ i.setState(i.state);
+ }
+
+ handlePurgePostShow(i: PostListing) {
+ i.state.showPurgeDialog = true;
+ i.state.purgeType = PurgeType.Post;
+ i.state.showRemoveDialog = false;
+ i.setState(i.state);
+ }
+
+ handlePurgeReasonChange(i: PostListing, event: any) {
+ i.state.purgeReason = Some(event.target.value);
+ i.setState(i.state);
+ }
+
+ handlePurgeSubmit(i: PostListing, event: any) {
+ event.preventDefault();
+
+ if (i.state.purgeType == PurgeType.Person) {
+ let form = new PurgePerson({
+ person_id: i.props.post_view.creator.id,
+ reason: i.state.purgeReason,
+ auth: auth().unwrap(),
+ });
+ WebSocketService.Instance.send(wsClient.purgePerson(form));
+ } else if (i.state.purgeType == PurgeType.Post) {
+ let form = new PurgePost({
+ post_id: i.props.post_view.post.id,
+ reason: i.state.purgeReason,
+ auth: auth().unwrap(),
+ });
+ WebSocketService.Instance.send(wsClient.purgePost(form));
+ }
+
+ i.state.purgeLoading = true;
+ i.setState(i.state);
+ }
+
handleModBanReasonChange(i: PostListing, event: any) {
i.state.banReason = Some(event.target.value);
i.setState(i.state);
PostReportResponse,
PostResponse,
PostView,
+ PurgeItemResponse,
Search,
SearchResponse,
SearchType,
if (data) {
toast(i18n.t("report_created"));
}
+ } else if (
+ op == UserOperation.PurgePerson ||
+ op == UserOperation.PurgePost ||
+ op == UserOperation.PurgeComment ||
+ op == UserOperation.PurgeCommunity
+ ) {
+ let data = wsJsonToRes<PurgeItemResponse>(msg, PurgeItemResponse);
+ if (data.success) {
+ toast(i18n.t("purge_success"));
+ this.context.router.history.push(`/`);
+ }
}
}
}
Posts,
Saved,
}
+
+export enum PurgeType {
+ Person,
+ Community,
+ Post,
+ Comment,
+}
dependencies:
invert-kv "^1.0.0"
-lemmy-js-client@0.17.0-rc.32:
- version "0.17.0-rc.32"
- resolved "https://registry.yarnpkg.com/lemmy-js-client/-/lemmy-js-client-0.17.0-rc.32.tgz#d67f432f1fffc54c267f915278fe260ec554b018"
- integrity sha512-qPLybaesu3GVr1DMStsyCYanW4maxHrqX71UHadFMeuh+aUK8taC3zfsLRK9dlIlSDRS283xd8IZkI6ZlcOVEQ==
+lemmy-js-client@0.17.0-rc.33:
+ version "0.17.0-rc.33"
+ resolved "https://registry.yarnpkg.com/lemmy-js-client/-/lemmy-js-client-0.17.0-rc.33.tgz#e05cc88213da3c0c21c7ea53c29054041d619150"
+ integrity sha512-rG0yCc9AAc5/B+muDfWB7bKizBG7r/xSzHeEw5ms50xF4dN+KOqvRcHTf0+15uAYehBF5B54nyxdlKPRKL9GxQ==
levn@^0.4.1:
version "0.4.1"