mirror of
https://github.com/LemmyNet/lemmy-ui.git
synced 2024-11-26 06:11:15 +00:00
Adding purging of comments, posts, communities, and users. (#459)
* Starting on admin purge. * Updating translations. * Finishing up item purging.
This commit is contained in:
parent
75d52f1e4e
commit
96583bee47
13 changed files with 578 additions and 88 deletions
|
@ -1 +1 @@
|
||||||
Subproject commit 29c689af8d16417c1b84d9491f6bcea888720a87
|
Subproject commit de5d4f3a758f8e8b41869c90d97e53ab50577f90
|
|
@ -77,7 +77,7 @@
|
||||||
"eslint-plugin-prettier": "^4.0.0",
|
"eslint-plugin-prettier": "^4.0.0",
|
||||||
"husky": "^7.0.4",
|
"husky": "^7.0.4",
|
||||||
"import-sort-style-module": "^6.0.0",
|
"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",
|
"lint-staged": "^12.4.1",
|
||||||
"mini-css-extract-plugin": "^2.6.0",
|
"mini-css-extract-plugin": "^2.6.0",
|
||||||
"node-fetch": "^2.6.1",
|
"node-fetch": "^2.6.1",
|
||||||
|
|
|
@ -17,6 +17,8 @@ import {
|
||||||
MarkPersonMentionAsRead,
|
MarkPersonMentionAsRead,
|
||||||
PersonMentionView,
|
PersonMentionView,
|
||||||
PersonViewSafe,
|
PersonViewSafe,
|
||||||
|
PurgeComment,
|
||||||
|
PurgePerson,
|
||||||
RemoveComment,
|
RemoveComment,
|
||||||
SaveComment,
|
SaveComment,
|
||||||
toUndefined,
|
toUndefined,
|
||||||
|
@ -24,7 +26,11 @@ import {
|
||||||
} from "lemmy-js-client";
|
} from "lemmy-js-client";
|
||||||
import moment from "moment";
|
import moment from "moment";
|
||||||
import { i18n } from "../../i18next";
|
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 { UserService, WebSocketService } from "../../services";
|
||||||
import {
|
import {
|
||||||
amCommunityCreator,
|
amCommunityCreator,
|
||||||
|
@ -42,7 +48,7 @@ import {
|
||||||
showScores,
|
showScores,
|
||||||
wsClient,
|
wsClient,
|
||||||
} from "../../utils";
|
} from "../../utils";
|
||||||
import { Icon, Spinner } from "../common/icon";
|
import { Icon, PurgeWarning, Spinner } from "../common/icon";
|
||||||
import { MomentTime } from "../common/moment-time";
|
import { MomentTime } from "../common/moment-time";
|
||||||
import { CommunityLink } from "../community/community-link";
|
import { CommunityLink } from "../community/community-link";
|
||||||
import { PersonListing } from "../person/person-listing";
|
import { PersonListing } from "../person/person-listing";
|
||||||
|
@ -59,6 +65,10 @@ interface CommentNodeState {
|
||||||
banReason: Option<string>;
|
banReason: Option<string>;
|
||||||
banExpireDays: Option<number>;
|
banExpireDays: Option<number>;
|
||||||
banType: BanType;
|
banType: BanType;
|
||||||
|
showPurgeDialog: boolean;
|
||||||
|
purgeReason: Option<string>;
|
||||||
|
purgeType: PurgeType;
|
||||||
|
purgeLoading: boolean;
|
||||||
showConfirmTransferSite: boolean;
|
showConfirmTransferSite: boolean;
|
||||||
showConfirmTransferCommunity: boolean;
|
showConfirmTransferCommunity: boolean;
|
||||||
showConfirmAppointAsMod: boolean;
|
showConfirmAppointAsMod: boolean;
|
||||||
|
@ -102,6 +112,10 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
|
||||||
banReason: None,
|
banReason: None,
|
||||||
banExpireDays: None,
|
banExpireDays: None,
|
||||||
banType: BanType.Community,
|
banType: BanType.Community,
|
||||||
|
showPurgeDialog: false,
|
||||||
|
purgeLoading: false,
|
||||||
|
purgeReason: None,
|
||||||
|
purgeType: PurgeType.Person,
|
||||||
collapsed: false,
|
collapsed: false,
|
||||||
viewSource: false,
|
viewSource: false,
|
||||||
showAdvanced: false,
|
showAdvanced: false,
|
||||||
|
@ -147,6 +161,13 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
|
||||||
let node = this.props.node;
|
let node = this.props.node;
|
||||||
let cv = this.props.node.comment_view;
|
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(
|
let canMod_ = canMod(
|
||||||
this.props.moderators,
|
this.props.moderators,
|
||||||
this.props.admins,
|
this.props.admins,
|
||||||
|
@ -645,30 +666,54 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
|
||||||
{/* Admins can ban from all, and appoint other admins */}
|
{/* Admins can ban from all, and appoint other admins */}
|
||||||
{canAdmin_ && (
|
{canAdmin_ && (
|
||||||
<>
|
<>
|
||||||
{!isAdmin_ &&
|
{!isAdmin_ && (
|
||||||
(!isBanned(cv.creator) ? (
|
<>
|
||||||
<button
|
<button
|
||||||
class="btn btn-link btn-animate text-muted"
|
class="btn btn-link btn-animate text-muted"
|
||||||
onClick={linkEvent(
|
onClick={linkEvent(
|
||||||
this,
|
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>
|
||||||
) : (
|
|
||||||
<button
|
<button
|
||||||
class="btn btn-link btn-animate text-muted"
|
class="btn btn-link btn-animate text-muted"
|
||||||
onClick={linkEvent(
|
onClick={linkEvent(
|
||||||
this,
|
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>
|
</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) &&
|
{!isBanned(cv.creator) &&
|
||||||
cv.creator.local &&
|
cv.creator.local &&
|
||||||
(!this.state.showConfirmAppointAsAdmin ? (
|
(!this.state.showConfirmAppointAsAdmin ? (
|
||||||
|
@ -848,6 +893,36 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</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 && (
|
{this.state.showReply && (
|
||||||
<CommentForm
|
<CommentForm
|
||||||
node={Left(node)}
|
node={Left(node)}
|
||||||
|
@ -1202,6 +1277,48 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
|
||||||
i.setState(i.state);
|
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) {
|
handleShowConfirmAppointAsMod(i: CommentNode) {
|
||||||
i.state.showConfirmAppointAsMod = true;
|
i.state.showConfirmAppointAsMod = true;
|
||||||
i.setState(i.state);
|
i.setState(i.state);
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import classNames from "classnames";
|
import classNames from "classnames";
|
||||||
import { Component } from "inferno";
|
import { Component } from "inferno";
|
||||||
|
import { i18n } from "../../i18next";
|
||||||
|
|
||||||
interface IconProps {
|
interface IconProps {
|
||||||
icon: string;
|
icon: string;
|
||||||
|
@ -48,3 +49,18 @@ export class Spinner extends Component<SpinnerProps, any> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -19,6 +19,7 @@ import {
|
||||||
PostReportResponse,
|
PostReportResponse,
|
||||||
PostResponse,
|
PostResponse,
|
||||||
PostView,
|
PostView,
|
||||||
|
PurgeItemResponse,
|
||||||
SortType,
|
SortType,
|
||||||
toOption,
|
toOption,
|
||||||
UserOperation,
|
UserOperation,
|
||||||
|
@ -656,6 +657,12 @@ export class Community extends Component<any, State> {
|
||||||
if (data) {
|
if (data) {
|
||||||
toast(i18n.t("report_created"));
|
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(`/`);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { Option, Some } from "@sniptt/monads";
|
import { None, Option, Some } from "@sniptt/monads";
|
||||||
import { Component, linkEvent } from "inferno";
|
import { Component, linkEvent } from "inferno";
|
||||||
import { Link } from "inferno-router";
|
import { Link } from "inferno-router";
|
||||||
import {
|
import {
|
||||||
|
@ -8,6 +8,7 @@ import {
|
||||||
DeleteCommunity,
|
DeleteCommunity,
|
||||||
FollowCommunity,
|
FollowCommunity,
|
||||||
PersonViewSafe,
|
PersonViewSafe,
|
||||||
|
PurgeCommunity,
|
||||||
RemoveCommunity,
|
RemoveCommunity,
|
||||||
SubscribedType,
|
SubscribedType,
|
||||||
toUndefined,
|
toUndefined,
|
||||||
|
@ -25,7 +26,7 @@ import {
|
||||||
wsClient,
|
wsClient,
|
||||||
} from "../../utils";
|
} from "../../utils";
|
||||||
import { BannerIconHeader } from "../common/banner-icon-header";
|
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 { CommunityForm } from "../community/community-form";
|
||||||
import { CommunityLink } from "../community/community-link";
|
import { CommunityLink } from "../community/community-link";
|
||||||
import { PersonListing } from "../person/person-listing";
|
import { PersonListing } from "../person/person-listing";
|
||||||
|
@ -44,6 +45,9 @@ interface SidebarState {
|
||||||
removeExpires: Option<string>;
|
removeExpires: Option<string>;
|
||||||
showEdit: boolean;
|
showEdit: boolean;
|
||||||
showRemoveDialog: boolean;
|
showRemoveDialog: boolean;
|
||||||
|
showPurgeDialog: boolean;
|
||||||
|
purgeReason: Option<string>;
|
||||||
|
purgeLoading: boolean;
|
||||||
showConfirmLeaveModTeam: boolean;
|
showConfirmLeaveModTeam: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -51,8 +55,11 @@ export class Sidebar extends Component<SidebarProps, SidebarState> {
|
||||||
private emptyState: SidebarState = {
|
private emptyState: SidebarState = {
|
||||||
showEdit: false,
|
showEdit: false,
|
||||||
showRemoveDialog: false,
|
showRemoveDialog: false,
|
||||||
removeReason: null,
|
removeReason: None,
|
||||||
removeExpires: null,
|
removeExpires: None,
|
||||||
|
showPurgeDialog: false,
|
||||||
|
purgeReason: None,
|
||||||
|
purgeLoading: false,
|
||||||
showConfirmLeaveModTeam: false,
|
showConfirmLeaveModTeam: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -403,12 +410,19 @@ export class Sidebar extends Component<SidebarProps, SidebarState> {
|
||||||
{i18n.t("restore")}
|
{i18n.t("restore")}
|
||||||
</button>
|
</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>
|
</li>
|
||||||
)}
|
)}
|
||||||
</ul>
|
</ul>
|
||||||
{this.state.showRemoveDialog && (
|
{this.state.showRemoveDialog && (
|
||||||
<form onSubmit={linkEvent(this, this.handleModRemoveSubmit)}>
|
<form onSubmit={linkEvent(this, this.handleModRemoveSubmit)}>
|
||||||
<div class="form-group row">
|
<div class="form-group">
|
||||||
<label class="col-form-label" htmlFor="remove-reason">
|
<label class="col-form-label" htmlFor="remove-reason">
|
||||||
{i18n.t("reason")}
|
{i18n.t("reason")}
|
||||||
</label>
|
</label>
|
||||||
|
@ -426,13 +440,46 @@ export class Sidebar extends Component<SidebarProps, SidebarState> {
|
||||||
{/* <label class="col-form-label">Expires</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)} /> */}
|
{/* <input type="date" class="form-control mr-2" placeholder={i18n.t('expires')} value={this.state.removeExpires} onInput={linkEvent(this, this.handleModRemoveExpiresChange)} /> */}
|
||||||
{/* </div> */}
|
{/* </div> */}
|
||||||
<div class="form-group row">
|
<div class="form-group">
|
||||||
<button type="submit" class="btn btn-secondary">
|
<button type="submit" class="btn btn-secondary">
|
||||||
{i18n.t("remove_community")}
|
{i18n.t("remove_community")}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</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>
|
||||||
|
)}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -542,13 +589,12 @@ export class Sidebar extends Component<SidebarProps, SidebarState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
handleModRemoveReasonChange(i: Sidebar, event: any) {
|
handleModRemoveReasonChange(i: Sidebar, event: any) {
|
||||||
i.state.removeReason = event.target.value;
|
i.state.removeReason = Some(event.target.value);
|
||||||
i.setState(i.state);
|
i.setState(i.state);
|
||||||
}
|
}
|
||||||
|
|
||||||
handleModRemoveExpiresChange(i: Sidebar, event: any) {
|
handleModRemoveExpiresChange(i: Sidebar, event: any) {
|
||||||
console.log(event.target.value);
|
i.state.removeExpires = Some(event.target.value);
|
||||||
i.state.removeExpires = event.target.value;
|
|
||||||
i.setState(i.state);
|
i.setState(i.state);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -566,4 +612,29 @@ export class Sidebar extends Component<SidebarProps, SidebarState> {
|
||||||
i.state.showRemoveDialog = false;
|
i.state.showRemoveDialog = false;
|
||||||
i.setState(i.state);
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,6 +21,7 @@ import {
|
||||||
PostReportResponse,
|
PostReportResponse,
|
||||||
PostResponse,
|
PostResponse,
|
||||||
PostView,
|
PostView,
|
||||||
|
PurgeItemResponse,
|
||||||
SiteResponse,
|
SiteResponse,
|
||||||
SortType,
|
SortType,
|
||||||
UserOperation,
|
UserOperation,
|
||||||
|
@ -860,6 +861,17 @@ export class Home extends Component<any, HomeState> {
|
||||||
if (data) {
|
if (data) {
|
||||||
toast(i18n.t("report_created"));
|
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(`/`);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,10 @@ import { None, Option, Some } from "@sniptt/monads";
|
||||||
import { Component } from "inferno";
|
import { Component } from "inferno";
|
||||||
import { Link } from "inferno-router";
|
import { Link } from "inferno-router";
|
||||||
import {
|
import {
|
||||||
|
AdminPurgeCommentView,
|
||||||
|
AdminPurgeCommunityView,
|
||||||
|
AdminPurgePersonView,
|
||||||
|
AdminPurgePostView,
|
||||||
CommunityModeratorView,
|
CommunityModeratorView,
|
||||||
GetCommunity,
|
GetCommunity,
|
||||||
GetCommunityResponse,
|
GetCommunityResponse,
|
||||||
|
@ -57,11 +61,16 @@ enum ModlogEnum {
|
||||||
ModTransferCommunity,
|
ModTransferCommunity,
|
||||||
ModAdd,
|
ModAdd,
|
||||||
ModBan,
|
ModBan,
|
||||||
|
AdminPurgePerson,
|
||||||
|
AdminPurgeCommunity,
|
||||||
|
AdminPurgePost,
|
||||||
|
AdminPurgeComment,
|
||||||
}
|
}
|
||||||
|
|
||||||
type ModlogType = {
|
type ModlogType = {
|
||||||
id: number;
|
id: number;
|
||||||
type_: ModlogEnum;
|
type_: ModlogEnum;
|
||||||
|
moderator: PersonSafe;
|
||||||
view:
|
view:
|
||||||
| ModRemovePostView
|
| ModRemovePostView
|
||||||
| ModLockPostView
|
| ModLockPostView
|
||||||
|
@ -72,7 +81,11 @@ type ModlogType = {
|
||||||
| ModBanView
|
| ModBanView
|
||||||
| ModAddCommunityView
|
| ModAddCommunityView
|
||||||
| ModTransferCommunityView
|
| ModTransferCommunityView
|
||||||
| ModAddView;
|
| ModAddView
|
||||||
|
| AdminPurgePersonView
|
||||||
|
| AdminPurgeCommunityView
|
||||||
|
| AdminPurgePostView
|
||||||
|
| AdminPurgeCommentView;
|
||||||
when_: string;
|
when_: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -118,11 +131,13 @@ export class Modlog extends Component<any, ModlogState> {
|
||||||
if (this.isoData.path == this.context.router.route.match.url) {
|
if (this.isoData.path == this.context.router.route.match.url) {
|
||||||
this.state.res = Some(this.isoData.routeData[0] as GetModlogResponse);
|
this.state.res = Some(this.isoData.routeData[0] as GetModlogResponse);
|
||||||
|
|
||||||
// Getting the moderators
|
if (this.isoData.routeData[1]) {
|
||||||
let communityRes = Some(
|
// Getting the moderators
|
||||||
this.isoData.routeData[1] as GetCommunityResponse
|
let communityRes = Some(
|
||||||
);
|
this.isoData.routeData[1] as GetCommunityResponse
|
||||||
this.state.communityMods = communityRes.map(c => c.moderators);
|
);
|
||||||
|
this.state.communityMods = communityRes.map(c => c.moderators);
|
||||||
|
}
|
||||||
|
|
||||||
this.state.loading = false;
|
this.state.loading = false;
|
||||||
} else {
|
} else {
|
||||||
|
@ -141,6 +156,7 @@ export class Modlog extends Component<any, ModlogState> {
|
||||||
id: r.mod_remove_post.id,
|
id: r.mod_remove_post.id,
|
||||||
type_: ModlogEnum.ModRemovePost,
|
type_: ModlogEnum.ModRemovePost,
|
||||||
view: r,
|
view: r,
|
||||||
|
moderator: r.moderator,
|
||||||
when_: r.mod_remove_post.when_,
|
when_: r.mod_remove_post.when_,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
@ -148,6 +164,7 @@ export class Modlog extends Component<any, ModlogState> {
|
||||||
id: r.mod_lock_post.id,
|
id: r.mod_lock_post.id,
|
||||||
type_: ModlogEnum.ModLockPost,
|
type_: ModlogEnum.ModLockPost,
|
||||||
view: r,
|
view: r,
|
||||||
|
moderator: r.moderator,
|
||||||
when_: r.mod_lock_post.when_,
|
when_: r.mod_lock_post.when_,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
@ -155,6 +172,7 @@ export class Modlog extends Component<any, ModlogState> {
|
||||||
id: r.mod_sticky_post.id,
|
id: r.mod_sticky_post.id,
|
||||||
type_: ModlogEnum.ModStickyPost,
|
type_: ModlogEnum.ModStickyPost,
|
||||||
view: r,
|
view: r,
|
||||||
|
moderator: r.moderator,
|
||||||
when_: r.mod_sticky_post.when_,
|
when_: r.mod_sticky_post.when_,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
@ -162,6 +180,7 @@ export class Modlog extends Component<any, ModlogState> {
|
||||||
id: r.mod_remove_comment.id,
|
id: r.mod_remove_comment.id,
|
||||||
type_: ModlogEnum.ModRemoveComment,
|
type_: ModlogEnum.ModRemoveComment,
|
||||||
view: r,
|
view: r,
|
||||||
|
moderator: r.moderator,
|
||||||
when_: r.mod_remove_comment.when_,
|
when_: r.mod_remove_comment.when_,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
@ -169,6 +188,7 @@ export class Modlog extends Component<any, ModlogState> {
|
||||||
id: r.mod_remove_community.id,
|
id: r.mod_remove_community.id,
|
||||||
type_: ModlogEnum.ModRemoveCommunity,
|
type_: ModlogEnum.ModRemoveCommunity,
|
||||||
view: r,
|
view: r,
|
||||||
|
moderator: r.moderator,
|
||||||
when_: r.mod_remove_community.when_,
|
when_: r.mod_remove_community.when_,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
@ -177,6 +197,7 @@ export class Modlog extends Component<any, ModlogState> {
|
||||||
id: r.mod_ban_from_community.id,
|
id: r.mod_ban_from_community.id,
|
||||||
type_: ModlogEnum.ModBanFromCommunity,
|
type_: ModlogEnum.ModBanFromCommunity,
|
||||||
view: r,
|
view: r,
|
||||||
|
moderator: r.moderator,
|
||||||
when_: r.mod_ban_from_community.when_,
|
when_: r.mod_ban_from_community.when_,
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
@ -185,6 +206,7 @@ export class Modlog extends Component<any, ModlogState> {
|
||||||
id: r.mod_add_community.id,
|
id: r.mod_add_community.id,
|
||||||
type_: ModlogEnum.ModAddCommunity,
|
type_: ModlogEnum.ModAddCommunity,
|
||||||
view: r,
|
view: r,
|
||||||
|
moderator: r.moderator,
|
||||||
when_: r.mod_add_community.when_,
|
when_: r.mod_add_community.when_,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
@ -193,6 +215,7 @@ export class Modlog extends Component<any, ModlogState> {
|
||||||
id: r.mod_transfer_community.id,
|
id: r.mod_transfer_community.id,
|
||||||
type_: ModlogEnum.ModTransferCommunity,
|
type_: ModlogEnum.ModTransferCommunity,
|
||||||
view: r,
|
view: r,
|
||||||
|
moderator: r.moderator,
|
||||||
when_: r.mod_transfer_community.when_,
|
when_: r.mod_transfer_community.when_,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
@ -200,6 +223,7 @@ export class Modlog extends Component<any, ModlogState> {
|
||||||
id: r.mod_add.id,
|
id: r.mod_add.id,
|
||||||
type_: ModlogEnum.ModAdd,
|
type_: ModlogEnum.ModAdd,
|
||||||
view: r,
|
view: r,
|
||||||
|
moderator: r.moderator,
|
||||||
when_: r.mod_add.when_,
|
when_: r.mod_add.when_,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
@ -207,9 +231,44 @@ export class Modlog extends Component<any, ModlogState> {
|
||||||
id: r.mod_ban.id,
|
id: r.mod_ban.id,
|
||||||
type_: ModlogEnum.ModBan,
|
type_: ModlogEnum.ModBan,
|
||||||
view: r,
|
view: r,
|
||||||
|
moderator: r.moderator,
|
||||||
when_: r.mod_ban.when_,
|
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[] = [];
|
let combined: ModlogType[] = [];
|
||||||
|
|
||||||
combined.push(...removed_posts);
|
combined.push(...removed_posts);
|
||||||
|
@ -222,6 +281,10 @@ export class Modlog extends Component<any, ModlogState> {
|
||||||
combined.push(...transferred_to_community);
|
combined.push(...transferred_to_community);
|
||||||
combined.push(...added);
|
combined.push(...added);
|
||||||
combined.push(...banned);
|
combined.push(...banned);
|
||||||
|
combined.push(...purged_persons);
|
||||||
|
combined.push(...purged_communities);
|
||||||
|
combined.push(...purged_posts);
|
||||||
|
combined.push(...purged_comments);
|
||||||
|
|
||||||
// Sort them by time
|
// Sort them by time
|
||||||
combined.sort((a, b) => b.when_.localeCompare(a.when_));
|
combined.sort((a, b) => b.when_.localeCompare(a.when_));
|
||||||
|
@ -234,18 +297,22 @@ export class Modlog extends Component<any, ModlogState> {
|
||||||
case ModlogEnum.ModRemovePost: {
|
case ModlogEnum.ModRemovePost: {
|
||||||
let mrpv = i.view as ModRemovePostView;
|
let mrpv = i.view as ModRemovePostView;
|
||||||
return [
|
return [
|
||||||
mrpv.mod_remove_post.removed ? "Removed " : "Restored ",
|
mrpv.mod_remove_post.removed.unwrapOr(false)
|
||||||
|
? "Removed "
|
||||||
|
: "Restored ",
|
||||||
<span>
|
<span>
|
||||||
Post <Link to={`/post/${mrpv.post.id}`}>{mrpv.post.name}</Link>
|
Post <Link to={`/post/${mrpv.post.id}`}>{mrpv.post.name}</Link>
|
||||||
</span>,
|
</span>,
|
||||||
mrpv.mod_remove_post.reason &&
|
mrpv.mod_remove_post.reason.match({
|
||||||
` reason: ${mrpv.mod_remove_post.reason}`,
|
some: reason => <div>reason: {reason}</div>,
|
||||||
|
none: <></>,
|
||||||
|
}),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
case ModlogEnum.ModLockPost: {
|
case ModlogEnum.ModLockPost: {
|
||||||
let mlpv = i.view as ModLockPostView;
|
let mlpv = i.view as ModLockPostView;
|
||||||
return [
|
return [
|
||||||
mlpv.mod_lock_post.locked ? "Locked " : "Unlocked ",
|
mlpv.mod_lock_post.locked.unwrapOr(false) ? "Locked " : "Unlocked ",
|
||||||
<span>
|
<span>
|
||||||
Post <Link to={`/post/${mlpv.post.id}`}>{mlpv.post.name}</Link>
|
Post <Link to={`/post/${mlpv.post.id}`}>{mlpv.post.name}</Link>
|
||||||
</span>,
|
</span>,
|
||||||
|
@ -254,7 +321,9 @@ export class Modlog extends Component<any, ModlogState> {
|
||||||
case ModlogEnum.ModStickyPost: {
|
case ModlogEnum.ModStickyPost: {
|
||||||
let mspv = i.view as ModStickyPostView;
|
let mspv = i.view as ModStickyPostView;
|
||||||
return [
|
return [
|
||||||
mspv.mod_sticky_post.stickied ? "Stickied " : "Unstickied ",
|
mspv.mod_sticky_post.stickied.unwrapOr(false)
|
||||||
|
? "Stickied "
|
||||||
|
: "Unstickied ",
|
||||||
<span>
|
<span>
|
||||||
Post <Link to={`/post/${mspv.post.id}`}>{mspv.post.name}</Link>
|
Post <Link to={`/post/${mspv.post.id}`}>{mspv.post.name}</Link>
|
||||||
</span>,
|
</span>,
|
||||||
|
@ -263,7 +332,9 @@ export class Modlog extends Component<any, ModlogState> {
|
||||||
case ModlogEnum.ModRemoveComment: {
|
case ModlogEnum.ModRemoveComment: {
|
||||||
let mrc = i.view as ModRemoveCommentView;
|
let mrc = i.view as ModRemoveCommentView;
|
||||||
return [
|
return [
|
||||||
mrc.mod_remove_comment.removed ? "Removed " : "Restored ",
|
mrc.mod_remove_comment.removed.unwrapOr(false)
|
||||||
|
? "Removed "
|
||||||
|
: "Restored ",
|
||||||
<span>
|
<span>
|
||||||
Comment{" "}
|
Comment{" "}
|
||||||
<Link to={`/post/${mrc.post.id}/comment/${mrc.comment.id}`}>
|
<Link to={`/post/${mrc.post.id}/comment/${mrc.comment.id}`}>
|
||||||
|
@ -274,30 +345,40 @@ export class Modlog extends Component<any, ModlogState> {
|
||||||
{" "}
|
{" "}
|
||||||
by <PersonListing person={mrc.commenter} />
|
by <PersonListing person={mrc.commenter} />
|
||||||
</span>,
|
</span>,
|
||||||
mrc.mod_remove_comment.reason &&
|
mrc.mod_remove_comment.reason.match({
|
||||||
` reason: ${mrc.mod_remove_comment.reason}`,
|
some: reason => <div>reason: {reason}</div>,
|
||||||
|
none: <></>,
|
||||||
|
}),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
case ModlogEnum.ModRemoveCommunity: {
|
case ModlogEnum.ModRemoveCommunity: {
|
||||||
let mrco = i.view as ModRemoveCommunityView;
|
let mrco = i.view as ModRemoveCommunityView;
|
||||||
return [
|
return [
|
||||||
mrco.mod_remove_community.removed ? "Removed " : "Restored ",
|
mrco.mod_remove_community.removed.unwrapOr(false)
|
||||||
|
? "Removed "
|
||||||
|
: "Restored ",
|
||||||
<span>
|
<span>
|
||||||
Community <CommunityLink community={mrco.community} />
|
Community <CommunityLink community={mrco.community} />
|
||||||
</span>,
|
</span>,
|
||||||
mrco.mod_remove_community.reason.isSome() &&
|
mrco.mod_remove_community.reason.match({
|
||||||
` reason: ${mrco.mod_remove_community.reason.unwrap()}`,
|
some: reason => <div>reason: {reason}</div>,
|
||||||
mrco.mod_remove_community.expires.isSome() &&
|
none: <></>,
|
||||||
` expires: ${moment
|
}),
|
||||||
.utc(mrco.mod_remove_community.expires.unwrap())
|
mrco.mod_remove_community.expires.match({
|
||||||
.fromNow()}`,
|
some: expires => (
|
||||||
|
<div>expires: {moment.utc(expires).fromNow()}</div>
|
||||||
|
),
|
||||||
|
none: <></>,
|
||||||
|
}),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
case ModlogEnum.ModBanFromCommunity: {
|
case ModlogEnum.ModBanFromCommunity: {
|
||||||
let mbfc = i.view as ModBanFromCommunityView;
|
let mbfc = i.view as ModBanFromCommunityView;
|
||||||
return [
|
return [
|
||||||
<span>
|
<span>
|
||||||
{mbfc.mod_ban_from_community.banned ? "Banned " : "Unbanned "}{" "}
|
{mbfc.mod_ban_from_community.banned.unwrapOr(false)
|
||||||
|
? "Banned "
|
||||||
|
: "Unbanned "}{" "}
|
||||||
</span>,
|
</span>,
|
||||||
<span>
|
<span>
|
||||||
<PersonListing person={mbfc.banned_person} />
|
<PersonListing person={mbfc.banned_person} />
|
||||||
|
@ -306,23 +387,25 @@ export class Modlog extends Component<any, ModlogState> {
|
||||||
<span>
|
<span>
|
||||||
<CommunityLink community={mbfc.community} />
|
<CommunityLink community={mbfc.community} />
|
||||||
</span>,
|
</span>,
|
||||||
<div>
|
mbfc.mod_ban_from_community.reason.match({
|
||||||
{mbfc.mod_ban_from_community.reason.isSome() &&
|
some: reason => <div>reason: {reason}</div>,
|
||||||
` reason: ${mbfc.mod_ban_from_community.reason.unwrap()}`}
|
none: <></>,
|
||||||
</div>,
|
}),
|
||||||
<div>
|
mbfc.mod_ban_from_community.expires.match({
|
||||||
{mbfc.mod_ban_from_community.expires.isSome() &&
|
some: expires => (
|
||||||
` expires: ${moment
|
<div>expires: {moment.utc(expires).fromNow()}</div>
|
||||||
.utc(mbfc.mod_ban_from_community.expires.unwrap())
|
),
|
||||||
.fromNow()}`}
|
none: <></>,
|
||||||
</div>,
|
}),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
case ModlogEnum.ModAddCommunity: {
|
case ModlogEnum.ModAddCommunity: {
|
||||||
let mac = i.view as ModAddCommunityView;
|
let mac = i.view as ModAddCommunityView;
|
||||||
return [
|
return [
|
||||||
<span>
|
<span>
|
||||||
{mac.mod_add_community.removed ? "Removed " : "Appointed "}{" "}
|
{mac.mod_add_community.removed.unwrapOr(false)
|
||||||
|
? "Removed "
|
||||||
|
: "Appointed "}{" "}
|
||||||
</span>,
|
</span>,
|
||||||
<span>
|
<span>
|
||||||
<PersonListing person={mac.modded_person} />
|
<PersonListing person={mac.modded_person} />
|
||||||
|
@ -337,7 +420,9 @@ export class Modlog extends Component<any, ModlogState> {
|
||||||
let mtc = i.view as ModTransferCommunityView;
|
let mtc = i.view as ModTransferCommunityView;
|
||||||
return [
|
return [
|
||||||
<span>
|
<span>
|
||||||
{mtc.mod_transfer_community.removed ? "Removed " : "Transferred "}{" "}
|
{mtc.mod_transfer_community.removed.unwrapOr(false)
|
||||||
|
? "Removed "
|
||||||
|
: "Transferred "}{" "}
|
||||||
</span>,
|
</span>,
|
||||||
<span>
|
<span>
|
||||||
<CommunityLink community={mtc.community} />
|
<CommunityLink community={mtc.community} />
|
||||||
|
@ -351,27 +436,29 @@ export class Modlog extends Component<any, ModlogState> {
|
||||||
case ModlogEnum.ModBan: {
|
case ModlogEnum.ModBan: {
|
||||||
let mb = i.view as ModBanView;
|
let mb = i.view as ModBanView;
|
||||||
return [
|
return [
|
||||||
<span>{mb.mod_ban.banned ? "Banned " : "Unbanned "} </span>,
|
<span>
|
||||||
|
{mb.mod_ban.banned.unwrapOr(false) ? "Banned " : "Unbanned "}{" "}
|
||||||
|
</span>,
|
||||||
<span>
|
<span>
|
||||||
<PersonListing person={mb.banned_person} />
|
<PersonListing person={mb.banned_person} />
|
||||||
</span>,
|
</span>,
|
||||||
<div>
|
mb.mod_ban.reason.match({
|
||||||
{mb.mod_ban.reason.isSome() &&
|
some: reason => <div>reason: {reason}</div>,
|
||||||
` reason: ${mb.mod_ban.reason.unwrap()}`}
|
none: <></>,
|
||||||
</div>,
|
}),
|
||||||
<div>
|
mb.mod_ban.expires.match({
|
||||||
{mb.mod_ban.expires.isSome() &&
|
some: expires => (
|
||||||
` expires: ${moment.utc(mb.mod_ban.expires.unwrap()).fromNow()}`}
|
<div>expires: {moment.utc(expires).fromNow()}</div>
|
||||||
</div>,
|
),
|
||||||
|
none: <></>,
|
||||||
|
}),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
case ModlogEnum.ModAdd: {
|
case ModlogEnum.ModAdd: {
|
||||||
let ma = i.view as ModAddView;
|
let ma = i.view as ModAddView;
|
||||||
return [
|
return [
|
||||||
<span>
|
<span>
|
||||||
{ma.mod_add.removed.isSome() && ma.mod_add.removed.unwrap()
|
{ma.mod_add.removed.unwrapOr(false) ? "Removed " : "Appointed "}{" "}
|
||||||
? "Removed "
|
|
||||||
: "Appointed "}{" "}
|
|
||||||
</span>,
|
</span>,
|
||||||
<span>
|
<span>
|
||||||
<PersonListing person={ma.modded_person} />
|
<PersonListing person={ma.modded_person} />
|
||||||
|
@ -379,6 +466,50 @@ export class Modlog extends Component<any, ModlogState> {
|
||||||
<span> as an admin </span>,
|
<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:
|
default:
|
||||||
return <div />;
|
return <div />;
|
||||||
}
|
}
|
||||||
|
@ -396,9 +527,9 @@ export class Modlog extends Component<any, ModlogState> {
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
{this.amAdminOrMod ? (
|
{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>
|
||||||
<td>{this.renderModlogType(i)}</td>
|
<td>{this.renderModlogType(i)}</td>
|
||||||
|
@ -415,7 +546,7 @@ export class Modlog extends Component<any, ModlogState> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
modOrAdminText(person: PersonSafe): Text {
|
modOrAdminText(person: PersonSafe): string {
|
||||||
if (
|
if (
|
||||||
this.isoData.site_res.admins.map(a => a.person.id).includes(person.id)
|
this.isoData.site_res.admins.map(a => a.person.id).includes(person.id)
|
||||||
) {
|
) {
|
||||||
|
|
|
@ -12,6 +12,7 @@ import {
|
||||||
GetPersonDetailsResponse,
|
GetPersonDetailsResponse,
|
||||||
GetSiteResponse,
|
GetSiteResponse,
|
||||||
PostResponse,
|
PostResponse,
|
||||||
|
PurgeItemResponse,
|
||||||
SortType,
|
SortType,
|
||||||
toUndefined,
|
toUndefined,
|
||||||
UserOperation,
|
UserOperation,
|
||||||
|
@ -897,6 +898,17 @@ export class Profile extends Component<any, ProfileState> {
|
||||||
updatePersonBlock(data);
|
updatePersonBlock(data);
|
||||||
this.setPersonBlock();
|
this.setPersonBlock();
|
||||||
this.setState(this.state);
|
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(`/`);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,6 +15,8 @@ import {
|
||||||
LockPost,
|
LockPost,
|
||||||
PersonViewSafe,
|
PersonViewSafe,
|
||||||
PostView,
|
PostView,
|
||||||
|
PurgePerson,
|
||||||
|
PurgePost,
|
||||||
RemovePost,
|
RemovePost,
|
||||||
SavePost,
|
SavePost,
|
||||||
StickyPost,
|
StickyPost,
|
||||||
|
@ -23,7 +25,7 @@ import {
|
||||||
} from "lemmy-js-client";
|
} from "lemmy-js-client";
|
||||||
import { externalHost } from "../../env";
|
import { externalHost } from "../../env";
|
||||||
import { i18n } from "../../i18next";
|
import { i18n } from "../../i18next";
|
||||||
import { BanType } from "../../interfaces";
|
import { BanType, PurgeType } from "../../interfaces";
|
||||||
import { UserService, WebSocketService } from "../../services";
|
import { UserService, WebSocketService } from "../../services";
|
||||||
import {
|
import {
|
||||||
amCommunityCreator,
|
amCommunityCreator,
|
||||||
|
@ -45,7 +47,7 @@ import {
|
||||||
showScores,
|
showScores,
|
||||||
wsClient,
|
wsClient,
|
||||||
} from "../../utils";
|
} from "../../utils";
|
||||||
import { Icon } from "../common/icon";
|
import { Icon, PurgeWarning, Spinner } from "../common/icon";
|
||||||
import { MomentTime } from "../common/moment-time";
|
import { MomentTime } from "../common/moment-time";
|
||||||
import { PictrsImage } from "../common/pictrs-image";
|
import { PictrsImage } from "../common/pictrs-image";
|
||||||
import { CommunityLink } from "../community/community-link";
|
import { CommunityLink } from "../community/community-link";
|
||||||
|
@ -56,6 +58,10 @@ import { PostForm } from "./post-form";
|
||||||
interface PostListingState {
|
interface PostListingState {
|
||||||
showEdit: boolean;
|
showEdit: boolean;
|
||||||
showRemoveDialog: boolean;
|
showRemoveDialog: boolean;
|
||||||
|
showPurgeDialog: boolean;
|
||||||
|
purgeReason: Option<string>;
|
||||||
|
purgeType: PurgeType;
|
||||||
|
purgeLoading: boolean;
|
||||||
removeReason: Option<string>;
|
removeReason: Option<string>;
|
||||||
showBanDialog: boolean;
|
showBanDialog: boolean;
|
||||||
banReason: Option<string>;
|
banReason: Option<string>;
|
||||||
|
@ -93,6 +99,10 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
|
||||||
private emptyState: PostListingState = {
|
private emptyState: PostListingState = {
|
||||||
showEdit: false,
|
showEdit: false,
|
||||||
showRemoveDialog: false,
|
showRemoveDialog: false,
|
||||||
|
showPurgeDialog: false,
|
||||||
|
purgeReason: None,
|
||||||
|
purgeType: PurgeType.Person,
|
||||||
|
purgeLoading: false,
|
||||||
removeReason: None,
|
removeReason: None,
|
||||||
showBanDialog: false,
|
showBanDialog: false,
|
||||||
banReason: None,
|
banReason: None,
|
||||||
|
@ -943,24 +953,41 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
|
||||||
{/* Admins can ban from all, and appoint other admins */}
|
{/* Admins can ban from all, and appoint other admins */}
|
||||||
{this.canAdmin_ && (
|
{this.canAdmin_ && (
|
||||||
<>
|
<>
|
||||||
{!this.creatorIsAdmin_ &&
|
{!this.creatorIsAdmin_ && (
|
||||||
(!isBanned(post_view.creator) ? (
|
<>
|
||||||
|
{!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
|
<button
|
||||||
class="btn btn-link btn-animate text-muted py-0"
|
class="btn btn-link btn-animate text-muted py-0"
|
||||||
onClick={linkEvent(this, this.handleModBanShow)}
|
onClick={linkEvent(this, 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>
|
||||||
) : (
|
|
||||||
<button
|
<button
|
||||||
class="btn btn-link btn-animate text-muted py-0"
|
class="btn btn-link btn-animate text-muted py-0"
|
||||||
onClick={linkEvent(this, this.handleModBanSubmit)}
|
onClick={linkEvent(this, this.handlePurgePostShow)}
|
||||||
aria-label={i18n.t("unban_from_site")}
|
aria-label={i18n.t("purge_post")}
|
||||||
>
|
>
|
||||||
{i18n.t("unban_from_site")}
|
{i18n.t("purge_post")}
|
||||||
</button>
|
</button>
|
||||||
))}
|
</>
|
||||||
|
)}
|
||||||
{!isBanned(post_view.creator) && post_view.creator.local && (
|
{!isBanned(post_view.creator) && post_view.creator.local && (
|
||||||
<button
|
<button
|
||||||
class="btn btn-link btn-animate text-muted py-0"
|
class="btn btn-link btn-animate text-muted py-0"
|
||||||
|
@ -985,6 +1012,12 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
|
||||||
|
|
||||||
removeAndBanDialogs() {
|
removeAndBanDialogs() {
|
||||||
let post = this.props.post_view;
|
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 (
|
return (
|
||||||
<>
|
<>
|
||||||
{this.state.showRemoveDialog && (
|
{this.state.showRemoveDialog && (
|
||||||
|
@ -1098,6 +1131,36 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
|
||||||
</button>
|
</button>
|
||||||
</form>
|
</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>
|
||||||
|
)}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1411,6 +1474,48 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
|
||||||
i.setState(i.state);
|
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) {
|
handleModBanReasonChange(i: PostListing, event: any) {
|
||||||
i.state.banReason = Some(event.target.value);
|
i.state.banReason = Some(event.target.value);
|
||||||
i.setState(i.state);
|
i.setState(i.state);
|
||||||
|
|
|
@ -19,6 +19,7 @@ import {
|
||||||
PostReportResponse,
|
PostReportResponse,
|
||||||
PostResponse,
|
PostResponse,
|
||||||
PostView,
|
PostView,
|
||||||
|
PurgeItemResponse,
|
||||||
Search,
|
Search,
|
||||||
SearchResponse,
|
SearchResponse,
|
||||||
SearchType,
|
SearchType,
|
||||||
|
@ -760,6 +761,17 @@ export class Post extends Component<any, PostState> {
|
||||||
if (data) {
|
if (data) {
|
||||||
toast(i18n.t("report_created"));
|
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(`/`);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -73,3 +73,10 @@ export enum PersonDetailsView {
|
||||||
Posts,
|
Posts,
|
||||||
Saved,
|
Saved,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export enum PurgeType {
|
||||||
|
Person,
|
||||||
|
Community,
|
||||||
|
Post,
|
||||||
|
Comment,
|
||||||
|
}
|
||||||
|
|
|
@ -4948,10 +4948,10 @@ lcid@^1.0.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
invert-kv "^1.0.0"
|
invert-kv "^1.0.0"
|
||||||
|
|
||||||
lemmy-js-client@0.17.0-rc.32:
|
lemmy-js-client@0.17.0-rc.33:
|
||||||
version "0.17.0-rc.32"
|
version "0.17.0-rc.33"
|
||||||
resolved "https://registry.yarnpkg.com/lemmy-js-client/-/lemmy-js-client-0.17.0-rc.32.tgz#d67f432f1fffc54c267f915278fe260ec554b018"
|
resolved "https://registry.yarnpkg.com/lemmy-js-client/-/lemmy-js-client-0.17.0-rc.33.tgz#e05cc88213da3c0c21c7ea53c29054041d619150"
|
||||||
integrity sha512-qPLybaesu3GVr1DMStsyCYanW4maxHrqX71UHadFMeuh+aUK8taC3zfsLRK9dlIlSDRS283xd8IZkI6ZlcOVEQ==
|
integrity sha512-rG0yCc9AAc5/B+muDfWB7bKizBG7r/xSzHeEw5ms50xF4dN+KOqvRcHTf0+15uAYehBF5B54nyxdlKPRKL9GxQ==
|
||||||
|
|
||||||
levn@^0.4.1:
|
levn@^0.4.1:
|
||||||
version "0.4.1"
|
version "0.4.1"
|
||||||
|
|
Loading…
Reference in a new issue