2023-06-21 22:28:24 +00:00
|
|
|
import { myAuthRequired } from "@utils/app";
|
2023-06-14 12:20:40 +00:00
|
|
|
import { Component, InfernoNode, linkEvent } from "inferno";
|
2020-09-06 16:15:25 +00:00
|
|
|
import {
|
2023-06-14 12:20:40 +00:00
|
|
|
CreatePrivateMessage,
|
2022-09-28 12:50:47 +00:00
|
|
|
CreatePrivateMessageReport,
|
2020-12-24 01:58:27 +00:00
|
|
|
DeletePrivateMessage,
|
2023-06-14 12:20:40 +00:00
|
|
|
EditPrivateMessage,
|
2020-12-24 01:58:27 +00:00
|
|
|
MarkPrivateMessageAsRead,
|
2023-05-11 18:32:32 +00:00
|
|
|
Person,
|
2021-07-17 20:42:55 +00:00
|
|
|
PrivateMessageView,
|
2021-02-22 02:39:04 +00:00
|
|
|
} from "lemmy-js-client";
|
2023-06-21 22:28:24 +00:00
|
|
|
import { mdToHtml } from "../../markdown";
|
2023-06-22 00:54:35 +00:00
|
|
|
import { I18NextService, UserService } from "../../services";
|
2023-06-14 12:20:40 +00:00
|
|
|
import { Icon, Spinner } from "../common/icon";
|
2021-07-17 20:42:55 +00:00
|
|
|
import { MomentTime } from "../common/moment-time";
|
|
|
|
import { PersonListing } from "../person/person-listing";
|
2021-02-22 02:39:04 +00:00
|
|
|
import { PrivateMessageForm } from "./private-message-form";
|
2020-09-06 16:15:25 +00:00
|
|
|
|
|
|
|
interface PrivateMessageState {
|
|
|
|
showReply: boolean;
|
|
|
|
showEdit: boolean;
|
|
|
|
collapsed: boolean;
|
|
|
|
viewSource: boolean;
|
2022-09-28 12:50:47 +00:00
|
|
|
showReportDialog: boolean;
|
2023-01-04 16:56:24 +00:00
|
|
|
reportReason?: string;
|
2023-06-14 12:20:40 +00:00
|
|
|
deleteLoading: boolean;
|
|
|
|
readLoading: boolean;
|
|
|
|
reportLoading: boolean;
|
2020-09-06 16:15:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
interface PrivateMessageProps {
|
2020-12-24 01:58:27 +00:00
|
|
|
private_message_view: PrivateMessageView;
|
2023-06-14 12:20:40 +00:00
|
|
|
onDelete(form: DeletePrivateMessage): void;
|
|
|
|
onMarkRead(form: MarkPrivateMessageAsRead): void;
|
|
|
|
onReport(form: CreatePrivateMessageReport): void;
|
|
|
|
onCreate(form: CreatePrivateMessage): void;
|
|
|
|
onEdit(form: EditPrivateMessage): void;
|
2020-09-06 16:15:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
export class PrivateMessage extends Component<
|
|
|
|
PrivateMessageProps,
|
|
|
|
PrivateMessageState
|
|
|
|
> {
|
2023-01-04 16:56:24 +00:00
|
|
|
state: PrivateMessageState = {
|
2020-09-06 16:15:25 +00:00
|
|
|
showReply: false,
|
|
|
|
showEdit: false,
|
|
|
|
collapsed: false,
|
|
|
|
viewSource: false,
|
2022-09-28 12:50:47 +00:00
|
|
|
showReportDialog: false,
|
2023-06-14 12:20:40 +00:00
|
|
|
deleteLoading: false,
|
|
|
|
readLoading: false,
|
|
|
|
reportLoading: false,
|
2020-09-06 16:15:25 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
constructor(props: any, context: any) {
|
|
|
|
super(props, context);
|
|
|
|
this.handleReplyCancel = this.handleReplyCancel.bind(this);
|
|
|
|
}
|
|
|
|
|
|
|
|
get mine(): boolean {
|
2023-01-04 16:56:24 +00:00
|
|
|
return (
|
|
|
|
UserService.Instance.myUserInfo?.local_user_view.person.id ==
|
|
|
|
this.props.private_message_view.creator.id
|
|
|
|
);
|
2020-09-06 16:15:25 +00:00
|
|
|
}
|
|
|
|
|
2023-06-14 12:20:40 +00:00
|
|
|
componentWillReceiveProps(
|
|
|
|
nextProps: Readonly<{ children?: InfernoNode } & PrivateMessageProps>
|
|
|
|
): void {
|
|
|
|
if (this.props != nextProps) {
|
|
|
|
this.setState({
|
|
|
|
showReply: false,
|
|
|
|
showEdit: false,
|
|
|
|
collapsed: false,
|
|
|
|
viewSource: false,
|
|
|
|
showReportDialog: false,
|
|
|
|
deleteLoading: false,
|
|
|
|
readLoading: false,
|
|
|
|
reportLoading: false,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-09-06 16:15:25 +00:00
|
|
|
render() {
|
2023-06-05 21:31:12 +00:00
|
|
|
const message_view = this.props.private_message_view;
|
|
|
|
const otherPerson: Person = this.mine
|
2020-12-24 01:58:27 +00:00
|
|
|
? message_view.recipient
|
|
|
|
: message_view.creator;
|
2020-09-06 16:15:25 +00:00
|
|
|
|
|
|
|
return (
|
2023-06-20 18:46:16 +00:00
|
|
|
<div className="private-message border-top border-light">
|
2020-09-06 16:15:25 +00:00
|
|
|
<div>
|
2022-09-22 15:03:35 +00:00
|
|
|
<ul className="list-inline mb-0 text-muted small">
|
2020-09-06 16:15:25 +00:00
|
|
|
{/* TODO refactor this */}
|
|
|
|
<li className="list-inline-item">
|
2023-06-22 00:54:35 +00:00
|
|
|
{this.mine
|
|
|
|
? I18NextService.i18n.t("to")
|
|
|
|
: I18NextService.i18n.t("from")}
|
2020-09-06 16:15:25 +00:00
|
|
|
</li>
|
|
|
|
<li className="list-inline-item">
|
2021-03-15 18:09:31 +00:00
|
|
|
<PersonListing person={otherPerson} />
|
2020-09-06 16:15:25 +00:00
|
|
|
</li>
|
|
|
|
<li className="list-inline-item">
|
|
|
|
<span>
|
2022-06-21 21:42:29 +00:00
|
|
|
<MomentTime
|
|
|
|
published={message_view.private_message.published}
|
|
|
|
updated={message_view.private_message.updated}
|
|
|
|
/>
|
2020-09-06 16:15:25 +00:00
|
|
|
</span>
|
|
|
|
</li>
|
|
|
|
<li className="list-inline-item">
|
2023-06-25 05:35:34 +00:00
|
|
|
<button
|
|
|
|
type="button"
|
|
|
|
className="pointer text-monospace btn p-0 d-block"
|
2020-09-06 16:15:25 +00:00
|
|
|
onClick={linkEvent(this, this.handleMessageCollapse)}
|
|
|
|
>
|
|
|
|
{this.state.collapsed ? (
|
2023-06-25 05:35:34 +00:00
|
|
|
<Icon icon="plus-square" />
|
2020-09-06 16:15:25 +00:00
|
|
|
) : (
|
2023-06-25 05:35:34 +00:00
|
|
|
<Icon icon="minus-square" />
|
2020-09-06 16:15:25 +00:00
|
|
|
)}
|
2023-06-25 05:35:34 +00:00
|
|
|
</button>
|
2020-09-06 16:15:25 +00:00
|
|
|
</li>
|
|
|
|
</ul>
|
|
|
|
{this.state.showEdit && (
|
|
|
|
<PrivateMessageForm
|
2021-03-15 18:09:31 +00:00
|
|
|
recipient={otherPerson}
|
2023-01-04 16:56:24 +00:00
|
|
|
privateMessageView={message_view}
|
2023-06-14 12:20:40 +00:00
|
|
|
onEdit={this.props.onEdit}
|
2020-09-06 16:15:25 +00:00
|
|
|
onCancel={this.handleReplyCancel}
|
|
|
|
/>
|
|
|
|
)}
|
|
|
|
{!this.state.showEdit && !this.state.collapsed && (
|
|
|
|
<div>
|
|
|
|
{this.state.viewSource ? (
|
|
|
|
<pre>{this.messageUnlessRemoved}</pre>
|
|
|
|
) : (
|
|
|
|
<div
|
|
|
|
className="md-div"
|
|
|
|
dangerouslySetInnerHTML={mdToHtml(this.messageUnlessRemoved)}
|
|
|
|
/>
|
|
|
|
)}
|
2023-06-24 22:41:29 +00:00
|
|
|
<ul className="list-inline mb-0 text-muted fw-bold">
|
2020-09-06 16:15:25 +00:00
|
|
|
{!this.mine && (
|
|
|
|
<>
|
|
|
|
<li className="list-inline-item">
|
|
|
|
<button
|
2023-06-24 03:38:39 +00:00
|
|
|
type="button"
|
2022-09-22 15:03:35 +00:00
|
|
|
className="btn btn-link btn-animate text-muted"
|
2020-09-06 16:15:25 +00:00
|
|
|
onClick={linkEvent(this, this.handleMarkRead)}
|
|
|
|
data-tippy-content={
|
2020-12-24 01:58:27 +00:00
|
|
|
message_view.private_message.read
|
2023-06-22 00:54:35 +00:00
|
|
|
? I18NextService.i18n.t("mark_as_unread")
|
|
|
|
: I18NextService.i18n.t("mark_as_read")
|
2020-09-06 16:15:25 +00:00
|
|
|
}
|
2021-02-06 20:20:41 +00:00
|
|
|
aria-label={
|
|
|
|
message_view.private_message.read
|
2023-06-22 00:54:35 +00:00
|
|
|
? I18NextService.i18n.t("mark_as_unread")
|
|
|
|
: I18NextService.i18n.t("mark_as_read")
|
2021-02-06 20:20:41 +00:00
|
|
|
}
|
2020-09-06 16:15:25 +00:00
|
|
|
>
|
2023-06-14 12:20:40 +00:00
|
|
|
{this.state.readLoading ? (
|
|
|
|
<Spinner />
|
|
|
|
) : (
|
|
|
|
<Icon
|
|
|
|
icon="check"
|
|
|
|
classes={`icon-inline ${
|
|
|
|
message_view.private_message.read &&
|
|
|
|
"text-success"
|
|
|
|
}`}
|
|
|
|
/>
|
|
|
|
)}
|
2020-09-06 16:15:25 +00:00
|
|
|
</button>
|
|
|
|
</li>
|
2022-09-28 12:50:47 +00:00
|
|
|
<li className="list-inline-item">{this.reportButton}</li>
|
2020-09-06 16:15:25 +00:00
|
|
|
<li className="list-inline-item">
|
|
|
|
<button
|
2023-06-24 03:38:39 +00:00
|
|
|
type="button"
|
2022-09-22 15:03:35 +00:00
|
|
|
className="btn btn-link btn-animate text-muted"
|
2020-09-06 16:15:25 +00:00
|
|
|
onClick={linkEvent(this, this.handleReplyClick)}
|
2023-06-22 00:54:35 +00:00
|
|
|
data-tippy-content={I18NextService.i18n.t("reply")}
|
|
|
|
aria-label={I18NextService.i18n.t("reply")}
|
2020-09-06 16:15:25 +00:00
|
|
|
>
|
2021-02-11 20:35:27 +00:00
|
|
|
<Icon icon="reply1" classes="icon-inline" />
|
2020-09-06 16:15:25 +00:00
|
|
|
</button>
|
|
|
|
</li>
|
|
|
|
</>
|
|
|
|
)}
|
|
|
|
{this.mine && (
|
|
|
|
<>
|
|
|
|
<li className="list-inline-item">
|
|
|
|
<button
|
2023-06-24 03:38:39 +00:00
|
|
|
type="button"
|
2022-09-22 15:03:35 +00:00
|
|
|
className="btn btn-link btn-animate text-muted"
|
2020-09-06 16:15:25 +00:00
|
|
|
onClick={linkEvent(this, this.handleEditClick)}
|
2023-06-22 00:54:35 +00:00
|
|
|
data-tippy-content={I18NextService.i18n.t("edit")}
|
|
|
|
aria-label={I18NextService.i18n.t("edit")}
|
2020-09-06 16:15:25 +00:00
|
|
|
>
|
2021-02-11 20:35:27 +00:00
|
|
|
<Icon icon="edit" classes="icon-inline" />
|
2020-09-06 16:15:25 +00:00
|
|
|
</button>
|
|
|
|
</li>
|
|
|
|
<li className="list-inline-item">
|
|
|
|
<button
|
2023-06-24 03:38:39 +00:00
|
|
|
type="button"
|
2022-09-22 15:03:35 +00:00
|
|
|
className="btn btn-link btn-animate text-muted"
|
2020-09-06 16:15:25 +00:00
|
|
|
onClick={linkEvent(this, this.handleDeleteClick)}
|
|
|
|
data-tippy-content={
|
2020-12-24 01:58:27 +00:00
|
|
|
!message_view.private_message.deleted
|
2023-06-22 00:54:35 +00:00
|
|
|
? I18NextService.i18n.t("delete")
|
|
|
|
: I18NextService.i18n.t("restore")
|
2020-09-06 16:15:25 +00:00
|
|
|
}
|
2021-02-06 20:20:41 +00:00
|
|
|
aria-label={
|
|
|
|
!message_view.private_message.deleted
|
2023-06-22 00:54:35 +00:00
|
|
|
? I18NextService.i18n.t("delete")
|
|
|
|
: I18NextService.i18n.t("restore")
|
2021-02-06 20:20:41 +00:00
|
|
|
}
|
2020-09-06 16:15:25 +00:00
|
|
|
>
|
2023-06-14 12:20:40 +00:00
|
|
|
{this.state.deleteLoading ? (
|
|
|
|
<Spinner />
|
|
|
|
) : (
|
|
|
|
<Icon
|
|
|
|
icon="trash"
|
|
|
|
classes={`icon-inline ${
|
|
|
|
message_view.private_message.deleted &&
|
|
|
|
"text-danger"
|
|
|
|
}`}
|
|
|
|
/>
|
|
|
|
)}
|
2020-09-06 16:15:25 +00:00
|
|
|
</button>
|
|
|
|
</li>
|
|
|
|
</>
|
|
|
|
)}
|
|
|
|
<li className="list-inline-item">
|
|
|
|
<button
|
2023-06-24 03:38:39 +00:00
|
|
|
type="button"
|
2022-09-22 15:03:35 +00:00
|
|
|
className="btn btn-link btn-animate text-muted"
|
2020-09-06 16:15:25 +00:00
|
|
|
onClick={linkEvent(this, this.handleViewSource)}
|
2023-06-22 00:54:35 +00:00
|
|
|
data-tippy-content={I18NextService.i18n.t("view_source")}
|
|
|
|
aria-label={I18NextService.i18n.t("view_source")}
|
2020-09-06 16:15:25 +00:00
|
|
|
>
|
2021-02-11 20:35:27 +00:00
|
|
|
<Icon
|
|
|
|
icon="file-text"
|
|
|
|
classes={`icon-inline ${
|
2021-02-22 02:39:04 +00:00
|
|
|
this.state.viewSource && "text-success"
|
2020-09-06 16:15:25 +00:00
|
|
|
}`}
|
2021-02-11 20:35:27 +00:00
|
|
|
/>
|
2020-09-06 16:15:25 +00:00
|
|
|
</button>
|
|
|
|
</li>
|
|
|
|
</ul>
|
|
|
|
</div>
|
|
|
|
)}
|
|
|
|
</div>
|
2022-09-28 12:50:47 +00:00
|
|
|
{this.state.showReportDialog && (
|
|
|
|
<form
|
|
|
|
className="form-inline"
|
|
|
|
onSubmit={linkEvent(this, this.handleReportSubmit)}
|
|
|
|
>
|
2023-06-20 12:01:29 +00:00
|
|
|
<label className="visually-hidden" htmlFor="pm-report-reason">
|
2023-06-22 00:54:35 +00:00
|
|
|
{I18NextService.i18n.t("reason")}
|
2022-09-28 12:50:47 +00:00
|
|
|
</label>
|
|
|
|
<input
|
|
|
|
type="text"
|
|
|
|
id="pm-report-reason"
|
2023-06-20 12:01:29 +00:00
|
|
|
className="form-control me-2"
|
2023-06-22 00:54:35 +00:00
|
|
|
placeholder={I18NextService.i18n.t("reason")}
|
2022-09-28 12:50:47 +00:00
|
|
|
required
|
2023-01-04 16:56:24 +00:00
|
|
|
value={this.state.reportReason}
|
2022-09-28 12:50:47 +00:00
|
|
|
onInput={linkEvent(this, this.handleReportReasonChange)}
|
|
|
|
/>
|
|
|
|
<button
|
|
|
|
type="submit"
|
|
|
|
className="btn btn-secondary"
|
2023-06-22 00:54:35 +00:00
|
|
|
aria-label={I18NextService.i18n.t("create_report")}
|
2022-09-28 12:50:47 +00:00
|
|
|
>
|
2023-06-22 00:54:35 +00:00
|
|
|
{this.state.reportLoading ? (
|
|
|
|
<Spinner />
|
|
|
|
) : (
|
|
|
|
I18NextService.i18n.t("create_report")
|
|
|
|
)}
|
2022-09-28 12:50:47 +00:00
|
|
|
</button>
|
|
|
|
</form>
|
|
|
|
)}
|
2020-09-06 16:15:25 +00:00
|
|
|
{this.state.showReply && (
|
2023-06-24 03:38:39 +00:00
|
|
|
<div className="row">
|
|
|
|
<div className="col-sm-6">
|
|
|
|
<PrivateMessageForm
|
|
|
|
privateMessageView={message_view}
|
|
|
|
replyType={true}
|
|
|
|
recipient={otherPerson}
|
|
|
|
onCreate={this.props.onCreate}
|
|
|
|
onCancel={this.handleReplyCancel}
|
|
|
|
/>
|
|
|
|
</div>
|
|
|
|
</div>
|
2020-09-06 16:15:25 +00:00
|
|
|
)}
|
|
|
|
{/* A collapsed clearfix */}
|
2022-09-22 15:03:35 +00:00
|
|
|
{this.state.collapsed && <div className="row col-12"></div>}
|
2020-09-06 16:15:25 +00:00
|
|
|
</div>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2022-09-28 12:50:47 +00:00
|
|
|
get reportButton() {
|
|
|
|
return (
|
|
|
|
<button
|
2023-06-24 03:38:39 +00:00
|
|
|
type="button"
|
2022-09-28 12:50:47 +00:00
|
|
|
className="btn btn-link btn-animate text-muted py-0"
|
|
|
|
onClick={linkEvent(this, this.handleShowReportDialog)}
|
2023-06-22 00:54:35 +00:00
|
|
|
data-tippy-content={I18NextService.i18n.t("show_report_dialog")}
|
|
|
|
aria-label={I18NextService.i18n.t("show_report_dialog")}
|
2022-09-28 12:50:47 +00:00
|
|
|
>
|
|
|
|
<Icon icon="flag" inline />
|
|
|
|
</button>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2020-09-06 16:15:25 +00:00
|
|
|
get messageUnlessRemoved(): string {
|
2023-06-05 21:31:12 +00:00
|
|
|
const message = this.props.private_message_view.private_message;
|
2023-06-22 00:54:35 +00:00
|
|
|
return message.deleted
|
|
|
|
? `*${I18NextService.i18n.t("deleted")}*`
|
|
|
|
: message.content;
|
2020-09-06 16:15:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
handleReplyClick(i: PrivateMessage) {
|
2022-09-22 15:03:35 +00:00
|
|
|
i.setState({ showReply: true });
|
2020-09-06 16:15:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
handleEditClick(i: PrivateMessage) {
|
2022-09-22 15:03:35 +00:00
|
|
|
i.setState({ showEdit: true });
|
2020-09-06 16:15:25 +00:00
|
|
|
i.setState(i.state);
|
|
|
|
}
|
|
|
|
|
|
|
|
handleDeleteClick(i: PrivateMessage) {
|
2023-06-14 12:20:40 +00:00
|
|
|
i.setState({ deleteLoading: true });
|
|
|
|
i.props.onDelete({
|
|
|
|
private_message_id: i.props.private_message_view.private_message.id,
|
|
|
|
deleted: !i.props.private_message_view.private_message.deleted,
|
|
|
|
auth: myAuthRequired(),
|
|
|
|
});
|
2020-09-06 16:15:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
handleReplyCancel() {
|
2022-09-22 15:03:35 +00:00
|
|
|
this.setState({ showReply: false, showEdit: false });
|
2020-09-06 16:15:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
handleMarkRead(i: PrivateMessage) {
|
2023-06-14 12:20:40 +00:00
|
|
|
i.setState({ readLoading: true });
|
|
|
|
i.props.onMarkRead({
|
|
|
|
private_message_id: i.props.private_message_view.private_message.id,
|
|
|
|
read: !i.props.private_message_view.private_message.read,
|
|
|
|
auth: myAuthRequired(),
|
|
|
|
});
|
2020-09-06 16:15:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
handleMessageCollapse(i: PrivateMessage) {
|
2022-09-22 15:03:35 +00:00
|
|
|
i.setState({ collapsed: !i.state.collapsed });
|
2020-09-06 16:15:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
handleViewSource(i: PrivateMessage) {
|
2022-09-22 15:03:35 +00:00
|
|
|
i.setState({ viewSource: !i.state.viewSource });
|
2020-09-06 16:15:25 +00:00
|
|
|
}
|
|
|
|
|
2022-09-28 12:50:47 +00:00
|
|
|
handleShowReportDialog(i: PrivateMessage) {
|
|
|
|
i.setState({ showReportDialog: !i.state.showReportDialog });
|
|
|
|
}
|
|
|
|
|
|
|
|
handleReportReasonChange(i: PrivateMessage, event: any) {
|
2023-01-04 16:56:24 +00:00
|
|
|
i.setState({ reportReason: event.target.value });
|
2022-09-28 12:50:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
handleReportSubmit(i: PrivateMessage, event: any) {
|
|
|
|
event.preventDefault();
|
2023-06-14 12:20:40 +00:00
|
|
|
i.setState({ reportLoading: true });
|
|
|
|
i.props.onReport({
|
|
|
|
private_message_id: i.props.private_message_view.private_message.id,
|
|
|
|
reason: i.state.reportReason ?? "",
|
|
|
|
auth: myAuthRequired(),
|
|
|
|
});
|
2020-09-06 16:15:25 +00:00
|
|
|
}
|
|
|
|
}
|