Adding private message reporting. Fixes #782 (#806)

This commit is contained in:
Dessalines 2022-09-28 08:50:47 -04:00 committed by GitHub
parent 333642e065
commit dc62cab7e9
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 353 additions and 75 deletions

View file

@ -78,7 +78,7 @@
"eslint-plugin-prettier": "^4.2.1",
"husky": "^8.0.1",
"import-sort-style-module": "^6.0.0",
"lemmy-js-client": "0.17.0-rc.45",
"lemmy-js-client": "0.17.0-rc.46",
"lint-staged": "^13.0.3",
"mini-css-extract-plugin": "^2.6.1",
"node-fetch": "^2.6.1",

View file

@ -1,4 +1,4 @@
import { None, Some } from "@sniptt/monads";
import { None } from "@sniptt/monads";
import { Component, createRef, linkEvent, RefObject } from "inferno";
import { NavLink } from "inferno-router";
import {
@ -209,7 +209,7 @@ export class Navbar extends Component<NavbarProps, NavbarState> {
</li>
</ul>
)}
{this.amAdmin && (
{amAdmin() && (
<ul className="navbar-nav ml-1">
<li className="nav-item">
<NavLink
@ -298,7 +298,7 @@ export class Navbar extends Component<NavbarProps, NavbarState> {
</li>
</ul>
<ul className="navbar-nav my-2">
{this.amAdmin && (
{amAdmin() && (
<li className="nav-item">
<NavLink
to="/admin"
@ -388,7 +388,7 @@ export class Navbar extends Component<NavbarProps, NavbarState> {
</li>
</ul>
)}
{this.amAdmin && (
{amAdmin() && (
<ul className="navbar-nav my-2">
<li className="nav-item">
<NavLink
@ -525,10 +525,6 @@ export class Navbar extends Component<NavbarProps, NavbarState> {
);
}
get amAdmin(): boolean {
return amAdmin(Some(this.props.siteRes.admins));
}
handleToggleExpandNavbar(i: Navbar) {
i.setState({ expanded: !i.state.expanded });
}
@ -605,7 +601,10 @@ export class Navbar extends Component<NavbarProps, NavbarState> {
GetReportCountResponse
);
this.setState({
unreadReportCount: data.post_reports + data.comment_reports,
unreadReportCount:
data.post_reports +
data.comment_reports +
data.private_message_reports.unwrapOr(0),
});
this.sendReportUnread();
} else if (op == UserOperation.GetUnreadRegistrationApplicationCount) {
@ -673,7 +672,7 @@ export class Navbar extends Component<NavbarProps, NavbarState> {
});
WebSocketService.Instance.send(wsClient.getReportCount(reportCountForm));
if (this.amAdmin) {
if (amAdmin()) {
console.log("Fetching applications...");
let applicationCountForm = new GetUnreadRegistrationApplicationCount({

View file

@ -420,7 +420,7 @@ export class Sidebar extends Component<SidebarProps, SidebarState> {
)}
</>
)}
{amAdmin(Some(this.props.admins)) && (
{amAdmin() && (
<li className="list-inline-item">
{!this.props.community_view.community.removed ? (
<button
@ -600,7 +600,7 @@ export class Sidebar extends Component<SidebarProps, SidebarState> {
return (
!this.props.community_view.community.posting_restricted_to_mods ||
amMod(Some(this.props.moderators)) ||
amAdmin(Some(this.props.admins))
amAdmin()
);
}

View file

@ -113,7 +113,7 @@ export class SiteSidebar extends Component<SiteSidebarProps, SiteSidebarState> {
adminButtons() {
return (
amAdmin(this.props.admins) && (
amAdmin() && (
<ul className="list-inline mb-1 text-muted font-weight-bold">
<li className="list-inline-item-action">
<button

View file

@ -618,10 +618,7 @@ export class Modlog extends Component<any, ModlogState> {
}
get amAdminOrMod(): boolean {
return (
amAdmin(Some(this.state.siteRes.admins)) ||
amMod(this.state.communityMods)
);
return amAdmin() || amMod(this.state.communityMods);
}
modOrAdminText(person: Option<PersonSafe>): string {

View file

@ -17,6 +17,7 @@ import {
PersonMentionResponse,
PersonMentionView,
PostReportResponse,
PrivateMessageReportResponse,
PrivateMessageResponse,
PrivateMessagesResponse,
PrivateMessageView,
@ -880,6 +881,14 @@ export class Inbox extends Component<any, InboxState> {
if (data) {
toast(i18n.t("report_created"));
}
} else if (op == UserOperation.CreatePrivateMessageReport) {
let data = wsJsonToRes<PrivateMessageReportResponse>(
msg,
PrivateMessageReportResponse
);
if (data) {
toast(i18n.t("report_created"));
}
}
}

View file

@ -8,8 +8,12 @@ import {
ListCommentReportsResponse,
ListPostReports,
ListPostReportsResponse,
ListPrivateMessageReports,
ListPrivateMessageReportsResponse,
PostReportResponse,
PostReportView,
PrivateMessageReportResponse,
PrivateMessageReportView,
UserOperation,
wsJsonToRes,
wsUserOp,
@ -19,6 +23,7 @@ import { i18n } from "../../i18next";
import { InitialFetchRequest } from "../../interfaces";
import { UserService, WebSocketService } from "../../services";
import {
amAdmin,
auth,
fetchLimit,
isBrowser,
@ -27,6 +32,7 @@ import {
toast,
updateCommentReportRes,
updatePostReportRes,
updatePrivateMessageReportRes,
wsClient,
wsSubscribe,
} from "../../utils";
@ -35,6 +41,7 @@ import { HtmlTags } from "../common/html-tags";
import { Spinner } from "../common/icon";
import { Paginator } from "../common/paginator";
import { PostReport } from "../post/post-report";
import { PrivateMessageReport } from "../private_message/private-message-report";
enum UnreadOrAll {
Unread,
@ -45,23 +52,26 @@ enum MessageType {
All,
CommentReport,
PostReport,
PrivateMessageReport,
}
enum MessageEnum {
CommentReport,
PostReport,
PrivateMessageReport,
}
type ItemType = {
id: number;
type_: MessageEnum;
view: CommentReportView | PostReportView;
view: CommentReportView | PostReportView | PrivateMessageReportView;
published: string;
};
interface ReportsState {
listCommentReportsResponse: Option<ListCommentReportsResponse>;
listPostReportsResponse: Option<ListPostReportsResponse>;
listPrivateMessageReportsResponse: Option<ListPrivateMessageReportsResponse>;
unreadOrAll: UnreadOrAll;
messageType: MessageType;
combined: ItemType[];
@ -74,12 +84,14 @@ export class Reports extends Component<any, ReportsState> {
private isoData = setIsoData(
this.context,
ListCommentReportsResponse,
ListPostReportsResponse
ListPostReportsResponse,
ListPrivateMessageReportsResponse
);
private subscription: Subscription;
private emptyState: ReportsState = {
listCommentReportsResponse: None,
listPostReportsResponse: None,
listPrivateMessageReportsResponse: None,
unreadOrAll: UnreadOrAll.Unread,
messageType: MessageType.All,
combined: [],
@ -113,6 +125,14 @@ export class Reports extends Component<any, ReportsState> {
this.isoData.routeData[1] as ListPostReportsResponse
),
};
if (amAdmin()) {
this.state = {
...this.state,
listPrivateMessageReportsResponse: Some(
this.isoData.routeData[2] as ListPrivateMessageReportsResponse
),
};
}
this.state = {
...this.state,
combined: this.buildCombined(),
@ -166,6 +186,8 @@ export class Reports extends Component<any, ReportsState> {
this.commentReports()}
{this.state.messageType == MessageType.PostReport &&
this.postReports()}
{this.state.messageType == MessageType.PrivateMessageReport &&
this.privateMessageReports()}
<Paginator
page={this.state.page}
onChange={this.handlePageChange}
@ -252,6 +274,26 @@ export class Reports extends Component<any, ReportsState> {
/>
{i18n.t("posts")}
</label>
{amAdmin() && (
<label
className={`btn btn-outline-secondary pointer
${
this.state.messageType == MessageType.PrivateMessageReport &&
"active"
}
`}
>
<input
type="radio"
value={MessageType.PrivateMessageReport}
checked={
this.state.messageType == MessageType.PrivateMessageReport
}
onChange={linkEvent(this, this.handleMessageTypeChange)}
/>
{i18n.t("messages")}
</label>
)}
</div>
);
}
@ -265,7 +307,7 @@ export class Reports extends Component<any, ReportsState> {
);
}
replyToReplyType(r: CommentReportView): ItemType {
commentReportToItemType(r: CommentReportView): ItemType {
return {
id: r.comment_report.id,
type_: MessageEnum.CommentReport,
@ -274,7 +316,7 @@ export class Reports extends Component<any, ReportsState> {
};
}
mentionToReplyType(r: PostReportView): ItemType {
postReportToItemType(r: PostReportView): ItemType {
return {
id: r.post_report.id,
type_: MessageEnum.PostReport,
@ -283,17 +325,31 @@ export class Reports extends Component<any, ReportsState> {
};
}
privateMessageReportToItemType(r: PrivateMessageReportView): ItemType {
return {
id: r.private_message_report.id,
type_: MessageEnum.PrivateMessageReport,
view: r,
published: r.private_message_report.published,
};
}
buildCombined(): ItemType[] {
let comments: ItemType[] = this.state.listCommentReportsResponse
.map(r => r.comment_reports)
.unwrapOr([])
.map(r => this.replyToReplyType(r));
.map(r => this.commentReportToItemType(r));
let posts: ItemType[] = this.state.listPostReportsResponse
.map(r => r.post_reports)
.unwrapOr([])
.map(r => this.mentionToReplyType(r));
.map(r => this.postReportToItemType(r));
let privateMessages: ItemType[] =
this.state.listPrivateMessageReportsResponse
.map(r => r.private_message_reports)
.unwrapOr([])
.map(r => this.privateMessageReportToItemType(r));
return [...comments, ...posts].sort((a, b) =>
return [...comments, ...posts, ...privateMessages].sort((a, b) =>
b.published.localeCompare(a.published)
);
}
@ -306,6 +362,13 @@ export class Reports extends Component<any, ReportsState> {
);
case MessageEnum.PostReport:
return <PostReport key={i.id} report={i.view as PostReportView} />;
case MessageEnum.PrivateMessageReport:
return (
<PrivateMessageReport
key={i.id}
report={i.view as PrivateMessageReportView}
/>
);
default:
return <div />;
}
@ -356,6 +419,25 @@ export class Reports extends Component<any, ReportsState> {
});
}
privateMessageReports() {
return this.state.listPrivateMessageReportsResponse.match({
some: res => (
<div>
{res.private_message_reports.map(pmr => (
<>
<hr />
<PrivateMessageReport
key={pmr.private_message_report.id}
report={pmr}
/>
</>
))}
</div>
),
none: <></>,
});
}
handlePageChange(page: number) {
this.setState({ page });
this.refetch();
@ -400,6 +482,18 @@ export class Reports extends Component<any, ReportsState> {
});
promises.push(req.client.listPostReports(postReportsForm));
if (amAdmin()) {
let privateMessageReportsForm = new ListPrivateMessageReports({
unresolved_only,
page,
limit,
auth,
});
promises.push(
req.client.listPrivateMessageReports(privateMessageReportsForm)
);
}
return promises;
}
@ -430,6 +524,18 @@ export class Reports extends Component<any, ReportsState> {
auth: auth().unwrap(),
});
WebSocketService.Instance.send(wsClient.listPostReports(postReportsForm));
if (amAdmin()) {
let privateMessageReportsForm = new ListPrivateMessageReports({
unresolved_only,
page,
limit,
auth: auth().unwrap(),
});
WebSocketService.Instance.send(
wsClient.listPrivateMessageReports(privateMessageReportsForm)
);
}
}
parseMessage(msg: any) {
@ -460,6 +566,16 @@ export class Reports extends Component<any, ReportsState> {
// this.sendUnreadCount();
window.scrollTo(0, 0);
setupTippy();
} else if (op == UserOperation.ListPrivateMessageReports) {
let data = wsJsonToRes<ListPrivateMessageReportsResponse>(
msg,
ListPrivateMessageReportsResponse
);
this.setState({ listPrivateMessageReportsResponse: Some(data) });
this.setState({ combined: this.buildCombined(), loading: false });
// this.sendUnreadCount();
window.scrollTo(0, 0);
setupTippy();
} else if (op == UserOperation.ResolvePostReport) {
let data = wsJsonToRes<PostReportResponse>(msg, PostReportResponse);
updatePostReportRes(
@ -488,6 +604,24 @@ export class Reports extends Component<any, ReportsState> {
urcs.next(urcs.getValue() + 1);
}
this.setState(this.state);
} else if (op == UserOperation.ResolvePrivateMessageReport) {
let data = wsJsonToRes<PrivateMessageReportResponse>(
msg,
PrivateMessageReportResponse
);
updatePrivateMessageReportRes(
data.private_message_report_view,
this.state.listPrivateMessageReportsResponse
.map(r => r.private_message_reports)
.unwrapOr([])
);
let urcs = UserService.Instance.unreadReportCountSub;
if (data.private_message_report_view.private_message_report.resolved) {
urcs.next(urcs.getValue() - 1);
} else {
urcs.next(urcs.getValue() + 1);
}
this.setState(this.state);
}
}
}

View file

@ -0,0 +1,92 @@
import { Component, linkEvent } from "inferno";
import { T } from "inferno-i18next-dess";
import {
PrivateMessageReportView,
ResolvePrivateMessageReport,
} from "lemmy-js-client";
import { i18n } from "../../i18next";
import { WebSocketService } from "../../services";
import { auth, mdToHtml, wsClient } from "../../utils";
import { Icon } from "../common/icon";
import { PersonListing } from "../person/person-listing";
interface Props {
report: PrivateMessageReportView;
}
export class PrivateMessageReport extends Component<Props, any> {
constructor(props: any, context: any) {
super(props, context);
}
render() {
let r = this.props.report;
let pmr = r.private_message_report;
let tippyContent = i18n.t(
r.private_message_report.resolved ? "unresolve_report" : "resolve_report"
);
return (
<div>
<div>
{i18n.t("creator")}:{" "}
<PersonListing person={r.private_message_creator} />
</div>
<div>
{i18n.t("message")}:
<div
className="md-div"
dangerouslySetInnerHTML={mdToHtml(pmr.original_pm_text)}
/>
</div>
<div>
{i18n.t("reporter")}: <PersonListing person={r.creator} />
</div>
<div>
{i18n.t("reason")}: {pmr.reason}
</div>
{r.resolver.match({
some: resolver => (
<div>
{pmr.resolved ? (
<T i18nKey="resolved_by">
#
<PersonListing person={resolver} />
</T>
) : (
<T i18nKey="unresolved_by">
#
<PersonListing person={resolver} />
</T>
)}
</div>
),
none: <></>,
})}
<button
className="btn btn-link btn-animate text-muted py-0"
onClick={linkEvent(this, this.handleResolveReport)}
data-tippy-content={tippyContent}
aria-label={tippyContent}
>
<Icon
icon="check"
classes={`icon-inline ${
pmr.resolved ? "text-success" : "text-danger"
}`}
/>
</button>
</div>
);
}
handleResolveReport(i: PrivateMessageReport) {
let pmr = i.props.report.private_message_report;
let form = new ResolvePrivateMessageReport({
report_id: pmr.id,
resolved: !pmr.resolved,
auth: auth().unwrap(),
});
WebSocketService.Instance.send(wsClient.resolvePrivateMessageReport(form));
}
}

View file

@ -1,10 +1,12 @@
import { None, Some } from "@sniptt/monads/build";
import { None, Option, Some } from "@sniptt/monads/build";
import { Component, linkEvent } from "inferno";
import {
CreatePrivateMessageReport,
DeletePrivateMessage,
MarkPrivateMessageAsRead,
PersonSafe,
PrivateMessageView,
toUndefined,
} from "lemmy-js-client";
import { i18n } from "../../i18next";
import { UserService, WebSocketService } from "../../services";
@ -19,6 +21,8 @@ interface PrivateMessageState {
showEdit: boolean;
collapsed: boolean;
viewSource: boolean;
showReportDialog: boolean;
reportReason: Option<string>;
}
interface PrivateMessageProps {
@ -34,6 +38,8 @@ export class PrivateMessage extends Component<
showEdit: false,
collapsed: false,
viewSource: false,
showReportDialog: false,
reportReason: None,
};
constructor(props: any, context: any) {
@ -140,6 +146,7 @@ export class PrivateMessage extends Component<
/>
</button>
</li>
<li className="list-inline-item">{this.reportButton}</li>
<li className="list-inline-item">
<button
className="btn btn-link btn-animate text-muted"
@ -209,6 +216,32 @@ export class PrivateMessage extends Component<
</div>
)}
</div>
{this.state.showReportDialog && (
<form
className="form-inline"
onSubmit={linkEvent(this, this.handleReportSubmit)}
>
<label className="sr-only" htmlFor="pm-report-reason">
{i18n.t("reason")}
</label>
<input
type="text"
id="pm-report-reason"
className="form-control mr-2"
placeholder={i18n.t("reason")}
required
value={toUndefined(this.state.reportReason)}
onInput={linkEvent(this, this.handleReportReasonChange)}
/>
<button
type="submit"
className="btn btn-secondary"
aria-label={i18n.t("create_report")}
>
{i18n.t("create_report")}
</button>
</form>
)}
{this.state.showReply && (
<PrivateMessageForm
recipient={otherPerson}
@ -222,6 +255,19 @@ export class PrivateMessage extends Component<
);
}
get reportButton() {
return (
<button
className="btn btn-link btn-animate text-muted py-0"
onClick={linkEvent(this, this.handleShowReportDialog)}
data-tippy-content={i18n.t("show_report_dialog")}
aria-label={i18n.t("show_report_dialog")}
>
<Icon icon="flag" inline />
</button>
);
}
get messageUnlessRemoved(): string {
let message = this.props.private_message_view.private_message;
return message.deleted ? `*${i18n.t("deleted")}*` : message.content;
@ -266,6 +312,26 @@ export class PrivateMessage extends Component<
i.setState({ viewSource: !i.state.viewSource });
}
handleShowReportDialog(i: PrivateMessage) {
i.setState({ showReportDialog: !i.state.showReportDialog });
}
handleReportReasonChange(i: PrivateMessage, event: any) {
i.setState({ reportReason: Some(event.target.value) });
}
handleReportSubmit(i: PrivateMessage, event: any) {
event.preventDefault();
let form = new CreatePrivateMessageReport({
private_message_id: i.props.private_message_view.private_message.id,
reason: toUndefined(i.state.reportReason),
auth: auth().unwrap(),
});
WebSocketService.Instance.send(wsClient.createPrivateMessageReport(form));
i.setState({ showReportDialog: false });
}
handlePrivateMessageEdit() {
this.setState({ showEdit: false });
}

View file

@ -23,6 +23,7 @@ import {
PersonViewSafe,
PostReportView,
PostView,
PrivateMessageReportView,
PrivateMessageView,
RegistrationApplicationView,
Search,
@ -246,14 +247,10 @@ export function isAdmin(
});
}
export function amAdmin(
admins: Option<PersonViewSafe[]>,
myUserInfo = UserService.Instance.myUserInfo
): boolean {
return myUserInfo.match({
some: mui => isAdmin(admins, mui.local_user_view.person.id),
none: false,
});
export function amAdmin(myUserInfo = UserService.Instance.myUserInfo): boolean {
return myUserInfo
.map(mui => mui.local_user_view.person.admin)
.unwrapOr(false);
}
export function amCommunityCreator(
@ -948,6 +945,7 @@ export function editPostRes(data: PostView, post: PostView) {
}
}
// TODO possible to make these generic?
export function updatePostReportRes(
data: PostReportView,
reports: PostReportView[]
@ -968,6 +966,18 @@ export function updateCommentReportRes(
}
}
export function updatePrivateMessageReportRes(
data: PrivateMessageReportView,
reports: PrivateMessageReportView[]
) {
let found = reports.find(
c => c.private_message_report.id == data.private_message_report.id
);
if (found) {
found.private_message_report = data.private_message_report;
}
}
export function updateRegistrationApplicationRes(
data: RegistrationApplicationView,
applications: RegistrationApplicationView[]
@ -1448,11 +1458,14 @@ export function myFirstDiscussionLanguageId(
);
}
export function canCreateCommunity(siteRes: GetSiteResponse): boolean {
export function canCreateCommunity(
siteRes: GetSiteResponse,
myUserInfo = UserService.Instance.myUserInfo
): boolean {
let adminOnly = siteRes.site_view
.map(s => s.site.community_creation_admin_only)
.unwrapOr(false);
return !adminOnly || amAdmin(Some(siteRes.admins));
return !adminOnly || amAdmin(myUserInfo);
}
export function isPostBlocked(

View file

@ -2657,7 +2657,7 @@ debug@^4.1.0, debug@^4.1.1, debug@^4.3.2, debug@^4.3.4:
dependencies:
ms "2.1.2"
debuglog@*, debuglog@^1.0.1:
debuglog@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/debuglog/-/debuglog-1.0.1.tgz#aa24ffb9ac3df9a2351837cfb2d279360cd78492"
integrity sha512-syBZ+rnAK3EgMsH2aYEOLUW7mZSY9Gb+0wUMCFsZvcmiz+HigA0LOcq/HoQqVuGG+EKykunc7QG2bzrponfaSw==
@ -4165,7 +4165,7 @@ import-sort@^6.0.0:
is-builtin-module "^3.0.0"
resolve "^1.8.1"
imurmurhash@*, imurmurhash@^0.1.4:
imurmurhash@^0.1.4:
version "0.1.4"
resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea"
integrity sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==
@ -4799,10 +4799,10 @@ lcid@^1.0.0:
dependencies:
invert-kv "^1.0.0"
lemmy-js-client@0.17.0-rc.45:
version "0.17.0-rc.45"
resolved "https://registry.yarnpkg.com/lemmy-js-client/-/lemmy-js-client-0.17.0-rc.45.tgz#903122b615d5dae10e65abffa1ee6b70b1a3e52c"
integrity sha512-jPeeDwGgMQHt6rYJfO7lzHQQ63Ox75Ojdihs7DsCUjLOMygantCsM6BD0xtPs3/rYV09zbYeNizKxEjv/wBnRA==
lemmy-js-client@0.17.0-rc.46:
version "0.17.0-rc.46"
resolved "https://registry.yarnpkg.com/lemmy-js-client/-/lemmy-js-client-0.17.0-rc.46.tgz#c2820821ca46394fd17d1045e54c00a04b15700c"
integrity sha512-9HqKKsvToSB397ywXpl0jPa7KIhDaULWel0g35CgmfOkylvuTlpF8UZW20vMXr02Br9qvbRf0hRvi9s5uaji+Q==
levn@^0.4.1:
version "0.4.1"
@ -4967,11 +4967,6 @@ lockfile@^1.0.4:
dependencies:
signal-exit "^3.0.2"
lodash._baseindexof@*:
version "3.1.0"
resolved "https://registry.yarnpkg.com/lodash._baseindexof/-/lodash._baseindexof-3.1.0.tgz#fe52b53a1c6761e42618d654e4a25789ed61822c"
integrity sha512-bSYo8Pc/f0qAkr8fPJydpJjtrHiSynYfYBjtANIgXv5xEf1WlTC63dIDlgu0s9dmTvzRu1+JJTxcIAHe+sH0FQ==
lodash._baseuniq@~4.6.0:
version "4.6.0"
resolved "https://registry.yarnpkg.com/lodash._baseuniq/-/lodash._baseuniq-4.6.0.tgz#0ebb44e456814af7905c6212fa2c9b2d51b841e8"
@ -4980,33 +4975,11 @@ lodash._baseuniq@~4.6.0:
lodash._createset "~4.0.0"
lodash._root "~3.0.0"
lodash._bindcallback@*:
version "3.0.1"
resolved "https://registry.yarnpkg.com/lodash._bindcallback/-/lodash._bindcallback-3.0.1.tgz#e531c27644cf8b57a99e17ed95b35c748789392e"
integrity sha512-2wlI0JRAGX8WEf4Gm1p/mv/SZ+jLijpj0jyaE/AXeuQphzCgD8ZQW4oSpoN8JAopujOFGU3KMuq7qfHBWlGpjQ==
lodash._cacheindexof@*:
version "3.0.2"
resolved "https://registry.yarnpkg.com/lodash._cacheindexof/-/lodash._cacheindexof-3.0.2.tgz#3dc69ac82498d2ee5e3ce56091bafd2adc7bde92"
integrity sha512-S8dUjWr7SUT/X6TBIQ/OYoCHo1Stu1ZRy6uMUSKqzFnZp5G5RyQizSm6kvxD2Ewyy6AVfMg4AToeZzKfF99T5w==
lodash._createcache@*:
version "3.1.2"
resolved "https://registry.yarnpkg.com/lodash._createcache/-/lodash._createcache-3.1.2.tgz#56d6a064017625e79ebca6b8018e17440bdcf093"
integrity sha512-ev5SP+iFpZOugyab/DEUQxUeZP5qyciVTlgQ1f4Vlw7VUcCD8fVnyIqVUEIaoFH9zjAqdgi69KiofzvVmda/ZQ==
dependencies:
lodash._getnative "^3.0.0"
lodash._createset@~4.0.0:
version "4.0.3"
resolved "https://registry.yarnpkg.com/lodash._createset/-/lodash._createset-4.0.3.tgz#0f4659fbb09d75194fa9e2b88a6644d363c9fe26"
integrity sha512-GTkC6YMprrJZCYU3zcqZj+jkXkrXzq3IPBcF/fIPpNEAB4hZEtXU8zp/RwKOvZl43NUmwDbyRk3+ZTbeRdEBXA==
lodash._getnative@*, lodash._getnative@^3.0.0:
version "3.9.1"
resolved "https://registry.yarnpkg.com/lodash._getnative/-/lodash._getnative-3.9.1.tgz#570bc7dede46d61cdcde687d65d3eecbaa3aaff5"
integrity sha512-RrL9VxMEPyDMHOd9uFbvMe8X55X16/cGM5IgOKgRElQZutpX89iS6vwl64duTV1/16w5JY7tuFNXqoekmh1EmA==
lodash._root@~3.0.0:
version "3.0.1"
resolved "https://registry.yarnpkg.com/lodash._root/-/lodash._root-3.0.1.tgz#fba1c4524c19ee9a5f8136b4609f017cf4ded692"
@ -5032,11 +5005,6 @@ lodash.pick@^4.4.0:
resolved "https://registry.yarnpkg.com/lodash.pick/-/lodash.pick-4.4.0.tgz#52f05610fff9ded422611441ed1fc123a03001b3"
integrity sha512-hXt6Ul/5yWjfklSGvLQl8vM//l3FtyHZeuelpzK6mm99pNvN9yTDruNZPEJZD1oWrqo+izBmB7oUfWgcCX7s4Q==
lodash.restparam@*:
version "3.6.1"
resolved "https://registry.yarnpkg.com/lodash.restparam/-/lodash.restparam-3.6.1.tgz#936a4e309ef330a7645ed4145986c85ae5b20805"
integrity sha512-L4/arjjuq4noiUJpt3yS6KIKDtJwNe2fIYgMqyYYKoeIfV1iEqvPwhCx23o+R9dzouGihDAPN1dTIRWa7zk8tw==
lodash.union@~4.6.0:
version "4.6.0"
resolved "https://registry.yarnpkg.com/lodash.union/-/lodash.union-4.6.0.tgz#48bb5088409f16f1821666641c44dd1aaae3cd88"
@ -6725,7 +6693,7 @@ readable-stream@~1.1.10:
isarray "0.0.1"
string_decoder "~0.10.x"
readdir-scoped-modules@*, readdir-scoped-modules@^1.0.0:
readdir-scoped-modules@^1.0.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/readdir-scoped-modules/-/readdir-scoped-modules-1.1.0.tgz#8d45407b4f870a0dcaebc0e28670d18e74514309"
integrity sha512-asaikDeqAQg7JifRsZn1NJZXo9E+VwlyCfbkZhwyISinqk5zNS6266HS5kah6P0SaQKGF6SkNnZVHUzHFYxYDw==