1 import { myAuthRequired } from "@utils/app";
2 import { Component, InfernoNode, linkEvent } from "inferno";
5 CreatePrivateMessageReport,
8 MarkPrivateMessageAsRead,
11 } from "lemmy-js-client";
12 import { mdToHtml } from "../../markdown";
13 import { I18NextService, UserService } from "../../services";
14 import { Icon, Spinner } from "../common/icon";
15 import { MomentTime } from "../common/moment-time";
16 import { PersonListing } from "../person/person-listing";
17 import { PrivateMessageForm } from "./private-message-form";
19 interface PrivateMessageState {
24 showReportDialog: boolean;
25 reportReason?: string;
26 deleteLoading: boolean;
28 reportLoading: boolean;
31 interface PrivateMessageProps {
32 private_message_view: PrivateMessageView;
33 onDelete(form: DeletePrivateMessage): void;
34 onMarkRead(form: MarkPrivateMessageAsRead): void;
35 onReport(form: CreatePrivateMessageReport): void;
36 onCreate(form: CreatePrivateMessage): void;
37 onEdit(form: EditPrivateMessage): void;
40 export class PrivateMessage extends Component<
44 state: PrivateMessageState = {
49 showReportDialog: false,
55 constructor(props: any, context: any) {
56 super(props, context);
57 this.handleReplyCancel = this.handleReplyCancel.bind(this);
62 UserService.Instance.myUserInfo?.local_user_view.person.id ==
63 this.props.private_message_view.creator.id
67 componentWillReceiveProps(
68 nextProps: Readonly<{ children?: InfernoNode } & PrivateMessageProps>
70 if (this.props != nextProps) {
76 showReportDialog: false,
85 const message_view = this.props.private_message_view;
86 const otherPerson: Person = this.mine
87 ? message_view.recipient
88 : message_view.creator;
91 <div className="private-message border-top border-light">
93 <ul className="list-inline mb-0 text-muted small">
94 {/* TODO refactor this */}
95 <li className="list-inline-item">
97 ? I18NextService.i18n.t("to")
98 : I18NextService.i18n.t("from")}
100 <li className="list-inline-item">
101 <PersonListing person={otherPerson} />
103 <li className="list-inline-item">
106 published={message_view.private_message.published}
107 updated={message_view.private_message.updated}
111 <li className="list-inline-item">
114 className="pointer text-monospace"
115 onClick={linkEvent(this, this.handleMessageCollapse)}
117 {this.state.collapsed ? (
118 <Icon icon="plus-square" classes="icon-inline" />
120 <Icon icon="minus-square" classes="icon-inline" />
125 {this.state.showEdit && (
127 recipient={otherPerson}
128 privateMessageView={message_view}
129 onEdit={this.props.onEdit}
130 onCancel={this.handleReplyCancel}
133 {!this.state.showEdit && !this.state.collapsed && (
135 {this.state.viewSource ? (
136 <pre>{this.messageUnlessRemoved}</pre>
140 dangerouslySetInnerHTML={mdToHtml(this.messageUnlessRemoved)}
143 <ul className="list-inline mb-0 text-muted font-weight-bold">
146 <li className="list-inline-item">
149 className="btn btn-link btn-animate text-muted"
150 onClick={linkEvent(this, this.handleMarkRead)}
152 message_view.private_message.read
153 ? I18NextService.i18n.t("mark_as_unread")
154 : I18NextService.i18n.t("mark_as_read")
157 message_view.private_message.read
158 ? I18NextService.i18n.t("mark_as_unread")
159 : I18NextService.i18n.t("mark_as_read")
162 {this.state.readLoading ? (
167 classes={`icon-inline ${
168 message_view.private_message.read &&
175 <li className="list-inline-item">{this.reportButton}</li>
176 <li className="list-inline-item">
179 className="btn btn-link btn-animate text-muted"
180 onClick={linkEvent(this, this.handleReplyClick)}
181 data-tippy-content={I18NextService.i18n.t("reply")}
182 aria-label={I18NextService.i18n.t("reply")}
184 <Icon icon="reply1" classes="icon-inline" />
191 <li className="list-inline-item">
194 className="btn btn-link btn-animate text-muted"
195 onClick={linkEvent(this, this.handleEditClick)}
196 data-tippy-content={I18NextService.i18n.t("edit")}
197 aria-label={I18NextService.i18n.t("edit")}
199 <Icon icon="edit" classes="icon-inline" />
202 <li className="list-inline-item">
205 className="btn btn-link btn-animate text-muted"
206 onClick={linkEvent(this, this.handleDeleteClick)}
208 !message_view.private_message.deleted
209 ? I18NextService.i18n.t("delete")
210 : I18NextService.i18n.t("restore")
213 !message_view.private_message.deleted
214 ? I18NextService.i18n.t("delete")
215 : I18NextService.i18n.t("restore")
218 {this.state.deleteLoading ? (
223 classes={`icon-inline ${
224 message_view.private_message.deleted &&
233 <li className="list-inline-item">
236 className="btn btn-link btn-animate text-muted"
237 onClick={linkEvent(this, this.handleViewSource)}
238 data-tippy-content={I18NextService.i18n.t("view_source")}
239 aria-label={I18NextService.i18n.t("view_source")}
243 classes={`icon-inline ${
244 this.state.viewSource && "text-success"
253 {this.state.showReportDialog && (
255 className="form-inline"
256 onSubmit={linkEvent(this, this.handleReportSubmit)}
258 <label className="visually-hidden" htmlFor="pm-report-reason">
259 {I18NextService.i18n.t("reason")}
263 id="pm-report-reason"
264 className="form-control me-2"
265 placeholder={I18NextService.i18n.t("reason")}
267 value={this.state.reportReason}
268 onInput={linkEvent(this, this.handleReportReasonChange)}
272 className="btn btn-secondary"
273 aria-label={I18NextService.i18n.t("create_report")}
275 {this.state.reportLoading ? (
278 I18NextService.i18n.t("create_report")
283 {this.state.showReply && (
284 <div className="row">
285 <div className="col-sm-6">
287 privateMessageView={message_view}
289 recipient={otherPerson}
290 onCreate={this.props.onCreate}
291 onCancel={this.handleReplyCancel}
296 {/* A collapsed clearfix */}
297 {this.state.collapsed && <div className="row col-12"></div>}
306 className="btn btn-link btn-animate text-muted py-0"
307 onClick={linkEvent(this, this.handleShowReportDialog)}
308 data-tippy-content={I18NextService.i18n.t("show_report_dialog")}
309 aria-label={I18NextService.i18n.t("show_report_dialog")}
311 <Icon icon="flag" inline />
316 get messageUnlessRemoved(): string {
317 const message = this.props.private_message_view.private_message;
318 return message.deleted
319 ? `*${I18NextService.i18n.t("deleted")}*`
323 handleReplyClick(i: PrivateMessage) {
324 i.setState({ showReply: true });
327 handleEditClick(i: PrivateMessage) {
328 i.setState({ showEdit: true });
332 handleDeleteClick(i: PrivateMessage) {
333 i.setState({ deleteLoading: true });
335 private_message_id: i.props.private_message_view.private_message.id,
336 deleted: !i.props.private_message_view.private_message.deleted,
337 auth: myAuthRequired(),
341 handleReplyCancel() {
342 this.setState({ showReply: false, showEdit: false });
345 handleMarkRead(i: PrivateMessage) {
346 i.setState({ readLoading: true });
348 private_message_id: i.props.private_message_view.private_message.id,
349 read: !i.props.private_message_view.private_message.read,
350 auth: myAuthRequired(),
354 handleMessageCollapse(i: PrivateMessage) {
355 i.setState({ collapsed: !i.state.collapsed });
358 handleViewSource(i: PrivateMessage) {
359 i.setState({ viewSource: !i.state.viewSource });
362 handleShowReportDialog(i: PrivateMessage) {
363 i.setState({ showReportDialog: !i.state.showReportDialog });
366 handleReportReasonChange(i: PrivateMessage, event: any) {
367 i.setState({ reportReason: event.target.value });
370 handleReportSubmit(i: PrivateMessage, event: any) {
371 event.preventDefault();
372 i.setState({ reportLoading: true });
374 private_message_id: i.props.private_message_view.private_message.id,
375 reason: i.state.reportReason ?? "",
376 auth: myAuthRequired(),