mirror of
https://github.com/LemmyNet/lemmy-ui.git
synced 2024-11-29 07:41:13 +00:00
Reporting (#434)
* Updating translations. * A first pass at reporting. Fixes #102
This commit is contained in:
parent
65be5c7833
commit
06099fee8a
17 changed files with 1029 additions and 85 deletions
|
@ -1 +1 @@
|
||||||
Subproject commit 7dd7b98da76477222f9fd9720b4b25e14e3ddc97
|
Subproject commit 9a584ef77e7861466bd5f44dd87d3681d4871a60
|
|
@ -72,7 +72,7 @@
|
||||||
"husky": "^7.0.1",
|
"husky": "^7.0.1",
|
||||||
"import-sort-style-module": "^6.0.0",
|
"import-sort-style-module": "^6.0.0",
|
||||||
"iso-639-1": "^2.1.9",
|
"iso-639-1": "^2.1.9",
|
||||||
"lemmy-js-client": "0.12.0",
|
"lemmy-js-client": "0.12.3-rc.5",
|
||||||
"lint-staged": "^11.0.1",
|
"lint-staged": "^11.0.1",
|
||||||
"mini-css-extract-plugin": "^2.1.0",
|
"mini-css-extract-plugin": "^2.1.0",
|
||||||
"node-fetch": "^2.6.1",
|
"node-fetch": "^2.6.1",
|
||||||
|
|
|
@ -8,6 +8,8 @@ import {
|
||||||
GetPrivateMessages,
|
GetPrivateMessages,
|
||||||
GetReplies,
|
GetReplies,
|
||||||
GetRepliesResponse,
|
GetRepliesResponse,
|
||||||
|
GetReportCount,
|
||||||
|
GetReportCountResponse,
|
||||||
GetSiteResponse,
|
GetSiteResponse,
|
||||||
PrivateMessageResponse,
|
PrivateMessageResponse,
|
||||||
PrivateMessagesResponse,
|
PrivateMessagesResponse,
|
||||||
|
@ -48,7 +50,8 @@ interface NavbarState {
|
||||||
replies: CommentView[];
|
replies: CommentView[];
|
||||||
mentions: CommentView[];
|
mentions: CommentView[];
|
||||||
messages: PrivateMessageView[];
|
messages: PrivateMessageView[];
|
||||||
unreadCount: number;
|
unreadInboxCount: number;
|
||||||
|
unreadReportCount: number;
|
||||||
searchParam: string;
|
searchParam: string;
|
||||||
toggleSearch: boolean;
|
toggleSearch: boolean;
|
||||||
showDropdown: boolean;
|
showDropdown: boolean;
|
||||||
|
@ -58,11 +61,13 @@ interface NavbarState {
|
||||||
export class Navbar extends Component<NavbarProps, NavbarState> {
|
export class Navbar extends Component<NavbarProps, NavbarState> {
|
||||||
private wsSub: Subscription;
|
private wsSub: Subscription;
|
||||||
private userSub: Subscription;
|
private userSub: Subscription;
|
||||||
private unreadCountSub: Subscription;
|
private unreadInboxCountSub: Subscription;
|
||||||
|
private unreadReportCountSub: Subscription;
|
||||||
private searchTextField: RefObject<HTMLInputElement>;
|
private searchTextField: RefObject<HTMLInputElement>;
|
||||||
emptyState: NavbarState = {
|
emptyState: NavbarState = {
|
||||||
isLoggedIn: !!this.props.site_res.my_user,
|
isLoggedIn: !!this.props.site_res.my_user,
|
||||||
unreadCount: 0,
|
unreadInboxCount: 0,
|
||||||
|
unreadReportCount: 0,
|
||||||
replies: [],
|
replies: [],
|
||||||
mentions: [],
|
mentions: [],
|
||||||
messages: [],
|
messages: [],
|
||||||
|
@ -117,18 +122,23 @@ export class Navbar extends Component<NavbarProps, NavbarState> {
|
||||||
});
|
});
|
||||||
|
|
||||||
// Subscribe to unread count changes
|
// Subscribe to unread count changes
|
||||||
this.unreadCountSub = UserService.Instance.unreadCountSub.subscribe(
|
this.unreadInboxCountSub =
|
||||||
res => {
|
UserService.Instance.unreadInboxCountSub.subscribe(res => {
|
||||||
this.setState({ unreadCount: res });
|
this.setState({ unreadInboxCount: res });
|
||||||
}
|
});
|
||||||
);
|
// Subscribe to unread report count changes
|
||||||
|
this.unreadReportCountSub =
|
||||||
|
UserService.Instance.unreadReportCountSub.subscribe(res => {
|
||||||
|
this.setState({ unreadReportCount: res });
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillUnmount() {
|
componentWillUnmount() {
|
||||||
this.wsSub.unsubscribe();
|
this.wsSub.unsubscribe();
|
||||||
this.userSub.unsubscribe();
|
this.userSub.unsubscribe();
|
||||||
this.unreadCountSub.unsubscribe();
|
this.unreadInboxCountSub.unsubscribe();
|
||||||
|
this.unreadReportCountSub.unsubscribe();
|
||||||
}
|
}
|
||||||
|
|
||||||
updateUrl() {
|
updateUrl() {
|
||||||
|
@ -177,23 +187,48 @@ export class Navbar extends Component<NavbarProps, NavbarState> {
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
{this.state.isLoggedIn && (
|
{this.state.isLoggedIn && (
|
||||||
|
<>
|
||||||
|
<ul class="navbar-nav ml-auto">
|
||||||
|
<li className="nav-item">
|
||||||
<button
|
<button
|
||||||
className="ml-auto p-1 navbar-toggler nav-link border-0 btn btn-link"
|
className="p-1 navbar-toggler nav-link border-0 btn btn-link"
|
||||||
onClick={linkEvent(this, this.handleGotoInbox)}
|
onClick={linkEvent(this, this.handleGotoInbox)}
|
||||||
title={i18n.t("inbox")}
|
title={i18n.t("unread_messages", {
|
||||||
|
count: this.state.unreadInboxCount,
|
||||||
|
formattedCount: numToSI(this.state.unreadInboxCount),
|
||||||
|
})}
|
||||||
>
|
>
|
||||||
<Icon icon="bell" />
|
<Icon icon="bell" />
|
||||||
{this.state.unreadCount > 0 && (
|
{this.state.unreadInboxCount > 0 && (
|
||||||
<span
|
<span class="mx-1 badge badge-light">
|
||||||
class="mx-1 badge badge-light"
|
{numToSI(this.state.unreadInboxCount)}
|
||||||
aria-label={`${this.state.unreadCount} ${i18n.t(
|
|
||||||
"unread_messages"
|
|
||||||
)}`}
|
|
||||||
>
|
|
||||||
{numToSI(this.state.unreadCount)}
|
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
</button>
|
</button>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
{UserService.Instance.myUserInfo?.moderates.length > 0 && (
|
||||||
|
<ul class="navbar-nav ml-1">
|
||||||
|
<li className="nav-item">
|
||||||
|
<button
|
||||||
|
className="p-1 navbar-toggler nav-link border-0 btn btn-link"
|
||||||
|
onClick={linkEvent(this, this.handleGotoReports)}
|
||||||
|
title={i18n.t("unread_reports", {
|
||||||
|
count: this.state.unreadReportCount,
|
||||||
|
formattedCount: numToSI(this.state.unreadReportCount),
|
||||||
|
})}
|
||||||
|
>
|
||||||
|
<Icon icon="shield" />
|
||||||
|
{this.state.unreadReportCount > 0 && (
|
||||||
|
<span class="mx-1 badge badge-light">
|
||||||
|
{numToSI(this.state.unreadReportCount)}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
)}
|
)}
|
||||||
<button
|
<button
|
||||||
class="navbar-toggler border-0 p-1"
|
class="navbar-toggler border-0 p-1"
|
||||||
|
@ -300,22 +335,41 @@ export class Navbar extends Component<NavbarProps, NavbarState> {
|
||||||
<Link
|
<Link
|
||||||
className="nav-link"
|
className="nav-link"
|
||||||
to="/inbox"
|
to="/inbox"
|
||||||
title={i18n.t("inbox")}
|
title={i18n.t("unread_messages", {
|
||||||
|
count: this.state.unreadInboxCount,
|
||||||
|
formattedCount: numToSI(this.state.unreadInboxCount),
|
||||||
|
})}
|
||||||
>
|
>
|
||||||
<Icon icon="bell" />
|
<Icon icon="bell" />
|
||||||
{this.state.unreadCount > 0 && (
|
{this.state.unreadInboxCount > 0 && (
|
||||||
<span
|
<span class="ml-1 badge badge-light">
|
||||||
class="ml-1 badge badge-light"
|
{numToSI(this.state.unreadInboxCount)}
|
||||||
aria-label={`${this.state.unreadCount} ${i18n.t(
|
|
||||||
"unread_messages"
|
|
||||||
)}`}
|
|
||||||
>
|
|
||||||
{numToSI(this.state.unreadCount)}
|
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
</Link>
|
</Link>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
{UserService.Instance.myUserInfo?.moderates.length > 0 && (
|
||||||
|
<ul class="navbar-nav my-2">
|
||||||
|
<li className="nav-item">
|
||||||
|
<Link
|
||||||
|
className="nav-link"
|
||||||
|
to="/reports"
|
||||||
|
title={i18n.t("unread_reports", {
|
||||||
|
count: this.state.unreadReportCount,
|
||||||
|
formattedCount: numToSI(this.state.unreadReportCount),
|
||||||
|
})}
|
||||||
|
>
|
||||||
|
<Icon icon="shield" />
|
||||||
|
{this.state.unreadReportCount > 0 && (
|
||||||
|
<span class="ml-1 badge badge-light">
|
||||||
|
{numToSI(this.state.unreadReportCount)}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</Link>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
)}
|
||||||
<ul class="navbar-nav">
|
<ul class="navbar-nav">
|
||||||
<li class="nav-item dropdown">
|
<li class="nav-item dropdown">
|
||||||
<button
|
<button
|
||||||
|
@ -481,6 +535,11 @@ export class Navbar extends Component<NavbarProps, NavbarState> {
|
||||||
i.context.router.history.push(`/inbox`);
|
i.context.router.history.push(`/inbox`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleGotoReports(i: Navbar) {
|
||||||
|
i.setState({ showDropdown: false, expanded: false });
|
||||||
|
i.context.router.history.push(`/reports`);
|
||||||
|
}
|
||||||
|
|
||||||
handleGotoAdmin(i: Navbar) {
|
handleGotoAdmin(i: Navbar) {
|
||||||
i.setState({ showDropdown: false, expanded: false });
|
i.setState({ showDropdown: false, expanded: false });
|
||||||
i.context.router.history.push(`/admin`);
|
i.context.router.history.push(`/admin`);
|
||||||
|
@ -523,7 +582,7 @@ export class Navbar extends Component<NavbarProps, NavbarState> {
|
||||||
let unreadReplies = data.replies.filter(r => !r.comment.read);
|
let unreadReplies = data.replies.filter(r => !r.comment.read);
|
||||||
|
|
||||||
this.state.replies = unreadReplies;
|
this.state.replies = unreadReplies;
|
||||||
this.state.unreadCount = this.calculateUnreadCount();
|
this.state.unreadInboxCount = this.calculateUnreadInboxCount();
|
||||||
this.setState(this.state);
|
this.setState(this.state);
|
||||||
this.sendUnreadCount();
|
this.sendUnreadCount();
|
||||||
} else if (op == UserOperation.GetPersonMentions) {
|
} else if (op == UserOperation.GetPersonMentions) {
|
||||||
|
@ -531,7 +590,7 @@ export class Navbar extends Component<NavbarProps, NavbarState> {
|
||||||
let unreadMentions = data.mentions.filter(r => !r.comment.read);
|
let unreadMentions = data.mentions.filter(r => !r.comment.read);
|
||||||
|
|
||||||
this.state.mentions = unreadMentions;
|
this.state.mentions = unreadMentions;
|
||||||
this.state.unreadCount = this.calculateUnreadCount();
|
this.state.unreadInboxCount = this.calculateUnreadInboxCount();
|
||||||
this.setState(this.state);
|
this.setState(this.state);
|
||||||
this.sendUnreadCount();
|
this.sendUnreadCount();
|
||||||
} else if (op == UserOperation.GetPrivateMessages) {
|
} else if (op == UserOperation.GetPrivateMessages) {
|
||||||
|
@ -541,9 +600,14 @@ export class Navbar extends Component<NavbarProps, NavbarState> {
|
||||||
);
|
);
|
||||||
|
|
||||||
this.state.messages = unreadMessages;
|
this.state.messages = unreadMessages;
|
||||||
this.state.unreadCount = this.calculateUnreadCount();
|
this.state.unreadInboxCount = this.calculateUnreadInboxCount();
|
||||||
this.setState(this.state);
|
this.setState(this.state);
|
||||||
this.sendUnreadCount();
|
this.sendUnreadCount();
|
||||||
|
} else if (op == UserOperation.GetReportCount) {
|
||||||
|
let data = wsJsonToRes<GetReportCountResponse>(msg).data;
|
||||||
|
this.state.unreadReportCount = data.post_reports + data.comment_reports;
|
||||||
|
this.setState(this.state);
|
||||||
|
this.sendReportUnread();
|
||||||
} else if (op == UserOperation.GetSite) {
|
} else if (op == UserOperation.GetSite) {
|
||||||
// This is only called on a successful login
|
// This is only called on a successful login
|
||||||
let data = wsJsonToRes<GetSiteResponse>(msg).data;
|
let data = wsJsonToRes<GetSiteResponse>(msg).data;
|
||||||
|
@ -565,7 +629,7 @@ export class Navbar extends Component<NavbarProps, NavbarState> {
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
this.state.replies.push(data.comment_view);
|
this.state.replies.push(data.comment_view);
|
||||||
this.state.unreadCount++;
|
this.state.unreadInboxCount++;
|
||||||
this.setState(this.state);
|
this.setState(this.state);
|
||||||
this.sendUnreadCount();
|
this.sendUnreadCount();
|
||||||
notifyComment(data.comment_view, this.context.router);
|
notifyComment(data.comment_view, this.context.router);
|
||||||
|
@ -580,7 +644,7 @@ export class Navbar extends Component<NavbarProps, NavbarState> {
|
||||||
UserService.Instance.myUserInfo.local_user_view.person.id
|
UserService.Instance.myUserInfo.local_user_view.person.id
|
||||||
) {
|
) {
|
||||||
this.state.messages.push(data.private_message_view);
|
this.state.messages.push(data.private_message_view);
|
||||||
this.state.unreadCount++;
|
this.state.unreadInboxCount++;
|
||||||
this.setState(this.state);
|
this.setState(this.state);
|
||||||
this.sendUnreadCount();
|
this.sendUnreadCount();
|
||||||
notifyPrivateMessage(data.private_message_view, this.context.router);
|
notifyPrivateMessage(data.private_message_view, this.context.router);
|
||||||
|
@ -590,7 +654,10 @@ export class Navbar extends Component<NavbarProps, NavbarState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fetchUnreads() {
|
fetchUnreads() {
|
||||||
console.log("Fetching unreads...");
|
// TODO we should just add a count call to the API for these, because this is a limited fetch,
|
||||||
|
// and it shouldn't have to fetch the actual content
|
||||||
|
if (this.currentLocation !== "/inbox") {
|
||||||
|
console.log("Fetching inbox unreads...");
|
||||||
let repliesForm: GetReplies = {
|
let repliesForm: GetReplies = {
|
||||||
sort: SortType.New,
|
sort: SortType.New,
|
||||||
unread_only: true,
|
unread_only: true,
|
||||||
|
@ -614,7 +681,6 @@ export class Navbar extends Component<NavbarProps, NavbarState> {
|
||||||
auth: authField(),
|
auth: authField(),
|
||||||
};
|
};
|
||||||
|
|
||||||
if (this.currentLocation !== "/inbox") {
|
|
||||||
WebSocketService.Instance.send(wsClient.getReplies(repliesForm));
|
WebSocketService.Instance.send(wsClient.getReplies(repliesForm));
|
||||||
WebSocketService.Instance.send(
|
WebSocketService.Instance.send(
|
||||||
wsClient.getPersonMentions(personMentionsForm)
|
wsClient.getPersonMentions(personMentionsForm)
|
||||||
|
@ -623,6 +689,14 @@ export class Navbar extends Component<NavbarProps, NavbarState> {
|
||||||
wsClient.getPrivateMessages(privateMessagesForm)
|
wsClient.getPrivateMessages(privateMessagesForm)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
console.log("Fetching reports...");
|
||||||
|
|
||||||
|
let reportCountForm: GetReportCount = {
|
||||||
|
auth: authField(),
|
||||||
|
};
|
||||||
|
|
||||||
|
WebSocketService.Instance.send(wsClient.getReportCount(reportCountForm));
|
||||||
}
|
}
|
||||||
|
|
||||||
get currentLocation() {
|
get currentLocation() {
|
||||||
|
@ -630,10 +704,16 @@ export class Navbar extends Component<NavbarProps, NavbarState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
sendUnreadCount() {
|
sendUnreadCount() {
|
||||||
UserService.Instance.unreadCountSub.next(this.state.unreadCount);
|
UserService.Instance.unreadInboxCountSub.next(this.state.unreadInboxCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
calculateUnreadCount(): number {
|
sendReportUnread() {
|
||||||
|
UserService.Instance.unreadReportCountSub.next(
|
||||||
|
this.state.unreadReportCount
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
calculateUnreadInboxCount(): number {
|
||||||
return (
|
return (
|
||||||
this.state.replies.filter(r => !r.comment.read).length +
|
this.state.replies.filter(r => !r.comment.read).length +
|
||||||
this.state.mentions.filter(r => !r.comment.read).length +
|
this.state.mentions.filter(r => !r.comment.read).length +
|
||||||
|
|
|
@ -9,6 +9,7 @@ import {
|
||||||
CommentView,
|
CommentView,
|
||||||
CommunityModeratorView,
|
CommunityModeratorView,
|
||||||
CreateCommentLike,
|
CreateCommentLike,
|
||||||
|
CreateCommentReport,
|
||||||
DeleteComment,
|
DeleteComment,
|
||||||
MarkCommentAsRead,
|
MarkCommentAsRead,
|
||||||
MarkPersonMentionAsRead,
|
MarkPersonMentionAsRead,
|
||||||
|
@ -59,6 +60,8 @@ interface CommentNodeState {
|
||||||
collapsed: boolean;
|
collapsed: boolean;
|
||||||
viewSource: boolean;
|
viewSource: boolean;
|
||||||
showAdvanced: boolean;
|
showAdvanced: boolean;
|
||||||
|
showReportDialog: boolean;
|
||||||
|
reportReason: string;
|
||||||
my_vote: number;
|
my_vote: number;
|
||||||
score: number;
|
score: number;
|
||||||
upvotes: number;
|
upvotes: number;
|
||||||
|
@ -102,6 +105,8 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
|
||||||
showConfirmTransferCommunity: false,
|
showConfirmTransferCommunity: false,
|
||||||
showConfirmAppointAsMod: false,
|
showConfirmAppointAsMod: false,
|
||||||
showConfirmAppointAsAdmin: false,
|
showConfirmAppointAsAdmin: false,
|
||||||
|
showReportDialog: false,
|
||||||
|
reportReason: null,
|
||||||
my_vote: this.props.node.comment_view.my_vote,
|
my_vote: this.props.node.comment_view.my_vote,
|
||||||
score: this.props.node.comment_view.counts.score,
|
score: this.props.node.comment_view.counts.score,
|
||||||
upvotes: this.props.node.comment_view.counts.upvotes,
|
upvotes: this.props.node.comment_view.counts.upvotes,
|
||||||
|
@ -350,6 +355,19 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
|
||||||
<Icon icon="mail" />
|
<Icon icon="mail" />
|
||||||
</Link>
|
</Link>
|
||||||
</button>
|
</button>
|
||||||
|
<button
|
||||||
|
class="btn btn-link btn-animate text-muted"
|
||||||
|
onClick={linkEvent(
|
||||||
|
this,
|
||||||
|
this.handleShowReportDialog
|
||||||
|
)}
|
||||||
|
data-tippy-content={i18n.t(
|
||||||
|
"show_report_dialog"
|
||||||
|
)}
|
||||||
|
aria-label={i18n.t("show_report_dialog")}
|
||||||
|
>
|
||||||
|
<Icon icon="flag" />
|
||||||
|
</button>
|
||||||
<button
|
<button
|
||||||
class="btn btn-link btn-animate text-muted"
|
class="btn btn-link btn-animate text-muted"
|
||||||
onClick={linkEvent(
|
onClick={linkEvent(
|
||||||
|
@ -746,6 +764,32 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
|
||||||
</button>
|
</button>
|
||||||
</form>
|
</form>
|
||||||
)}
|
)}
|
||||||
|
{this.state.showReportDialog && (
|
||||||
|
<form
|
||||||
|
class="form-inline"
|
||||||
|
onSubmit={linkEvent(this, this.handleReportSubmit)}
|
||||||
|
>
|
||||||
|
<label class="sr-only" htmlFor={`report-reason-${cv.comment.id}`}>
|
||||||
|
{i18n.t("reason")}
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
required
|
||||||
|
id={`report-reason-${cv.comment.id}`}
|
||||||
|
class="form-control mr-2"
|
||||||
|
placeholder={i18n.t("reason")}
|
||||||
|
value={this.state.reportReason}
|
||||||
|
onInput={linkEvent(this, this.handleReportReasonChange)}
|
||||||
|
/>
|
||||||
|
<button
|
||||||
|
type="submit"
|
||||||
|
class="btn btn-secondary"
|
||||||
|
aria-label={i18n.t("create_report")}
|
||||||
|
>
|
||||||
|
{i18n.t("create_report")}
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
)}
|
||||||
{this.state.showBanDialog && (
|
{this.state.showBanDialog && (
|
||||||
<form onSubmit={linkEvent(this, this.handleModBanBothSubmit)}>
|
<form onSubmit={linkEvent(this, this.handleModBanBothSubmit)}>
|
||||||
<div class="form-group row">
|
<div class="form-group row">
|
||||||
|
@ -1043,6 +1087,29 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
|
||||||
setupTippy();
|
setupTippy();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleShowReportDialog(i: CommentNode) {
|
||||||
|
i.state.showReportDialog = !i.state.showReportDialog;
|
||||||
|
i.setState(i.state);
|
||||||
|
}
|
||||||
|
|
||||||
|
handleReportReasonChange(i: CommentNode, event: any) {
|
||||||
|
i.state.reportReason = event.target.value;
|
||||||
|
i.setState(i.state);
|
||||||
|
}
|
||||||
|
|
||||||
|
handleReportSubmit(i: CommentNode) {
|
||||||
|
let comment = i.props.node.comment_view.comment;
|
||||||
|
let form: CreateCommentReport = {
|
||||||
|
comment_id: comment.id,
|
||||||
|
reason: i.state.reportReason,
|
||||||
|
auth: authField(),
|
||||||
|
};
|
||||||
|
WebSocketService.Instance.send(wsClient.createCommentReport(form));
|
||||||
|
|
||||||
|
i.state.showReportDialog = false;
|
||||||
|
i.setState(i.state);
|
||||||
|
}
|
||||||
|
|
||||||
handleModRemoveShow(i: CommentNode) {
|
handleModRemoveShow(i: CommentNode) {
|
||||||
i.state.showRemoveDialog = true;
|
i.state.showRemoveDialog = true;
|
||||||
i.setState(i.state);
|
i.setState(i.state);
|
||||||
|
|
107
src/shared/components/comment/comment_report.tsx
Normal file
107
src/shared/components/comment/comment_report.tsx
Normal file
|
@ -0,0 +1,107 @@
|
||||||
|
import { Component, linkEvent } from "inferno";
|
||||||
|
import { T } from "inferno-i18next-dess";
|
||||||
|
import {
|
||||||
|
CommentReportView,
|
||||||
|
CommentView,
|
||||||
|
ResolveCommentReport,
|
||||||
|
} from "lemmy-js-client";
|
||||||
|
import { i18n } from "../../i18next";
|
||||||
|
import { CommentNode as CommentNodeI } from "../../interfaces";
|
||||||
|
import { WebSocketService } from "../../services";
|
||||||
|
import { authField, wsClient } from "../../utils";
|
||||||
|
import { Icon } from "../common/icon";
|
||||||
|
import { PersonListing } from "../person/person-listing";
|
||||||
|
import { CommentNode } from "./comment-node";
|
||||||
|
|
||||||
|
interface CommentReportProps {
|
||||||
|
report: CommentReportView;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class CommentReport extends Component<CommentReportProps, any> {
|
||||||
|
constructor(props: any, context: any) {
|
||||||
|
super(props, context);
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
let r = this.props.report;
|
||||||
|
let comment = r.comment;
|
||||||
|
|
||||||
|
// Set the original post data ( a troll could change it )
|
||||||
|
comment.content = r.comment_report.original_comment_text;
|
||||||
|
|
||||||
|
let comment_view: CommentView = {
|
||||||
|
comment,
|
||||||
|
creator: r.comment_creator,
|
||||||
|
post: r.post,
|
||||||
|
community: r.community,
|
||||||
|
creator_banned_from_community: r.creator_banned_from_community,
|
||||||
|
counts: r.counts,
|
||||||
|
subscribed: false,
|
||||||
|
saved: false,
|
||||||
|
creator_blocked: false,
|
||||||
|
my_vote: r.my_vote,
|
||||||
|
};
|
||||||
|
|
||||||
|
let node: CommentNodeI = {
|
||||||
|
comment_view,
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<CommentNode
|
||||||
|
node={node}
|
||||||
|
moderators={[]}
|
||||||
|
admins={[]}
|
||||||
|
enableDownvotes={true}
|
||||||
|
/>
|
||||||
|
<div>
|
||||||
|
{i18n.t("reporter")}: <PersonListing person={r.creator} />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
{i18n.t("reason")}: {r.comment_report.reason}
|
||||||
|
</div>
|
||||||
|
{r.resolver && (
|
||||||
|
<div>
|
||||||
|
{r.comment_report.resolved ? (
|
||||||
|
<T i18nKey="resolved_by">
|
||||||
|
#
|
||||||
|
<PersonListing person={r.resolver} />
|
||||||
|
</T>
|
||||||
|
) : (
|
||||||
|
<T i18nKey="unresolved_by">
|
||||||
|
#
|
||||||
|
<PersonListing person={r.resolver} />
|
||||||
|
</T>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<button
|
||||||
|
className="btn btn-link btn-animate text-muted py-0"
|
||||||
|
onClick={linkEvent(this, this.handleResolveReport)}
|
||||||
|
data-tippy-content={
|
||||||
|
r.comment_report.resolved ? "unresolve_report" : "resolve_report"
|
||||||
|
}
|
||||||
|
aria-label={
|
||||||
|
r.comment_report.resolved ? "unresolve_report" : "resolve_report"
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<Icon
|
||||||
|
icon="check"
|
||||||
|
classes={`icon-inline ${
|
||||||
|
r.comment_report.resolved ? "text-success" : "text-danger"
|
||||||
|
}`}
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
handleResolveReport(i: CommentReport) {
|
||||||
|
let form: ResolveCommentReport = {
|
||||||
|
report_id: i.props.report.comment_report.id,
|
||||||
|
resolved: !i.props.report.comment_report.resolved,
|
||||||
|
auth: authField(),
|
||||||
|
};
|
||||||
|
WebSocketService.Instance.send(wsClient.resolveCommentReport(form));
|
||||||
|
}
|
||||||
|
}
|
|
@ -12,6 +12,12 @@ export const SYMBOLS = (
|
||||||
xmlnsXlink="http://www.w3.org/1999/xlink"
|
xmlnsXlink="http://www.w3.org/1999/xlink"
|
||||||
>
|
>
|
||||||
<defs>
|
<defs>
|
||||||
|
<symbol id="icon-shield" viewBox="0 0 24 24">
|
||||||
|
<path d="M12 20.862c-1.184-0.672-4.42-2.695-6.050-5.549-0.079-0.138-0.153-0.276-0.223-0.417-0.456-0.911-0.727-1.878-0.727-2.896v-6.307l7-2.625 7 2.625v6.307c0 1.018-0.271 1.985-0.726 2.897-0.070 0.14-0.145 0.279-0.223 0.417-1.631 2.854-4.867 4.876-6.050 5.549zM12.447 22.894c0 0 4.989-2.475 7.34-6.589 0.096-0.168 0.188-0.34 0.276-0.515 0.568-1.135 0.937-2.408 0.937-3.79v-7c0-0.426-0.267-0.79-0.649-0.936l-8-3c-0.236-0.089-0.485-0.082-0.702 0l-8 3c-0.399 0.149-0.646 0.527-0.649 0.936v7c0 1.382 0.369 2.655 0.938 3.791 0.087 0.175 0.179 0.346 0.276 0.515 2.351 4.114 7.34 6.589 7.34 6.589 0.292 0.146 0.62 0.136 0.894 0z"></path>
|
||||||
|
</symbol>
|
||||||
|
<symbol id="icon-flag" viewBox="0 0 24 24">
|
||||||
|
<path d="M5 13.397v-9.859c0.44-0.218 1.365-0.538 3-0.538 1.281 0 2.361 0.421 3.629 0.928 1.232 0.493 2.652 1.072 4.371 1.072 1.298 0 2.278-0.175 3-0.397v9.859c-0.44 0.218-1.365 0.538-3 0.538-1.281 0-2.361-0.421-3.629-0.928-1.232-0.493-2.652-1.072-4.371-1.072-1.298 0-2.278 0.175-3 0.397zM5 22v-6.462c0.44-0.218 1.365-0.538 3-0.538 1.281 0 2.361 0.421 3.629 0.928 1.232 0.493 2.652 1.072 4.371 1.072 3.247 0 4.507-1.093 4.707-1.293 0.195-0.195 0.293-0.451 0.293-0.707v-12c0-0.552-0.448-1-1-1-0.265 0-0.506 0.103-0.685 0.272-0.096 0.078-0.984 0.728-3.315 0.728-1.281 0-2.361-0.421-3.629-0.928-1.232-0.493-2.652-1.072-4.371-1.072-3.247 0-4.507 1.093-4.707 1.293-0.195 0.195-0.293 0.451-0.293 0.707v19c0 0.552 0.448 1 1 1s1-0.448 1-1z"></path>
|
||||||
|
</symbol>
|
||||||
<symbol id="icon-log-out" viewBox="0 0 24 24">
|
<symbol id="icon-log-out" viewBox="0 0 24 24">
|
||||||
<path d="M9 20h-4c-0.276 0-0.525-0.111-0.707-0.293s-0.293-0.431-0.293-0.707v-14c0-0.276 0.111-0.525 0.293-0.707s0.431-0.293 0.707-0.293h4c0.552 0 1-0.448 1-1s-0.448-1-1-1h-4c-0.828 0-1.58 0.337-2.121 0.879s-0.879 1.293-0.879 2.121v14c0 0.828 0.337 1.58 0.879 2.121s1.293 0.879 2.121 0.879h4c0.552 0 1-0.448 1-1s-0.448-1-1-1zM18.586 11h-9.586c-0.552 0-1 0.448-1 1s0.448 1 1 1h9.586l-3.293 3.293c-0.391 0.391-0.391 1.024 0 1.414s1.024 0.391 1.414 0l5-5c0.092-0.092 0.166-0.202 0.217-0.324 0.15-0.362 0.078-0.795-0.217-1.090l-5-5c-0.391-0.391-1.024-0.391-1.414 0s-0.391 1.024 0 1.414z"></path>
|
<path d="M9 20h-4c-0.276 0-0.525-0.111-0.707-0.293s-0.293-0.431-0.293-0.707v-14c0-0.276 0.111-0.525 0.293-0.707s0.431-0.293 0.707-0.293h4c0.552 0 1-0.448 1-1s-0.448-1-1-1h-4c-0.828 0-1.58 0.337-2.121 0.879s-0.879 1.293-0.879 2.121v14c0 0.828 0.337 1.58 0.879 2.121s1.293 0.879 2.121 0.879h4c0.552 0 1-0.448 1-1s-0.448-1-1-1zM18.586 11h-9.586c-0.552 0-1 0.448-1 1s0.448 1 1 1h9.586l-3.293 3.293c-0.391 0.391-0.391 1.024 0 1.414s1.024 0.391 1.414 0l5-5c0.092-0.092 0.166-0.202 0.217-0.324 0.15-0.362 0.078-0.795-0.217-1.090l-5-5c-0.391-0.391-1.024-0.391-1.414 0s-0.391 1.024 0 1.414z"></path>
|
||||||
</symbol>
|
</symbol>
|
||||||
|
|
|
@ -3,6 +3,7 @@ import {
|
||||||
AddModToCommunityResponse,
|
AddModToCommunityResponse,
|
||||||
BanFromCommunityResponse,
|
BanFromCommunityResponse,
|
||||||
BlockPersonResponse,
|
BlockPersonResponse,
|
||||||
|
CommentReportResponse,
|
||||||
CommentResponse,
|
CommentResponse,
|
||||||
CommentView,
|
CommentView,
|
||||||
CommunityResponse,
|
CommunityResponse,
|
||||||
|
@ -14,6 +15,7 @@ import {
|
||||||
GetPostsResponse,
|
GetPostsResponse,
|
||||||
GetSiteResponse,
|
GetSiteResponse,
|
||||||
ListingType,
|
ListingType,
|
||||||
|
PostReportResponse,
|
||||||
PostResponse,
|
PostResponse,
|
||||||
PostView,
|
PostView,
|
||||||
SortType,
|
SortType,
|
||||||
|
@ -549,6 +551,16 @@ export class Community extends Component<any, State> {
|
||||||
} else if (op == UserOperation.BlockPerson) {
|
} else if (op == UserOperation.BlockPerson) {
|
||||||
let data = wsJsonToRes<BlockPersonResponse>(msg).data;
|
let data = wsJsonToRes<BlockPersonResponse>(msg).data;
|
||||||
updatePersonBlock(data);
|
updatePersonBlock(data);
|
||||||
|
} else if (op == UserOperation.CreatePostReport) {
|
||||||
|
let data = wsJsonToRes<PostReportResponse>(msg).data;
|
||||||
|
if (data) {
|
||||||
|
toast(i18n.t("report_created"));
|
||||||
|
}
|
||||||
|
} else if (op == UserOperation.CreateCommentReport) {
|
||||||
|
let data = wsJsonToRes<CommentReportResponse>(msg).data;
|
||||||
|
if (data) {
|
||||||
|
toast(i18n.t("report_created"));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,7 @@ import {
|
||||||
AddAdminResponse,
|
AddAdminResponse,
|
||||||
BanPersonResponse,
|
BanPersonResponse,
|
||||||
BlockPersonResponse,
|
BlockPersonResponse,
|
||||||
|
CommentReportResponse,
|
||||||
CommentResponse,
|
CommentResponse,
|
||||||
CommentView,
|
CommentView,
|
||||||
CommunityView,
|
CommunityView,
|
||||||
|
@ -16,6 +17,7 @@ import {
|
||||||
ListCommunities,
|
ListCommunities,
|
||||||
ListCommunitiesResponse,
|
ListCommunitiesResponse,
|
||||||
ListingType,
|
ListingType,
|
||||||
|
PostReportResponse,
|
||||||
PostResponse,
|
PostResponse,
|
||||||
PostView,
|
PostView,
|
||||||
SiteResponse,
|
SiteResponse,
|
||||||
|
@ -955,6 +957,16 @@ export class Home extends Component<any, HomeState> {
|
||||||
} else if (op == UserOperation.BlockPerson) {
|
} else if (op == UserOperation.BlockPerson) {
|
||||||
let data = wsJsonToRes<BlockPersonResponse>(msg).data;
|
let data = wsJsonToRes<BlockPersonResponse>(msg).data;
|
||||||
updatePersonBlock(data);
|
updatePersonBlock(data);
|
||||||
|
} else if (op == UserOperation.CreatePostReport) {
|
||||||
|
let data = wsJsonToRes<PostReportResponse>(msg).data;
|
||||||
|
if (data) {
|
||||||
|
toast(i18n.t("report_created"));
|
||||||
|
}
|
||||||
|
} else if (op == UserOperation.CreateCommentReport) {
|
||||||
|
let data = wsJsonToRes<CommentReportResponse>(msg).data;
|
||||||
|
if (data) {
|
||||||
|
toast(i18n.t("report_created"));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import { Component, linkEvent } from "inferno";
|
import { Component, linkEvent } from "inferno";
|
||||||
import {
|
import {
|
||||||
BlockPersonResponse,
|
BlockPersonResponse,
|
||||||
|
CommentReportResponse,
|
||||||
CommentResponse,
|
CommentResponse,
|
||||||
CommentView,
|
CommentView,
|
||||||
GetPersonMentions,
|
GetPersonMentions,
|
||||||
|
@ -10,6 +11,7 @@ import {
|
||||||
GetRepliesResponse,
|
GetRepliesResponse,
|
||||||
PersonMentionResponse,
|
PersonMentionResponse,
|
||||||
PersonMentionView,
|
PersonMentionView,
|
||||||
|
PostReportResponse,
|
||||||
PrivateMessageResponse,
|
PrivateMessageResponse,
|
||||||
PrivateMessagesResponse,
|
PrivateMessagesResponse,
|
||||||
PrivateMessageView,
|
PrivateMessageView,
|
||||||
|
@ -761,11 +763,21 @@ export class Inbox extends Component<any, InboxState> {
|
||||||
} else if (op == UserOperation.BlockPerson) {
|
} else if (op == UserOperation.BlockPerson) {
|
||||||
let data = wsJsonToRes<BlockPersonResponse>(msg).data;
|
let data = wsJsonToRes<BlockPersonResponse>(msg).data;
|
||||||
updatePersonBlock(data);
|
updatePersonBlock(data);
|
||||||
|
} else if (op == UserOperation.CreatePostReport) {
|
||||||
|
let data = wsJsonToRes<PostReportResponse>(msg).data;
|
||||||
|
if (data) {
|
||||||
|
toast(i18n.t("report_created"));
|
||||||
|
}
|
||||||
|
} else if (op == UserOperation.CreateCommentReport) {
|
||||||
|
let data = wsJsonToRes<CommentReportResponse>(msg).data;
|
||||||
|
if (data) {
|
||||||
|
toast(i18n.t("report_created"));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sendUnreadCount() {
|
sendUnreadCount() {
|
||||||
UserService.Instance.unreadCountSub.next(this.unreadCount());
|
UserService.Instance.unreadInboxCountSub.next(this.unreadCount());
|
||||||
}
|
}
|
||||||
|
|
||||||
unreadCount(): number {
|
unreadCount(): number {
|
||||||
|
|
444
src/shared/components/person/reports.tsx
Normal file
444
src/shared/components/person/reports.tsx
Normal file
|
@ -0,0 +1,444 @@
|
||||||
|
import { Component, linkEvent } from "inferno";
|
||||||
|
import {
|
||||||
|
CommentReportResponse,
|
||||||
|
CommentReportView,
|
||||||
|
ListCommentReports,
|
||||||
|
ListCommentReportsResponse,
|
||||||
|
ListPostReports,
|
||||||
|
ListPostReportsResponse,
|
||||||
|
PostReportResponse,
|
||||||
|
PostReportView,
|
||||||
|
SiteView,
|
||||||
|
UserOperation,
|
||||||
|
} from "lemmy-js-client";
|
||||||
|
import { Subscription } from "rxjs";
|
||||||
|
import { i18n } from "../../i18next";
|
||||||
|
import { InitialFetchRequest } from "../../interfaces";
|
||||||
|
import { UserService, WebSocketService } from "../../services";
|
||||||
|
import {
|
||||||
|
authField,
|
||||||
|
fetchLimit,
|
||||||
|
isBrowser,
|
||||||
|
setIsoData,
|
||||||
|
setupTippy,
|
||||||
|
toast,
|
||||||
|
updateCommentReportRes,
|
||||||
|
updatePostReportRes,
|
||||||
|
wsClient,
|
||||||
|
wsJsonToRes,
|
||||||
|
wsSubscribe,
|
||||||
|
wsUserOp,
|
||||||
|
} from "../../utils";
|
||||||
|
import { CommentReport } from "../comment/comment_report";
|
||||||
|
import { HtmlTags } from "../common/html-tags";
|
||||||
|
import { Spinner } from "../common/icon";
|
||||||
|
import { Paginator } from "../common/paginator";
|
||||||
|
import { PostReport } from "../post/post_report";
|
||||||
|
|
||||||
|
enum UnreadOrAll {
|
||||||
|
Unread,
|
||||||
|
All,
|
||||||
|
}
|
||||||
|
|
||||||
|
enum MessageType {
|
||||||
|
All,
|
||||||
|
CommentReport,
|
||||||
|
PostReport,
|
||||||
|
}
|
||||||
|
|
||||||
|
enum MessageEnum {
|
||||||
|
CommentReport,
|
||||||
|
PostReport,
|
||||||
|
}
|
||||||
|
|
||||||
|
type ItemType = {
|
||||||
|
id: number;
|
||||||
|
type_: MessageEnum;
|
||||||
|
view: CommentReportView | PostReportView;
|
||||||
|
published: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
interface ReportsState {
|
||||||
|
unreadOrAll: UnreadOrAll;
|
||||||
|
messageType: MessageType;
|
||||||
|
commentReports: CommentReportView[];
|
||||||
|
postReports: PostReportView[];
|
||||||
|
combined: ItemType[];
|
||||||
|
page: number;
|
||||||
|
site_view: SiteView;
|
||||||
|
loading: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Reports extends Component<any, ReportsState> {
|
||||||
|
private isoData = setIsoData(this.context);
|
||||||
|
private subscription: Subscription;
|
||||||
|
private emptyState: ReportsState = {
|
||||||
|
unreadOrAll: UnreadOrAll.Unread,
|
||||||
|
messageType: MessageType.All,
|
||||||
|
commentReports: [],
|
||||||
|
postReports: [],
|
||||||
|
combined: [],
|
||||||
|
page: 1,
|
||||||
|
site_view: this.isoData.site_res.site_view,
|
||||||
|
loading: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
constructor(props: any, context: any) {
|
||||||
|
super(props, context);
|
||||||
|
|
||||||
|
this.state = this.emptyState;
|
||||||
|
this.handlePageChange = this.handlePageChange.bind(this);
|
||||||
|
|
||||||
|
if (!UserService.Instance.myUserInfo && isBrowser()) {
|
||||||
|
toast(i18n.t("not_logged_in"), "danger");
|
||||||
|
this.context.router.history.push(`/login`);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.parseMessage = this.parseMessage.bind(this);
|
||||||
|
this.subscription = wsSubscribe(this.parseMessage);
|
||||||
|
|
||||||
|
// Only fetch the data if coming from another route
|
||||||
|
if (this.isoData.path == this.context.router.route.match.url) {
|
||||||
|
this.state.commentReports =
|
||||||
|
this.isoData.routeData[0].comment_reports || [];
|
||||||
|
this.state.postReports = this.isoData.routeData[1].post_reports || [];
|
||||||
|
this.state.combined = this.buildCombined();
|
||||||
|
this.state.loading = false;
|
||||||
|
} else {
|
||||||
|
this.refetch();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillUnmount() {
|
||||||
|
if (isBrowser()) {
|
||||||
|
this.subscription.unsubscribe();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
get documentTitle(): string {
|
||||||
|
return `@${
|
||||||
|
UserService.Instance.myUserInfo.local_user_view.person.name
|
||||||
|
} ${i18n.t("reports")} - ${this.state.site_view.site.name}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<div class="container">
|
||||||
|
{this.state.loading ? (
|
||||||
|
<h5>
|
||||||
|
<Spinner large />
|
||||||
|
</h5>
|
||||||
|
) : (
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-12">
|
||||||
|
<HtmlTags
|
||||||
|
title={this.documentTitle}
|
||||||
|
path={this.context.router.route.match.url}
|
||||||
|
/>
|
||||||
|
<h5 class="mb-2">{i18n.t("reports")}</h5>
|
||||||
|
{this.selects()}
|
||||||
|
{this.state.messageType == MessageType.All && this.all()}
|
||||||
|
{this.state.messageType == MessageType.CommentReport &&
|
||||||
|
this.commentReports()}
|
||||||
|
{this.state.messageType == MessageType.PostReport &&
|
||||||
|
this.postReports()}
|
||||||
|
<Paginator
|
||||||
|
page={this.state.page}
|
||||||
|
onChange={this.handlePageChange}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
unreadOrAllRadios() {
|
||||||
|
return (
|
||||||
|
<div class="btn-group btn-group-toggle flex-wrap mb-2">
|
||||||
|
<label
|
||||||
|
className={`btn btn-outline-secondary pointer
|
||||||
|
${this.state.unreadOrAll == UnreadOrAll.Unread && "active"}
|
||||||
|
`}
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
type="radio"
|
||||||
|
value={UnreadOrAll.Unread}
|
||||||
|
checked={this.state.unreadOrAll == UnreadOrAll.Unread}
|
||||||
|
onChange={linkEvent(this, this.handleUnreadOrAllChange)}
|
||||||
|
/>
|
||||||
|
{i18n.t("unread")}
|
||||||
|
</label>
|
||||||
|
<label
|
||||||
|
className={`btn btn-outline-secondary pointer
|
||||||
|
${this.state.unreadOrAll == UnreadOrAll.All && "active"}
|
||||||
|
`}
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
type="radio"
|
||||||
|
value={UnreadOrAll.All}
|
||||||
|
checked={this.state.unreadOrAll == UnreadOrAll.All}
|
||||||
|
onChange={linkEvent(this, this.handleUnreadOrAllChange)}
|
||||||
|
/>
|
||||||
|
{i18n.t("all")}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
messageTypeRadios() {
|
||||||
|
return (
|
||||||
|
<div class="btn-group btn-group-toggle flex-wrap mb-2">
|
||||||
|
<label
|
||||||
|
className={`btn btn-outline-secondary pointer
|
||||||
|
${this.state.messageType == MessageType.All && "active"}
|
||||||
|
`}
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
type="radio"
|
||||||
|
value={MessageType.All}
|
||||||
|
checked={this.state.messageType == MessageType.All}
|
||||||
|
onChange={linkEvent(this, this.handleMessageTypeChange)}
|
||||||
|
/>
|
||||||
|
{i18n.t("all")}
|
||||||
|
</label>
|
||||||
|
<label
|
||||||
|
className={`btn btn-outline-secondary pointer
|
||||||
|
${this.state.messageType == MessageType.CommentReport && "active"}
|
||||||
|
`}
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
type="radio"
|
||||||
|
value={MessageType.CommentReport}
|
||||||
|
checked={this.state.messageType == MessageType.CommentReport}
|
||||||
|
onChange={linkEvent(this, this.handleMessageTypeChange)}
|
||||||
|
/>
|
||||||
|
{i18n.t("comments")}
|
||||||
|
</label>
|
||||||
|
<label
|
||||||
|
className={`btn btn-outline-secondary pointer
|
||||||
|
${this.state.messageType == MessageType.PostReport && "active"}
|
||||||
|
`}
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
type="radio"
|
||||||
|
value={MessageType.PostReport}
|
||||||
|
checked={this.state.messageType == MessageType.PostReport}
|
||||||
|
onChange={linkEvent(this, this.handleMessageTypeChange)}
|
||||||
|
/>
|
||||||
|
{i18n.t("posts")}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
selects() {
|
||||||
|
return (
|
||||||
|
<div className="mb-2">
|
||||||
|
<span class="mr-3">{this.unreadOrAllRadios()}</span>
|
||||||
|
<span class="mr-3">{this.messageTypeRadios()}</span>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
replyToReplyType(r: CommentReportView): ItemType {
|
||||||
|
return {
|
||||||
|
id: r.comment_report.id,
|
||||||
|
type_: MessageEnum.CommentReport,
|
||||||
|
view: r,
|
||||||
|
published: r.comment_report.published,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
mentionToReplyType(r: PostReportView): ItemType {
|
||||||
|
return {
|
||||||
|
id: r.post_report.id,
|
||||||
|
type_: MessageEnum.PostReport,
|
||||||
|
view: r,
|
||||||
|
published: r.post_report.published,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
buildCombined(): ItemType[] {
|
||||||
|
let comments: ItemType[] = this.state.commentReports.map(r =>
|
||||||
|
this.replyToReplyType(r)
|
||||||
|
);
|
||||||
|
let posts: ItemType[] = this.state.postReports.map(r =>
|
||||||
|
this.mentionToReplyType(r)
|
||||||
|
);
|
||||||
|
|
||||||
|
return [...comments, ...posts].sort((a, b) =>
|
||||||
|
b.published.localeCompare(a.published)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
renderItemType(i: ItemType) {
|
||||||
|
switch (i.type_) {
|
||||||
|
case MessageEnum.CommentReport:
|
||||||
|
return (
|
||||||
|
<CommentReport key={i.id} report={i.view as CommentReportView} />
|
||||||
|
);
|
||||||
|
case MessageEnum.PostReport:
|
||||||
|
return <PostReport key={i.id} report={i.view as PostReportView} />;
|
||||||
|
default:
|
||||||
|
return <div />;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
all() {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
{this.state.combined.map(i => (
|
||||||
|
<>
|
||||||
|
<hr />
|
||||||
|
{this.renderItemType(i)}
|
||||||
|
</>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
commentReports() {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
{this.state.commentReports.map(cr => (
|
||||||
|
<>
|
||||||
|
<hr />
|
||||||
|
<CommentReport key={cr.comment_report.id} report={cr} />
|
||||||
|
</>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
postReports() {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
{this.state.postReports.map(pr => (
|
||||||
|
<>
|
||||||
|
<hr />
|
||||||
|
<PostReport key={pr.post_report.id} report={pr} />
|
||||||
|
</>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
handlePageChange(page: number) {
|
||||||
|
this.setState({ page });
|
||||||
|
this.refetch();
|
||||||
|
}
|
||||||
|
|
||||||
|
handleUnreadOrAllChange(i: Reports, event: any) {
|
||||||
|
i.state.unreadOrAll = Number(event.target.value);
|
||||||
|
i.state.page = 1;
|
||||||
|
i.setState(i.state);
|
||||||
|
i.refetch();
|
||||||
|
}
|
||||||
|
|
||||||
|
handleMessageTypeChange(i: Reports, event: any) {
|
||||||
|
i.state.messageType = Number(event.target.value);
|
||||||
|
i.state.page = 1;
|
||||||
|
i.setState(i.state);
|
||||||
|
i.refetch();
|
||||||
|
}
|
||||||
|
|
||||||
|
static fetchInitialData(req: InitialFetchRequest): Promise<any>[] {
|
||||||
|
let promises: Promise<any>[] = [];
|
||||||
|
|
||||||
|
let commentReportsForm: ListCommentReports = {
|
||||||
|
// TODO community_id
|
||||||
|
unresolved_only: true,
|
||||||
|
page: 1,
|
||||||
|
limit: fetchLimit,
|
||||||
|
auth: req.auth,
|
||||||
|
};
|
||||||
|
promises.push(req.client.listCommentReports(commentReportsForm));
|
||||||
|
|
||||||
|
let postReportsForm: ListPostReports = {
|
||||||
|
// TODO community_id
|
||||||
|
unresolved_only: true,
|
||||||
|
page: 1,
|
||||||
|
limit: fetchLimit,
|
||||||
|
auth: req.auth,
|
||||||
|
};
|
||||||
|
promises.push(req.client.listPostReports(postReportsForm));
|
||||||
|
|
||||||
|
return promises;
|
||||||
|
}
|
||||||
|
|
||||||
|
refetch() {
|
||||||
|
let unresolved_only = this.state.unreadOrAll == UnreadOrAll.Unread;
|
||||||
|
let commentReportsForm: ListCommentReports = {
|
||||||
|
// TODO community_id
|
||||||
|
unresolved_only,
|
||||||
|
page: this.state.page,
|
||||||
|
limit: fetchLimit,
|
||||||
|
auth: authField(),
|
||||||
|
};
|
||||||
|
WebSocketService.Instance.send(
|
||||||
|
wsClient.listCommentReports(commentReportsForm)
|
||||||
|
);
|
||||||
|
|
||||||
|
let postReportsForm: ListPostReports = {
|
||||||
|
// TODO community_id
|
||||||
|
unresolved_only,
|
||||||
|
page: this.state.page,
|
||||||
|
limit: fetchLimit,
|
||||||
|
auth: authField(),
|
||||||
|
};
|
||||||
|
WebSocketService.Instance.send(wsClient.listPostReports(postReportsForm));
|
||||||
|
}
|
||||||
|
|
||||||
|
parseMessage(msg: any) {
|
||||||
|
let op = wsUserOp(msg);
|
||||||
|
console.log(msg);
|
||||||
|
if (msg.error) {
|
||||||
|
toast(i18n.t(msg.error), "danger");
|
||||||
|
return;
|
||||||
|
} else if (msg.reconnect) {
|
||||||
|
this.refetch();
|
||||||
|
} else if (op == UserOperation.ListCommentReports) {
|
||||||
|
let data = wsJsonToRes<ListCommentReportsResponse>(msg).data;
|
||||||
|
this.state.commentReports = data.comment_reports;
|
||||||
|
this.state.combined = this.buildCombined();
|
||||||
|
this.state.loading = false;
|
||||||
|
// this.sendUnreadCount();
|
||||||
|
window.scrollTo(0, 0);
|
||||||
|
this.setState(this.state);
|
||||||
|
setupTippy();
|
||||||
|
} else if (op == UserOperation.ListPostReports) {
|
||||||
|
let data = wsJsonToRes<ListPostReportsResponse>(msg).data;
|
||||||
|
this.state.postReports = data.post_reports;
|
||||||
|
this.state.combined = this.buildCombined();
|
||||||
|
this.state.loading = false;
|
||||||
|
// this.sendUnreadCount();
|
||||||
|
window.scrollTo(0, 0);
|
||||||
|
this.setState(this.state);
|
||||||
|
setupTippy();
|
||||||
|
} else if (op == UserOperation.ResolvePostReport) {
|
||||||
|
let data = wsJsonToRes<PostReportResponse>(msg).data;
|
||||||
|
updatePostReportRes(data.post_report_view, this.state.postReports);
|
||||||
|
let urcs = UserService.Instance.unreadReportCountSub;
|
||||||
|
if (data.post_report_view.post_report.resolved) {
|
||||||
|
urcs.next(urcs.getValue() - 1);
|
||||||
|
} else {
|
||||||
|
urcs.next(urcs.getValue() + 1);
|
||||||
|
}
|
||||||
|
this.setState(this.state);
|
||||||
|
} else if (op == UserOperation.ResolveCommentReport) {
|
||||||
|
let data = wsJsonToRes<CommentReportResponse>(msg).data;
|
||||||
|
updateCommentReportRes(
|
||||||
|
data.comment_report_view,
|
||||||
|
this.state.commentReports
|
||||||
|
);
|
||||||
|
let urcs = UserService.Instance.unreadReportCountSub;
|
||||||
|
if (data.comment_report_view.comment_report.resolved) {
|
||||||
|
urcs.next(urcs.getValue() - 1);
|
||||||
|
} else {
|
||||||
|
urcs.next(urcs.getValue() + 1);
|
||||||
|
}
|
||||||
|
this.setState(this.state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -8,6 +8,7 @@ import {
|
||||||
BlockPerson,
|
BlockPerson,
|
||||||
CommunityModeratorView,
|
CommunityModeratorView,
|
||||||
CreatePostLike,
|
CreatePostLike,
|
||||||
|
CreatePostReport,
|
||||||
DeletePost,
|
DeletePost,
|
||||||
LockPost,
|
LockPost,
|
||||||
PersonViewSafe,
|
PersonViewSafe,
|
||||||
|
@ -62,6 +63,8 @@ interface PostListingState {
|
||||||
showAdvanced: boolean;
|
showAdvanced: boolean;
|
||||||
showMoreMobile: boolean;
|
showMoreMobile: boolean;
|
||||||
showBody: boolean;
|
showBody: boolean;
|
||||||
|
showReportDialog: boolean;
|
||||||
|
reportReason: string;
|
||||||
my_vote: number;
|
my_vote: number;
|
||||||
score: number;
|
score: number;
|
||||||
upvotes: number;
|
upvotes: number;
|
||||||
|
@ -96,6 +99,8 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
|
||||||
showAdvanced: false,
|
showAdvanced: false,
|
||||||
showMoreMobile: false,
|
showMoreMobile: false,
|
||||||
showBody: false,
|
showBody: false,
|
||||||
|
showReportDialog: false,
|
||||||
|
reportReason: null,
|
||||||
my_vote: this.props.post_view.my_vote,
|
my_vote: this.props.post_view.my_vote,
|
||||||
score: this.props.post_view.counts.score,
|
score: this.props.post_view.counts.score,
|
||||||
upvotes: this.props.post_view.counts.upvotes,
|
upvotes: this.props.post_view.counts.upvotes,
|
||||||
|
@ -664,6 +669,15 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
|
||||||
<Icon icon="copy" classes="icon-inline" />
|
<Icon icon="copy" classes="icon-inline" />
|
||||||
</Link>
|
</Link>
|
||||||
{!this.myPost && (
|
{!this.myPost && (
|
||||||
|
<>
|
||||||
|
<button
|
||||||
|
class="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" classes="icon-inline" />
|
||||||
|
</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.handleBlockUserClick)}
|
onClick={linkEvent(this, this.handleBlockUserClick)}
|
||||||
|
@ -672,6 +686,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
|
||||||
>
|
>
|
||||||
<Icon icon="slash" classes="icon-inline" />
|
<Icon icon="slash" classes="icon-inline" />
|
||||||
</button>
|
</button>
|
||||||
|
</>
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
@ -1040,6 +1055,32 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
)}
|
)}
|
||||||
|
{this.state.showReportDialog && (
|
||||||
|
<form
|
||||||
|
class="form-inline"
|
||||||
|
onSubmit={linkEvent(this, this.handleReportSubmit)}
|
||||||
|
>
|
||||||
|
<label class="sr-only" htmlFor="post-report-reason">
|
||||||
|
{i18n.t("reason")}
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
id="post-report-reason"
|
||||||
|
class="form-control mr-2"
|
||||||
|
placeholder={i18n.t("reason")}
|
||||||
|
required
|
||||||
|
value={this.state.reportReason}
|
||||||
|
onInput={linkEvent(this, this.handleReportReasonChange)}
|
||||||
|
/>
|
||||||
|
<button
|
||||||
|
type="submit"
|
||||||
|
class="btn btn-secondary"
|
||||||
|
aria-label={i18n.t("create_report")}
|
||||||
|
>
|
||||||
|
{i18n.t("create_report")}
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
)}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1305,6 +1346,29 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
|
||||||
this.setState(this.state);
|
this.setState(this.state);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleShowReportDialog(i: PostListing) {
|
||||||
|
i.state.showReportDialog = !i.state.showReportDialog;
|
||||||
|
i.setState(this.state);
|
||||||
|
}
|
||||||
|
|
||||||
|
handleReportReasonChange(i: PostListing, event: any) {
|
||||||
|
i.state.reportReason = event.target.value;
|
||||||
|
i.setState(i.state);
|
||||||
|
}
|
||||||
|
|
||||||
|
handleReportSubmit(i: PostListing, event: any) {
|
||||||
|
event.preventDefault();
|
||||||
|
let form: CreatePostReport = {
|
||||||
|
post_id: i.props.post_view.post.id,
|
||||||
|
reason: i.state.reportReason,
|
||||||
|
auth: authField(),
|
||||||
|
};
|
||||||
|
WebSocketService.Instance.send(wsClient.createPostReport(form));
|
||||||
|
|
||||||
|
i.state.showReportDialog = false;
|
||||||
|
i.setState(i.state);
|
||||||
|
}
|
||||||
|
|
||||||
handleBlockUserClick(i: PostListing) {
|
handleBlockUserClick(i: PostListing) {
|
||||||
let blockUserForm: BlockPerson = {
|
let blockUserForm: BlockPerson = {
|
||||||
person_id: i.props.post_view.creator.id,
|
person_id: i.props.post_view.creator.id,
|
||||||
|
|
|
@ -6,6 +6,7 @@ import {
|
||||||
BanFromCommunityResponse,
|
BanFromCommunityResponse,
|
||||||
BanPersonResponse,
|
BanPersonResponse,
|
||||||
BlockPersonResponse,
|
BlockPersonResponse,
|
||||||
|
CommentReportResponse,
|
||||||
CommentResponse,
|
CommentResponse,
|
||||||
CommunityResponse,
|
CommunityResponse,
|
||||||
GetCommunityResponse,
|
GetCommunityResponse,
|
||||||
|
@ -14,6 +15,7 @@ import {
|
||||||
GetSiteResponse,
|
GetSiteResponse,
|
||||||
ListingType,
|
ListingType,
|
||||||
MarkCommentAsRead,
|
MarkCommentAsRead,
|
||||||
|
PostReportResponse,
|
||||||
PostResponse,
|
PostResponse,
|
||||||
PostView,
|
PostView,
|
||||||
Search,
|
Search,
|
||||||
|
@ -245,8 +247,8 @@ export class Post extends Component<any, PostState> {
|
||||||
auth: authField(),
|
auth: authField(),
|
||||||
};
|
};
|
||||||
WebSocketService.Instance.send(wsClient.markCommentAsRead(form));
|
WebSocketService.Instance.send(wsClient.markCommentAsRead(form));
|
||||||
UserService.Instance.unreadCountSub.next(
|
UserService.Instance.unreadInboxCountSub.next(
|
||||||
UserService.Instance.unreadCountSub.value - 1
|
UserService.Instance.unreadInboxCountSub.value - 1
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -619,6 +621,16 @@ export class Post extends Component<any, PostState> {
|
||||||
} else if (op == UserOperation.BlockPerson) {
|
} else if (op == UserOperation.BlockPerson) {
|
||||||
let data = wsJsonToRes<BlockPersonResponse>(msg).data;
|
let data = wsJsonToRes<BlockPersonResponse>(msg).data;
|
||||||
updatePersonBlock(data);
|
updatePersonBlock(data);
|
||||||
|
} else if (op == UserOperation.CreatePostReport) {
|
||||||
|
let data = wsJsonToRes<PostReportResponse>(msg).data;
|
||||||
|
if (data) {
|
||||||
|
toast(i18n.t("report_created"));
|
||||||
|
}
|
||||||
|
} else if (op == UserOperation.CreateCommentReport) {
|
||||||
|
let data = wsJsonToRes<CommentReportResponse>(msg).data;
|
||||||
|
if (data) {
|
||||||
|
toast(i18n.t("report_created"));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
99
src/shared/components/post/post_report.tsx
Normal file
99
src/shared/components/post/post_report.tsx
Normal file
|
@ -0,0 +1,99 @@
|
||||||
|
import { Component, linkEvent } from "inferno";
|
||||||
|
import { T } from "inferno-i18next-dess";
|
||||||
|
import { PostReportView, PostView, ResolvePostReport } from "lemmy-js-client";
|
||||||
|
import { i18n } from "../../i18next";
|
||||||
|
import { WebSocketService } from "../../services";
|
||||||
|
import { authField, wsClient } from "../../utils";
|
||||||
|
import { Icon } from "../common/icon";
|
||||||
|
import { PersonListing } from "../person/person-listing";
|
||||||
|
import { PostListing } from "./post-listing";
|
||||||
|
|
||||||
|
interface PostReportProps {
|
||||||
|
report: PostReportView;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class PostReport extends Component<PostReportProps, any> {
|
||||||
|
constructor(props: any, context: any) {
|
||||||
|
super(props, context);
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
let r = this.props.report;
|
||||||
|
let post = r.post;
|
||||||
|
|
||||||
|
// Set the original post data ( a troll could change it )
|
||||||
|
post.name = r.post_report.original_post_name;
|
||||||
|
post.url = r.post_report.original_post_url;
|
||||||
|
post.body = r.post_report.original_post_body;
|
||||||
|
let pv: PostView = {
|
||||||
|
post,
|
||||||
|
creator: r.post_creator,
|
||||||
|
community: r.community,
|
||||||
|
creator_banned_from_community: r.creator_banned_from_community,
|
||||||
|
counts: r.counts,
|
||||||
|
subscribed: false,
|
||||||
|
saved: false,
|
||||||
|
read: false,
|
||||||
|
creator_blocked: false,
|
||||||
|
my_vote: r.my_vote,
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<PostListing
|
||||||
|
post_view={pv}
|
||||||
|
showCommunity={true}
|
||||||
|
enableDownvotes={true}
|
||||||
|
enableNsfw={true}
|
||||||
|
/>
|
||||||
|
<div>
|
||||||
|
{i18n.t("reporter")}: <PersonListing person={r.creator} />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
{i18n.t("reason")}: {r.post_report.reason}
|
||||||
|
</div>
|
||||||
|
{r.resolver && (
|
||||||
|
<div>
|
||||||
|
{r.post_report.resolved ? (
|
||||||
|
<T i18nKey="resolved_by">
|
||||||
|
#
|
||||||
|
<PersonListing person={r.resolver} />
|
||||||
|
</T>
|
||||||
|
) : (
|
||||||
|
<T i18nKey="unresolved_by">
|
||||||
|
#
|
||||||
|
<PersonListing person={r.resolver} />
|
||||||
|
</T>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<button
|
||||||
|
className="btn btn-link btn-animate text-muted py-0"
|
||||||
|
onClick={linkEvent(this, this.handleResolveReport)}
|
||||||
|
data-tippy-content={
|
||||||
|
r.post_report.resolved ? "unresolve_report" : "resolve_report"
|
||||||
|
}
|
||||||
|
aria-label={
|
||||||
|
r.post_report.resolved ? "unresolve_report" : "resolve_report"
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<Icon
|
||||||
|
icon="check"
|
||||||
|
classes={`icon-inline ${
|
||||||
|
r.post_report.resolved ? "text-success" : "text-danger"
|
||||||
|
}`}
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
handleResolveReport(i: PostReport) {
|
||||||
|
let form: ResolvePostReport = {
|
||||||
|
report_id: i.props.report.post_report.id,
|
||||||
|
resolved: !i.props.report.post_report.resolved,
|
||||||
|
auth: authField(),
|
||||||
|
};
|
||||||
|
WebSocketService.Instance.send(wsClient.resolvePostReport(form));
|
||||||
|
}
|
||||||
|
}
|
|
@ -12,6 +12,7 @@ import { Signup } from "./components/home/signup";
|
||||||
import { Modlog } from "./components/modlog";
|
import { Modlog } from "./components/modlog";
|
||||||
import { Inbox } from "./components/person/inbox";
|
import { Inbox } from "./components/person/inbox";
|
||||||
import { Profile } from "./components/person/profile";
|
import { Profile } from "./components/person/profile";
|
||||||
|
import { Reports } from "./components/person/reports";
|
||||||
import { Settings } from "./components/person/settings";
|
import { Settings } from "./components/person/settings";
|
||||||
import { CreatePost } from "./components/post/create-post";
|
import { CreatePost } from "./components/post/create-post";
|
||||||
import { Post } from "./components/post/post";
|
import { Post } from "./components/post/post";
|
||||||
|
@ -122,6 +123,11 @@ export const routes: IRoutePropsWithFetch[] = [
|
||||||
component: AdminSettings,
|
component: AdminSettings,
|
||||||
fetchInitialData: req => AdminSettings.fetchInitialData(req),
|
fetchInitialData: req => AdminSettings.fetchInitialData(req),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: `/reports`,
|
||||||
|
component: Reports,
|
||||||
|
fetchInitialData: req => Reports.fetchInitialData(req),
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: `/search/q/:q/type/:type/sort/:sort/listing_type/:listing_type/community_id/:community_id/creator_id/:creator_id/page/:page`,
|
path: `/search/q/:q/type/:type/sort/:sort/listing_type/:listing_type/community_id/:community_id/creator_id/:creator_id/page/:page`,
|
||||||
component: Search,
|
component: Search,
|
||||||
|
|
|
@ -16,9 +16,10 @@ export class UserService {
|
||||||
public myUserInfo: MyUserInfo;
|
public myUserInfo: MyUserInfo;
|
||||||
public claims: Claims;
|
public claims: Claims;
|
||||||
public jwtSub: Subject<string> = new Subject<string>();
|
public jwtSub: Subject<string> = new Subject<string>();
|
||||||
public unreadCountSub: BehaviorSubject<number> = new BehaviorSubject<number>(
|
public unreadInboxCountSub: BehaviorSubject<number> =
|
||||||
0
|
new BehaviorSubject<number>(0);
|
||||||
);
|
public unreadReportCountSub: BehaviorSubject<number> =
|
||||||
|
new BehaviorSubject<number>(0);
|
||||||
|
|
||||||
private constructor() {
|
private constructor() {
|
||||||
if (this.auth) {
|
if (this.auth) {
|
||||||
|
|
|
@ -2,6 +2,7 @@ import emojiShortName from "emoji-short-name";
|
||||||
import {
|
import {
|
||||||
BlockCommunityResponse,
|
BlockCommunityResponse,
|
||||||
BlockPersonResponse,
|
BlockPersonResponse,
|
||||||
|
CommentReportView,
|
||||||
CommentView,
|
CommentView,
|
||||||
CommunityBlockView,
|
CommunityBlockView,
|
||||||
CommunityView,
|
CommunityView,
|
||||||
|
@ -13,6 +14,7 @@ import {
|
||||||
MyUserInfo,
|
MyUserInfo,
|
||||||
PersonBlockView,
|
PersonBlockView,
|
||||||
PersonViewSafe,
|
PersonViewSafe,
|
||||||
|
PostReportView,
|
||||||
PostView,
|
PostView,
|
||||||
PrivateMessageView,
|
PrivateMessageView,
|
||||||
Search,
|
Search,
|
||||||
|
@ -1055,6 +1057,26 @@ export function editPostRes(data: PostView, post: PostView) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function updatePostReportRes(
|
||||||
|
data: PostReportView,
|
||||||
|
reports: PostReportView[]
|
||||||
|
) {
|
||||||
|
let found = reports.find(p => p.post.id == data.post.id);
|
||||||
|
if (found) {
|
||||||
|
found.post_report = data.post_report;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function updateCommentReportRes(
|
||||||
|
data: CommentReportView,
|
||||||
|
reports: CommentReportView[]
|
||||||
|
) {
|
||||||
|
let found = reports.find(c => c.comment.id == data.comment.id);
|
||||||
|
if (found) {
|
||||||
|
found.comment_report = data.comment_report;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export function commentsToFlatNodes(comments: CommentView[]): CommentNodeI[] {
|
export function commentsToFlatNodes(comments: CommentView[]): CommentNodeI[] {
|
||||||
let nodes: CommentNodeI[] = [];
|
let nodes: CommentNodeI[] = [];
|
||||||
for (let comment of comments) {
|
for (let comment of comments) {
|
||||||
|
|
|
@ -4709,10 +4709,10 @@ lcid@^1.0.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
invert-kv "^1.0.0"
|
invert-kv "^1.0.0"
|
||||||
|
|
||||||
lemmy-js-client@0.12.0:
|
lemmy-js-client@0.12.3-rc.5:
|
||||||
version "0.12.0"
|
version "0.12.3-rc.5"
|
||||||
resolved "https://registry.yarnpkg.com/lemmy-js-client/-/lemmy-js-client-0.12.0.tgz#2337aca9d8b38d92908d7f7a9479f0066a9eaeae"
|
resolved "https://registry.yarnpkg.com/lemmy-js-client/-/lemmy-js-client-0.12.3-rc.5.tgz#26bc2d8443c5ab2bea1ed73697b202592fd00e15"
|
||||||
integrity sha512-PSebUBkojM7OUlfSXKQhL4IcYKaKF+Xj2G0+pybaCvP9sJvviy32qHUi9BQeIhRHXgw8ILRH0Y+xZGKu0a3wvQ==
|
integrity sha512-3Rs1G7b/MYhQkMYJqBgQ+piSE+anYa+C2tr1DqY7+JrO1vbepu2+GyDg3jjzPuoZ3GPPOWYtKJU5pt9GqLb1lg==
|
||||||
|
|
||||||
levn@^0.4.1:
|
levn@^0.4.1:
|
||||||
version "0.4.1"
|
version "0.4.1"
|
||||||
|
|
Loading…
Reference in a new issue