mirror of
https://github.com/LemmyNet/lemmy-ui.git
synced 2024-11-25 22:01:13 +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",
|
||||
"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",
|
||||
|
|
|
@ -17,6 +17,8 @@ import {
|
|||
MarkPersonMentionAsRead,
|
||||
PersonMentionView,
|
||||
PersonViewSafe,
|
||||
PurgeComment,
|
||||
PurgePerson,
|
||||
RemoveComment,
|
||||
SaveComment,
|
||||
toUndefined,
|
||||
|
@ -24,7 +26,11 @@ import {
|
|||
} 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,
|
||||
|
@ -42,7 +48,7 @@ import {
|
|||
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";
|
||||
|
@ -59,6 +65,10 @@ interface CommentNodeState {
|
|||
banReason: Option<string>;
|
||||
banExpireDays: Option<number>;
|
||||
banType: BanType;
|
||||
showPurgeDialog: boolean;
|
||||
purgeReason: Option<string>;
|
||||
purgeType: PurgeType;
|
||||
purgeLoading: boolean;
|
||||
showConfirmTransferSite: boolean;
|
||||
showConfirmTransferCommunity: boolean;
|
||||
showConfirmAppointAsMod: boolean;
|
||||
|
@ -102,6 +112,10 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
|
|||
banReason: None,
|
||||
banExpireDays: None,
|
||||
banType: BanType.Community,
|
||||
showPurgeDialog: false,
|
||||
purgeLoading: false,
|
||||
purgeReason: None,
|
||||
purgeType: PurgeType.Person,
|
||||
collapsed: false,
|
||||
viewSource: false,
|
||||
showAdvanced: false,
|
||||
|
@ -147,6 +161,13 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
|
|||
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,
|
||||
|
@ -645,8 +666,30 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
|
|||
{/* 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.handlePurgePersonShow
|
||||
)}
|
||||
aria-label={i18n.t("purge_user")}
|
||||
>
|
||||
{i18n.t("purge_user")}
|
||||
</button>
|
||||
<button
|
||||
class="btn btn-link btn-animate text-muted"
|
||||
onClick={linkEvent(
|
||||
this,
|
||||
this.handlePurgeCommentShow
|
||||
)}
|
||||
aria-label={i18n.t("purge_comment")}
|
||||
>
|
||||
{i18n.t("purge_comment")}
|
||||
</button>
|
||||
|
||||
{!isBanned(cv.creator) ? (
|
||||
<button
|
||||
class="btn btn-link btn-animate text-muted"
|
||||
onClick={linkEvent(
|
||||
|
@ -668,7 +711,9 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
|
|||
>
|
||||
{i18n.t("unban_from_site")}
|
||||
</button>
|
||||
))}
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
{!isBanned(cv.creator) &&
|
||||
cv.creator.local &&
|
||||
(!this.state.showConfirmAppointAsAdmin ? (
|
||||
|
@ -848,6 +893,36 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
|
|||
</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)}
|
||||
|
@ -1202,6 +1277,48 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
|
|||
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);
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import classNames from "classnames";
|
||||
import { Component } from "inferno";
|
||||
import { i18n } from "../../i18next";
|
||||
|
||||
interface IconProps {
|
||||
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,
|
||||
PostResponse,
|
||||
PostView,
|
||||
PurgeItemResponse,
|
||||
SortType,
|
||||
toOption,
|
||||
UserOperation,
|
||||
|
@ -656,6 +657,12 @@ export class Community extends Component<any, State> {
|
|||
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(`/`);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { Option, Some } from "@sniptt/monads";
|
||||
import { None, Option, Some } from "@sniptt/monads";
|
||||
import { Component, linkEvent } from "inferno";
|
||||
import { Link } from "inferno-router";
|
||||
import {
|
||||
|
@ -8,6 +8,7 @@ import {
|
|||
DeleteCommunity,
|
||||
FollowCommunity,
|
||||
PersonViewSafe,
|
||||
PurgeCommunity,
|
||||
RemoveCommunity,
|
||||
SubscribedType,
|
||||
toUndefined,
|
||||
|
@ -25,7 +26,7 @@ import {
|
|||
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";
|
||||
|
@ -44,6 +45,9 @@ interface SidebarState {
|
|||
removeExpires: Option<string>;
|
||||
showEdit: boolean;
|
||||
showRemoveDialog: boolean;
|
||||
showPurgeDialog: boolean;
|
||||
purgeReason: Option<string>;
|
||||
purgeLoading: boolean;
|
||||
showConfirmLeaveModTeam: boolean;
|
||||
}
|
||||
|
||||
|
@ -51,8 +55,11 @@ export class Sidebar extends Component<SidebarProps, SidebarState> {
|
|||
private emptyState: SidebarState = {
|
||||
showEdit: false,
|
||||
showRemoveDialog: false,
|
||||
removeReason: null,
|
||||
removeExpires: null,
|
||||
removeReason: None,
|
||||
removeExpires: None,
|
||||
showPurgeDialog: false,
|
||||
purgeReason: None,
|
||||
purgeLoading: false,
|
||||
showConfirmLeaveModTeam: false,
|
||||
};
|
||||
|
||||
|
@ -403,12 +410,19 @@ export class Sidebar extends Component<SidebarProps, SidebarState> {
|
|||
{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>
|
||||
|
@ -426,13 +440,46 @@ export class Sidebar extends Component<SidebarProps, SidebarState> {
|
|||
{/* <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>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
@ -542,13 +589,12 @@ export class Sidebar extends Component<SidebarProps, SidebarState> {
|
|||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -566,4 +612,29 @@ export class Sidebar extends Component<SidebarProps, SidebarState> {
|
|||
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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,6 +21,7 @@ import {
|
|||
PostReportResponse,
|
||||
PostResponse,
|
||||
PostView,
|
||||
PurgeItemResponse,
|
||||
SiteResponse,
|
||||
SortType,
|
||||
UserOperation,
|
||||
|
@ -860,6 +861,17 @@ export class Home extends Component<any, HomeState> {
|
|||
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(`/`);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,6 +2,10 @@ import { None, Option, Some } from "@sniptt/monads";
|
|||
import { Component } from "inferno";
|
||||
import { Link } from "inferno-router";
|
||||
import {
|
||||
AdminPurgeCommentView,
|
||||
AdminPurgeCommunityView,
|
||||
AdminPurgePersonView,
|
||||
AdminPurgePostView,
|
||||
CommunityModeratorView,
|
||||
GetCommunity,
|
||||
GetCommunityResponse,
|
||||
|
@ -57,11 +61,16 @@ enum ModlogEnum {
|
|||
ModTransferCommunity,
|
||||
ModAdd,
|
||||
ModBan,
|
||||
AdminPurgePerson,
|
||||
AdminPurgeCommunity,
|
||||
AdminPurgePost,
|
||||
AdminPurgeComment,
|
||||
}
|
||||
|
||||
type ModlogType = {
|
||||
id: number;
|
||||
type_: ModlogEnum;
|
||||
moderator: PersonSafe;
|
||||
view:
|
||||
| ModRemovePostView
|
||||
| ModLockPostView
|
||||
|
@ -72,7 +81,11 @@ type ModlogType = {
|
|||
| ModBanView
|
||||
| ModAddCommunityView
|
||||
| ModTransferCommunityView
|
||||
| ModAddView;
|
||||
| ModAddView
|
||||
| AdminPurgePersonView
|
||||
| AdminPurgeCommunityView
|
||||
| AdminPurgePostView
|
||||
| AdminPurgeCommentView;
|
||||
when_: string;
|
||||
};
|
||||
|
||||
|
@ -118,11 +131,13 @@ export class Modlog extends Component<any, ModlogState> {
|
|||
if (this.isoData.path == this.context.router.route.match.url) {
|
||||
this.state.res = Some(this.isoData.routeData[0] as GetModlogResponse);
|
||||
|
||||
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 {
|
||||
|
@ -141,6 +156,7 @@ export class Modlog extends Component<any, ModlogState> {
|
|||
id: r.mod_remove_post.id,
|
||||
type_: ModlogEnum.ModRemovePost,
|
||||
view: r,
|
||||
moderator: r.moderator,
|
||||
when_: r.mod_remove_post.when_,
|
||||
}));
|
||||
|
||||
|
@ -148,6 +164,7 @@ export class Modlog extends Component<any, ModlogState> {
|
|||
id: r.mod_lock_post.id,
|
||||
type_: ModlogEnum.ModLockPost,
|
||||
view: r,
|
||||
moderator: r.moderator,
|
||||
when_: r.mod_lock_post.when_,
|
||||
}));
|
||||
|
||||
|
@ -155,6 +172,7 @@ export class Modlog extends Component<any, ModlogState> {
|
|||
id: r.mod_sticky_post.id,
|
||||
type_: ModlogEnum.ModStickyPost,
|
||||
view: r,
|
||||
moderator: r.moderator,
|
||||
when_: r.mod_sticky_post.when_,
|
||||
}));
|
||||
|
||||
|
@ -162,6 +180,7 @@ export class Modlog extends Component<any, ModlogState> {
|
|||
id: r.mod_remove_comment.id,
|
||||
type_: ModlogEnum.ModRemoveComment,
|
||||
view: r,
|
||||
moderator: r.moderator,
|
||||
when_: r.mod_remove_comment.when_,
|
||||
}));
|
||||
|
||||
|
@ -169,6 +188,7 @@ export class Modlog extends Component<any, ModlogState> {
|
|||
id: r.mod_remove_community.id,
|
||||
type_: ModlogEnum.ModRemoveCommunity,
|
||||
view: r,
|
||||
moderator: r.moderator,
|
||||
when_: r.mod_remove_community.when_,
|
||||
}));
|
||||
|
||||
|
@ -177,6 +197,7 @@ export class Modlog extends Component<any, ModlogState> {
|
|||
id: r.mod_ban_from_community.id,
|
||||
type_: ModlogEnum.ModBanFromCommunity,
|
||||
view: r,
|
||||
moderator: r.moderator,
|
||||
when_: r.mod_ban_from_community.when_,
|
||||
})
|
||||
);
|
||||
|
@ -185,6 +206,7 @@ export class Modlog extends Component<any, ModlogState> {
|
|||
id: r.mod_add_community.id,
|
||||
type_: ModlogEnum.ModAddCommunity,
|
||||
view: r,
|
||||
moderator: r.moderator,
|
||||
when_: r.mod_add_community.when_,
|
||||
}));
|
||||
|
||||
|
@ -193,6 +215,7 @@ export class Modlog extends Component<any, ModlogState> {
|
|||
id: r.mod_transfer_community.id,
|
||||
type_: ModlogEnum.ModTransferCommunity,
|
||||
view: r,
|
||||
moderator: r.moderator,
|
||||
when_: r.mod_transfer_community.when_,
|
||||
}));
|
||||
|
||||
|
@ -200,6 +223,7 @@ export class Modlog extends Component<any, ModlogState> {
|
|||
id: r.mod_add.id,
|
||||
type_: ModlogEnum.ModAdd,
|
||||
view: r,
|
||||
moderator: r.moderator,
|
||||
when_: r.mod_add.when_,
|
||||
}));
|
||||
|
||||
|
@ -207,9 +231,44 @@ export class Modlog extends Component<any, ModlogState> {
|
|||
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);
|
||||
|
@ -222,6 +281,10 @@ export class Modlog extends Component<any, ModlogState> {
|
|||
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_));
|
||||
|
@ -234,18 +297,22 @@ export class Modlog extends Component<any, ModlogState> {
|
|||
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>,
|
||||
|
@ -254,7 +321,9 @@ export class Modlog extends Component<any, ModlogState> {
|
|||
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>,
|
||||
|
@ -263,7 +332,9 @@ export class Modlog extends Component<any, ModlogState> {
|
|||
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}`}>
|
||||
|
@ -274,30 +345,40 @@ export class Modlog extends Component<any, ModlogState> {
|
|||
{" "}
|
||||
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} />
|
||||
|
@ -306,23 +387,25 @@ export class Modlog extends Component<any, ModlogState> {
|
|||
<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} />
|
||||
|
@ -337,7 +420,9 @@ export class Modlog extends Component<any, ModlogState> {
|
|||
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} />
|
||||
|
@ -351,27 +436,29 @@ export class Modlog extends Component<any, ModlogState> {
|
|||
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} />
|
||||
|
@ -379,6 +466,50 @@ export class Modlog extends Component<any, ModlogState> {
|
|||
<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 />;
|
||||
}
|
||||
|
@ -396,9 +527,9 @@ export class Modlog extends Component<any, ModlogState> {
|
|||
</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>
|
||||
|
@ -415,7 +546,7 @@ export class Modlog extends Component<any, ModlogState> {
|
|||
);
|
||||
}
|
||||
|
||||
modOrAdminText(person: PersonSafe): Text {
|
||||
modOrAdminText(person: PersonSafe): string {
|
||||
if (
|
||||
this.isoData.site_res.admins.map(a => a.person.id).includes(person.id)
|
||||
) {
|
||||
|
|
|
@ -12,6 +12,7 @@ import {
|
|||
GetPersonDetailsResponse,
|
||||
GetSiteResponse,
|
||||
PostResponse,
|
||||
PurgeItemResponse,
|
||||
SortType,
|
||||
toUndefined,
|
||||
UserOperation,
|
||||
|
@ -897,6 +898,17 @@ export class Profile extends Component<any, ProfileState> {
|
|||
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(`/`);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,6 +15,8 @@ import {
|
|||
LockPost,
|
||||
PersonViewSafe,
|
||||
PostView,
|
||||
PurgePerson,
|
||||
PurgePost,
|
||||
RemovePost,
|
||||
SavePost,
|
||||
StickyPost,
|
||||
|
@ -23,7 +25,7 @@ import {
|
|||
} 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,
|
||||
|
@ -45,7 +47,7 @@ import {
|
|||
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";
|
||||
|
@ -56,6 +58,10 @@ import { PostForm } from "./post-form";
|
|||
interface PostListingState {
|
||||
showEdit: boolean;
|
||||
showRemoveDialog: boolean;
|
||||
showPurgeDialog: boolean;
|
||||
purgeReason: Option<string>;
|
||||
purgeType: PurgeType;
|
||||
purgeLoading: boolean;
|
||||
removeReason: Option<string>;
|
||||
showBanDialog: boolean;
|
||||
banReason: Option<string>;
|
||||
|
@ -93,6 +99,10 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
|
|||
private emptyState: PostListingState = {
|
||||
showEdit: false,
|
||||
showRemoveDialog: false,
|
||||
showPurgeDialog: false,
|
||||
purgeReason: None,
|
||||
purgeType: PurgeType.Person,
|
||||
purgeLoading: false,
|
||||
removeReason: None,
|
||||
showBanDialog: false,
|
||||
banReason: None,
|
||||
|
@ -943,8 +953,9 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
|
|||
{/* 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)}
|
||||
|
@ -960,7 +971,23 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
|
|||
>
|
||||
{i18n.t("unban_from_site")}
|
||||
</button>
|
||||
))}
|
||||
)}
|
||||
<button
|
||||
class="btn btn-link btn-animate text-muted py-0"
|
||||
onClick={linkEvent(this, this.handlePurgePersonShow)}
|
||||
aria-label={i18n.t("purge_user")}
|
||||
>
|
||||
{i18n.t("purge_user")}
|
||||
</button>
|
||||
<button
|
||||
class="btn btn-link btn-animate text-muted py-0"
|
||||
onClick={linkEvent(this, this.handlePurgePostShow)}
|
||||
aria-label={i18n.t("purge_post")}
|
||||
>
|
||||
{i18n.t("purge_post")}
|
||||
</button>
|
||||
</>
|
||||
)}
|
||||
{!isBanned(post_view.creator) && post_view.creator.local && (
|
||||
<button
|
||||
class="btn btn-link btn-animate text-muted py-0"
|
||||
|
@ -985,6 +1012,12 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
|
|||
|
||||
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 && (
|
||||
|
@ -1098,6 +1131,36 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
|
|||
</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>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
@ -1411,6 +1474,48 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
|
|||
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);
|
||||
|
|
|
@ -19,6 +19,7 @@ import {
|
|||
PostReportResponse,
|
||||
PostResponse,
|
||||
PostView,
|
||||
PurgeItemResponse,
|
||||
Search,
|
||||
SearchResponse,
|
||||
SearchType,
|
||||
|
@ -760,6 +761,17 @@ export class Post extends Component<any, PostState> {
|
|||
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(`/`);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -73,3 +73,10 @@ export enum PersonDetailsView {
|
|||
Posts,
|
||||
Saved,
|
||||
}
|
||||
|
||||
export enum PurgeType {
|
||||
Person,
|
||||
Community,
|
||||
Post,
|
||||
Comment,
|
||||
}
|
||||
|
|
|
@ -4948,10 +4948,10 @@ lcid@^1.0.0:
|
|||
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"
|
||||
|
|
Loading…
Reference in a new issue