1 import { myAuthRequired } from "@utils/app";
2 import { Component, InfernoNode, linkEvent } from "inferno";
5 CreatePrivateMessageReport,
8 MarkPrivateMessageAsRead,
11 } from "lemmy-js-client";
12 import { i18n } from "../../i18next";
13 import { mdToHtml } from "../../markdown";
14 import { UserService } from "../../services";
15 import { Icon, Spinner } from "../common/icon";
16 import { MomentTime } from "../common/moment-time";
17 import { PersonListing } from "../person/person-listing";
18 import { PrivateMessageForm } from "./private-message-form";
20 interface PrivateMessageState {
25 showReportDialog: boolean;
26 reportReason?: string;
27 deleteLoading: boolean;
29 reportLoading: boolean;
32 interface PrivateMessageProps {
33 private_message_view: PrivateMessageView;
34 onDelete(form: DeletePrivateMessage): void;
35 onMarkRead(form: MarkPrivateMessageAsRead): void;
36 onReport(form: CreatePrivateMessageReport): void;
37 onCreate(form: CreatePrivateMessage): void;
38 onEdit(form: EditPrivateMessage): void;
41 export class PrivateMessage extends Component<
45 state: PrivateMessageState = {
50 showReportDialog: false,
56 constructor(props: any, context: any) {
57 super(props, context);
58 this.handleReplyCancel = this.handleReplyCancel.bind(this);
63 UserService.Instance.myUserInfo?.local_user_view.person.id ==
64 this.props.private_message_view.creator.id
68 componentWillReceiveProps(
69 nextProps: Readonly<{ children?: InfernoNode } & PrivateMessageProps>
71 if (this.props != nextProps) {
77 showReportDialog: false,
86 const message_view = this.props.private_message_view;
87 const otherPerson: Person = this.mine
88 ? message_view.recipient
89 : message_view.creator;
92 <div className="private-message border-top border-light">
94 <ul className="list-inline mb-0 text-muted small">
95 {/* TODO refactor this */}
96 <li className="list-inline-item">
97 {this.mine ? i18n.t("to") : i18n.t("from")}
99 <li className="list-inline-item">
100 <PersonListing person={otherPerson} />
102 <li className="list-inline-item">
105 published={message_view.private_message.published}
106 updated={message_view.private_message.updated}
110 <li className="list-inline-item">
113 className="pointer text-monospace"
114 onClick={linkEvent(this, this.handleMessageCollapse)}
116 {this.state.collapsed ? (
117 <Icon icon="plus-square" classes="icon-inline" />
119 <Icon icon="minus-square" classes="icon-inline" />
124 {this.state.showEdit && (
126 recipient={otherPerson}
127 privateMessageView={message_view}
128 onEdit={this.props.onEdit}
129 onCancel={this.handleReplyCancel}
132 {!this.state.showEdit && !this.state.collapsed && (
134 {this.state.viewSource ? (
135 <pre>{this.messageUnlessRemoved}</pre>
139 dangerouslySetInnerHTML={mdToHtml(this.messageUnlessRemoved)}
142 <ul className="list-inline mb-0 text-muted font-weight-bold">
145 <li className="list-inline-item">
147 className="btn btn-link btn-animate text-muted"
148 onClick={linkEvent(this, this.handleMarkRead)}
150 message_view.private_message.read
151 ? i18n.t("mark_as_unread")
152 : i18n.t("mark_as_read")
155 message_view.private_message.read
156 ? i18n.t("mark_as_unread")
157 : i18n.t("mark_as_read")
160 {this.state.readLoading ? (
165 classes={`icon-inline ${
166 message_view.private_message.read &&
173 <li className="list-inline-item">{this.reportButton}</li>
174 <li className="list-inline-item">
176 className="btn btn-link btn-animate text-muted"
177 onClick={linkEvent(this, this.handleReplyClick)}
178 data-tippy-content={i18n.t("reply")}
179 aria-label={i18n.t("reply")}
181 <Icon icon="reply1" classes="icon-inline" />
188 <li className="list-inline-item">
190 className="btn btn-link btn-animate text-muted"
191 onClick={linkEvent(this, this.handleEditClick)}
192 data-tippy-content={i18n.t("edit")}
193 aria-label={i18n.t("edit")}
195 <Icon icon="edit" classes="icon-inline" />
198 <li className="list-inline-item">
200 className="btn btn-link btn-animate text-muted"
201 onClick={linkEvent(this, this.handleDeleteClick)}
203 !message_view.private_message.deleted
208 !message_view.private_message.deleted
213 {this.state.deleteLoading ? (
218 classes={`icon-inline ${
219 message_view.private_message.deleted &&
228 <li className="list-inline-item">
230 className="btn btn-link btn-animate text-muted"
231 onClick={linkEvent(this, this.handleViewSource)}
232 data-tippy-content={i18n.t("view_source")}
233 aria-label={i18n.t("view_source")}
237 classes={`icon-inline ${
238 this.state.viewSource && "text-success"
247 {this.state.showReportDialog && (
249 className="form-inline"
250 onSubmit={linkEvent(this, this.handleReportSubmit)}
252 <label className="visually-hidden" htmlFor="pm-report-reason">
257 id="pm-report-reason"
258 className="form-control me-2"
259 placeholder={i18n.t("reason")}
261 value={this.state.reportReason}
262 onInput={linkEvent(this, this.handleReportReasonChange)}
266 className="btn btn-secondary"
267 aria-label={i18n.t("create_report")}
269 {this.state.reportLoading ? <Spinner /> : i18n.t("create_report")}
273 {this.state.showReply && (
275 recipient={otherPerson}
276 onCreate={this.props.onCreate}
279 {/* A collapsed clearfix */}
280 {this.state.collapsed && <div className="row col-12"></div>}
288 className="btn btn-link btn-animate text-muted py-0"
289 onClick={linkEvent(this, this.handleShowReportDialog)}
290 data-tippy-content={i18n.t("show_report_dialog")}
291 aria-label={i18n.t("show_report_dialog")}
293 <Icon icon="flag" inline />
298 get messageUnlessRemoved(): string {
299 const message = this.props.private_message_view.private_message;
300 return message.deleted ? `*${i18n.t("deleted")}*` : message.content;
303 handleReplyClick(i: PrivateMessage) {
304 i.setState({ showReply: true });
307 handleEditClick(i: PrivateMessage) {
308 i.setState({ showEdit: true });
312 handleDeleteClick(i: PrivateMessage) {
313 i.setState({ deleteLoading: true });
315 private_message_id: i.props.private_message_view.private_message.id,
316 deleted: !i.props.private_message_view.private_message.deleted,
317 auth: myAuthRequired(),
321 handleReplyCancel() {
322 this.setState({ showReply: false, showEdit: false });
325 handleMarkRead(i: PrivateMessage) {
326 i.setState({ readLoading: true });
328 private_message_id: i.props.private_message_view.private_message.id,
329 read: !i.props.private_message_view.private_message.read,
330 auth: myAuthRequired(),
334 handleMessageCollapse(i: PrivateMessage) {
335 i.setState({ collapsed: !i.state.collapsed });
338 handleViewSource(i: PrivateMessage) {
339 i.setState({ viewSource: !i.state.viewSource });
342 handleShowReportDialog(i: PrivateMessage) {
343 i.setState({ showReportDialog: !i.state.showReportDialog });
346 handleReportReasonChange(i: PrivateMessage, event: any) {
347 i.setState({ reportReason: event.target.value });
350 handleReportSubmit(i: PrivateMessage, event: any) {
351 event.preventDefault();
352 i.setState({ reportLoading: true });
354 private_message_id: i.props.private_message_view.private_message.id,
355 reason: i.state.reportReason ?? "",
356 auth: myAuthRequired(),